Support casting in constant expressions

62ca04d4d6dbfbddffcc08ebfd9c93f766147331f2a933710f0cbbe59c5e8176
Alexis Sellier committed ago 1 parent 60ca87f0
lib/std/lang/resolver.rad +18 -0
2890 2890
        },
2891 2891
        case ast::NodeValue::UnOp(unop) => {
2892 2892
            // Unary expressions are constant if the operand is constant.
2893 2893
            return isConstExpr(self, unop.value);
2894 2894
        },
2895 +
        case ast::NodeValue::As(expr) => {
2896 +
            // Cast expressions are constant if the source value is constant.
2897 +
            return isConstExpr(self, expr.value);
2898 +
        },
2895 2899
        else => {
2896 2900
            return false;
2897 2901
        }
2898 2902
    }
2899 2903
}
5665 5669
5666 5670
    assert sourceTy != Type::Unknown;
5667 5671
    assert targetTy != Type::Unknown;
5668 5672
5669 5673
    if isValidCast(sourceTy, targetTy) {
5674 +
        // Propagate constant value through the cast, adjusting integer
5675 +
        // metadata to match the target type.
5676 +
        if let value = constValueEntry(self, expr.value) {
5677 +
            if let case ConstValue::Int(i) = value {
5678 +
                if let range = integerRange(targetTy) {
5679 +
                    match range {
5680 +
                        case IntegerRange::Signed { bits, .. } =>
5681 +
                            setNodeConstValue(self, node, constInt(i.magnitude, bits, true, i.negative)),
5682 +
                        case IntegerRange::Unsigned { bits, .. } =>
5683 +
                            setNodeConstValue(self, node, constInt(i.magnitude, bits, false, false)),
5684 +
                    }
5685 +
                }
5686 +
            }
5687 +
        }
5670 5688
        return setNodeType(self, node, targetTy);
5671 5689
    }
5672 5690
    throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast {
5673 5691
        from: sourceTy,
5674 5692
        to: targetTy,
lib/std/lang/resolver/tests.rad +25 -0
5023 5023
    let mut a = testResolver();
5024 5024
    let program = "const A: bool = true; const B: bool = not A;";
5025 5025
    let result = try resolveProgramStr(&mut a, program);
5026 5026
    try expectNoErrors(&result);
5027 5027
}
5028 +
5029 +
/// Test `as` casts in constant expressions: widening, narrowing, sign changes, chaining.
5030 +
@test fn testConstExprCast() throws (testing::TestError) {
5031 +
    try expectConstFold("const A: i32 = 42; const B: u64 = A as u64;", 1, 42);
5032 +
    try expectConstFold("const A: u64 = 10; const B: u8 = A as u8;", 1, 10);
5033 +
    try expectConstFold("const A: i32 = 7; const B: u32 = A as u32;", 1, 7);
5034 +
    try expectConstFold("const A: u32 = 100; const B: i32 = A as i32;", 1, 100);
5035 +
    try expectConstFold("const A: u8 = 5; const B: u64 = (A as u32) as u64;", 1, 5);
5036 +
    try expectConstFold("const A: u8 = 3; const B: u8 = 4; const C: i32 = (A as i32) + (B as i32);", 2, 7);
5037 +
}
5038 +
5039 +
/// Test `as` cast in constant expressions used as array size.
5040 +
@test fn testConstExprCastAsArraySize() throws (testing::TestError) {
5041 +
    let mut a = testResolver();
5042 +
    let program = "const LEN: u64 = 4; const SIZE: u32 = LEN as u32; const ARR: [i32; SIZE] = [1, 2, 3, 4];";
5043 +
    let result = try resolveProgramStr(&mut a, program);
5044 +
    try expectNoErrors(&result);
5045 +
5046 +
    let arrStmt = try getBlockStmt(result.root, 2);
5047 +
    let sym = super::symbolFor(&a, arrStmt)
5048 +
        else throw testing::TestError::Failed;
5049 +
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
5050 +
        else throw testing::TestError::Failed;
5051 +
    try testing::expect(arrType.length == 4);
5052 +
}
test/tests/const-expr-cast.rad added +32 -0
1 +
//! returns: 0
2 +
3 +
/// Test that `as` casts work in constant expressions.
4 +
5 +
const A: i32 = 42;
6 +
const B: u64 = A as u64;
7 +
const C: u8 = 10;
8 +
const D: i32 = C as i32;
9 +
const SIZE: u32 = 4;
10 +
const SHIFTED: u64 = SIZE as u64;
11 +
12 +
// Use a cast const expression as an array size.
13 +
const LEN: u32 = 8;
14 +
const LEN2: u32 = (LEN as u64) as u32;
15 +
static BUF: [u8; LEN2] = undefined;
16 +
17 +
// Cast const used in static data initializer.
18 +
const INIT_VAL: u8 = 0xFF;
19 +
const WIDE: u32 = INIT_VAL as u32;
20 +
21 +
// Cast with arithmetic.
22 +
const X: u8 = 3;
23 +
const Y: u8 = 4;
24 +
const Z: i32 = (X as i32) * (Y as i32);
25 +
26 +
@default fn main() -> i32 {
27 +
    assert BUF.len == 8;
28 +
    assert WIDE == 255;
29 +
    assert Z == 12;
30 +
31 +
    return 0;
32 +
}