Submitted, awaiting the pre-agent gates.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.35;
/* ===ARENA-MANIFEST===
{
"deploy": { "contract": "C", "args": [], "value": 0 },
"entry": { "function": "f", "args": [] },
"lane": "S",
"feature": "bytesn-width-expanding-op-as-tuple-rhs-component-skips-width-mask",
"note": "A width-expanding bytesN operation - left shift (b << k) or bitwise NOT (~b) - used as a TUPLE right-hand-side component silently skips its width mask and yields a WRONG VALUE. bytes4 b = 0x11223344; (x, y) = (b << 8, b); must leave x == 0x22334400; solidity-lean computes 0x1122334400, i.e. the shifted-out byte is retained and the value no longer fits the declared 4-byte width. This is a silent soundness bug: both engines succeed, no revert, only the value differs. WRONG VALUE for: (x, y) = (b << 8, b), (x, y) = (~b, b), the component in SECOND position (x, y) = (b, b << 8), and the tuple DECLARATION form (bytes4 x, bytes4 y) = (b << 8, b). AGREES with solc: the single-assignment control bytes4 x; x = b << 8 (identical computation, non-tuple target); (x, y) = (b >> 8, b) and (x, y) = (b & mask, b) - operators that cannot exceed the width; bytes32 components (nothing to truncate); and a tuple SWAP (x, y) = (y, x) where the shifted value was already masked at its own assignment. ROOT CAUSE (source-level): the width mask is emitted only by Expr.toCoreFixedBytesBitOp?, which wraps shl and bitNot results in fixedBytesCast size size. It is reached only via Expr.toCoreAsWithEnvBitAware?, which requires BOTH a known bytesN target type AND Expr.isFixedBytesBitOpShape on the expression's TOP node. Tuple RHS components are not lowered through that route, so the mask is never emitted. Distinct from my 'width-expanding-bytesn-op-in-abi-encode-head-or-topic-arm' report: that position REVERTS with Panic(0) because the encoder rejects the out-of-lane value, whereas this position silently stores and returns it - a different observable class (wrong-value vs revert) at a different lowering site."
}
===END-ARENA-MANIFEST=== */
contract C {
function f() external pure returns (bytes4) {
bytes4 b = 0x11223344;
bytes4 x;
bytes4 y;
(x, y) = (b << 8, b);
// solc+EVM: 0x22334400 | solidity-lean: 0x1122334400 (mask skipped)
return x;
}
}