lang: Fold signed right shifts arithmetically

470dd84a2660655e20dd2f513af46afc8b3a5a6ec80d844fdad76f9710f51d42
The constant folder shifted a negative value’s absolute magnitude
and restored its sign, which rounds odd negative values toward zero
instead of matching an arithmetic right shift.

Shift signed constants through their signed representation and
reconstruct the width-limited result from its bit pattern.

Assisted-by: Codex:gpt-5.6-sol
Alexis Sellier committed ago 1 parent 1361514f
lib/std/lang/resolver.rad +10 -3
6345 6345
            let raw = (0 - left.magnitude) if left.negative else left.magnitude;
6346 6346
            let shamt = right.magnitude % left.bits as u64;
6347 6347
            return ConstValue::Int(constIntFromBits(raw << shamt, left.bits, left.signed));
6348 6348
        },
6349 6349
        case ast::BinaryOp::Shr => {
6350 -
            return ConstValue::Int(ConstInt {
6351 -
                magnitude: left.magnitude >> right.magnitude, bits, signed, negative: left.negative,
6352 -
            });
6350 +
            let shamt = right.magnitude % left.bits as u64;
6351 +
            if left.signed {
6352 +
                let shifted = constIntToSigned(left) >> shamt as i64;
6353 +
                return ConstValue::Int(
6354 +
                    constIntFromBits(shifted as u64, left.bits, true)
6355 +
                );
6356 +
            }
6357 +
            return ConstValue::Int(
6358 +
                constIntFromBits(left.magnitude >> shamt, left.bits, false)
6359 +
            );
6353 6360
        },
6354 6361
        case ast::BinaryOp::Eq  => return ConstValue::Bool(l == r),
6355 6362
        case ast::BinaryOp::Ne  => return ConstValue::Bool(l <> r),
6356 6363
        case ast::BinaryOp::Lt =>
6357 6364
            return ConstValue::Bool(l < r if signed else left.magnitude < right.magnitude),
test/tests/const.i32.shift.right.rad added +12 -0
1 +
//! returns: 0
2 +
//! Folded signed right shifts must round toward negative infinity.
3 +
4 +
constant NEGATIVE: i32 = -7;
5 +
constant SHIFTED: i32 = NEGATIVE >> 1;
6 +
7 +
@default fn main() -> i32 {
8 +
    if SHIFTED == -4 {
9 +
        return 0;
10 +
    }
11 +
    return 1;
12 +
}