int[] + int + int[] currently compiles, but the generated script does not preserve int[] element boundaries. At runtime, the middle int is coerced to a single byte and concatenated into the serialized int[] blob, so later int[] indexing reads the wrong 8-byte chunks.
Minimal repro
contract Arrays() {
entrypoint function main() {
int[] left = [1, 2];
int middle = 3;
int[] right = [4, 5];
int[5] mixed = left + middle + right;
require(mixed[0] == 1);
require(mixed[1] == 2);
require(mixed[2] == 3); // fails here
require(mixed[3] == 4);
require(mixed[4] == 5);
}
}
Observed behavior
The contract compiles, but execution fails on require(mixed[2] == 3).
Expected behavior
One of these should happen:
int[] + int + int[] is supported and lowers by serializing the scalar int as an 8-byte int element before concatenation.
- The compiler rejects this expression as unsupported/type-invalid.
Root cause
BinaryOp::Add currently switches to concat mode whenever either side is considered “bytes-like”. int[] is treated as bytes-like because is_bytes_type() falls back to is_array_type(type_name). In concat mode, non-bytes operands are coerced with OP_NUM2BIN(1), so the scalar int is inserted as a 1-byte payload instead of an 8-byte int element.
This means:
left + middle becomes byte concat
middle is encoded as 1 byte
- later
int[] indexing still assumes 8-byte element boundaries
Notes
int[] + int[] works.
- The issue is specifically with mixing
int[] concatenation and scalar int.
byte[] + byte currently works, but for type-system consistency it may also be better to disallow mixing array and scalar operands unless the scalar is explicitly cast to the array element encoding (byte[1], byte[8], etc.). That would make concat semantics uniform across byte[] and int[] instead of relying on implicit scalar coercions for some element types but not others.
int[] + int + int[]currently compiles, but the generated script does not preserveint[]element boundaries. At runtime, the middleintis coerced to a single byte and concatenated into the serializedint[]blob, so laterint[]indexing reads the wrong 8-byte chunks.Minimal repro
Observed behavior
The contract compiles, but execution fails on
require(mixed[2] == 3).Expected behavior
One of these should happen:
int[] + int + int[]is supported and lowers by serializing the scalarintas an 8-byteintelement before concatenation.Root cause
BinaryOp::Addcurrently switches to concat mode whenever either side is considered “bytes-like”.int[]is treated as bytes-like becauseis_bytes_type()falls back tois_array_type(type_name). In concat mode, non-bytes operands are coerced withOP_NUM2BIN(1), so the scalarintis inserted as a 1-byte payload instead of an 8-byteintelement.This means:
left + middlebecomes byte concatmiddleis encoded as 1 byteint[]indexing still assumes 8-byte element boundariesNotes
int[] + int[]works.int[]concatenation and scalarint.byte[] + bytecurrently works, but for type-system consistency it may also be better to disallow mixing array and scalar operands unless the scalar is explicitly cast to the array element encoding (byte[1],byte[8], etc.). That would make concat semantics uniform acrossbyte[]andint[]instead of relying on implicit scalar coercions for some element types but not others.