compiler: Construct for-loop metadata in checked code

50908ae883e3e5f97c9663b13800d3393f53bbd20249c5717273e128723e695e
Alexis Sellier committed ago 1 parent 37360ae7
lib/std/lang/resolver.rad +15 -11
6011 6011
    try visitOptional(self, loopNode.elseBranch, Type::Void);
6012 6012
6013 6013
    return setNodeType(self, node, Type::Void);
6014 6014
}
6015 6015
6016 -
/// Analyze a `for` loop, binding iteration variables.
6017 -
unsafe fn resolveFor 'arena (self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For) -> Type
6018 -
    throws (ResolveError)
6019 -
{
6020 -
    let iterableTy = try infer(self, forStmt.iterable);
6021 -
6016 +
/// Store complete iteration metadata and return the loop binding type.
6017 +
fn resolveForInfo 'arena (
6018 +
    self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For, iterableTy: Type
6019 +
) -> Type throws (ResolveError) {
6022 6020
    // Extract binding names for the lowerer.
6023 6021
    let mut bindingName: ?*[u8] = nil;
6024 6022
    if let case ast::NodeValue::Ident(name) = forStmt.binding.value {
6025 6023
        set bindingName = name;
6026 6024
    }
6029 6027
        if let case ast::NodeValue::Ident(name) = idx.value {
6030 6028
            set indexName = name;
6031 6029
        }
6032 6030
    }
6033 6031
    // Extract item type and store pre-computed loop metadata for the lowerer.
6034 -
    let mut itemTy: Type = undefined;
6035 6032
    match iterableTy {
6036 6033
        case Type::Slice { item, class, .. } => {
6037 6034
            if class == types::PointerClass::Unsafe {
6038 6035
                try requireUnsafe(self, forStmt.iterable);
6039 6036
            }
6040 -
            set itemTy = *item;
6041 6037
            setForLoopInfo(self, node, ForLoopInfo::Collection {
6042 6038
                elemType: item, length: nil, bindingName, indexName
6043 6039
            });
6040 +
            return *item;
6044 6041
        }
6045 6042
        case Type::Range { start, .. } => {
6046 6043
            // Iterable ranges must have a start, and since we enforce type
6047 6044
            // equality for start and end, that is always the item type.
6048 6045
            let valType = start else {
6049 6046
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
6050 6047
            };
6051 6048
            let case ast::NodeValue::Range(range) = forStmt.iterable.value else {
6052 6049
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
6053 6050
            };
6054 -
            set itemTy = *valType;
6055 -
6056 6051
            setForLoopInfo(self, node, ForLoopInfo::Range {
6057 6052
                valType, range, bindingName, indexName
6058 6053
            });
6054 +
            return *valType;
6059 6055
        }
6060 6056
        case Type::Array(arrayInfo) => {
6061 -
            set itemTy = *arrayInfo.item;
6062 6057
            setForLoopInfo(self, node, ForLoopInfo::Collection {
6063 6058
                elemType: arrayInfo.item,
6064 6059
                length: arrayInfo.length,
6065 6060
                bindingName,
6066 6061
                indexName,
6067 6062
            });
6063 +
            return *arrayInfo.item;
6068 6064
        }
6069 6065
        else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable),
6070 6066
    }
6067 +
}
6068 +
6069 +
/// Analyze a `for` loop, binding iteration variables.
6070 +
unsafe fn resolveFor 'arena (self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For) -> Type
6071 +
    throws (ResolveError)
6072 +
{
6073 +
    let iterableTy = try infer(self, forStmt.iterable);
6074 +
    let itemTy = try resolveForInfo(self, node, forStmt, iterableTy);
6071 6075
    enterScope(self, node);
6072 6076
    try bindForLoopPattern(self, forStmt.binding, itemTy, false);
6073 6077
6074 6078
    if let pat = forStmt.index {
6075 6079
        try bindForLoopPattern(self, pat, Type::U32, false);
lib/std/lang/resolver/tests.rad +76 -0
53 53
            }
54 54
        }
55 55
    }
56 56
}
57 57
58 +
/// Loop bindings and lowering metadata retain the same item type and names.
59 +
@test unsafe fn testForLoopMetadata() throws (testing::TestError) {
60 +
    let mut arena = testArena();
61 +
    let storage: 'test = &mut arena in {
62 +
        let mut res = testResolver(storage);
63 +
        let result = try resolveProgramStr(&mut res,
64 +
            "fn f(s: &[u8], start: u64, end: u64) { let xs: [u16; 2] = [1, 2]; for value, idx in xs {} for byte in s {} for _, idx in start..end {} }"
65 +
        );
66 +
        try expectNoErrors(&result);
67 +
        let body = try getFnBody(&res, result.root, "f");
68 +
        for index in [1 as u32, 2, 3] {
69 +
            let node = body.statements[index];
70 +
            let case ast::NodeValue::For(loopNode) = node.value else throw testing::TestError::Failed;
71 +
            let info = super::forLoopInfoFor(&res, node) else throw testing::TestError::Failed;
72 +
            let bindingType = super::typeFor(&res, loopNode.binding) else throw testing::TestError::Failed;
73 +
            match info {
74 +
                case super::ForLoopInfo::Collection { elemType, length, bindingName, indexName } => {
75 +
                    assert bindingType == *elemType;
76 +
                    let name = bindingName else throw testing::TestError::Failed;
77 +
                    if index == 1 {
78 +
                        assert *elemType == super::Type::U16;
79 +
                        let count = length else throw testing::TestError::Failed;
80 +
                        assert count == 2;
81 +
                        assert mem::eq(name, "value");
82 +
                        let name = indexName else throw testing::TestError::Failed;
83 +
                        assert mem::eq(name, "idx");
84 +
                    } else {
85 +
                        assert index == 2;
86 +
                        assert *elemType == super::Type::U8;
87 +
                        assert length == nil;
88 +
                        assert mem::eq(name, "byte");
89 +
                        assert indexName == nil;
90 +
                    }
91 +
                }
92 +
                case super::ForLoopInfo::Range { valType, range, bindingName, indexName } => {
93 +
                    assert index == 3;
94 +
                    assert bindingType == *valType;
95 +
                    assert *valType == super::Type::U64;
96 +
                    assert bindingName == nil;
97 +
                    let name = indexName else throw testing::TestError::Failed;
98 +
                    assert mem::eq(name, "idx");
99 +
                    let case ast::NodeValue::Range(source) = loopNode.iterable.value else throw testing::TestError::Failed;
100 +
                    assert range == source;
101 +
                }
102 +
            }
103 +
            if let binding = loopNode.index {
104 +
                let ty = super::typeFor(&res, binding) else throw testing::TestError::Failed;
105 +
                assert ty == super::Type::U32;
106 +
            }
107 +
        }
108 +
    }
109 +
}
110 +
111 +
/// Invalid iterables and unsafe slice iteration retain their diagnostics.
112 +
@test unsafe fn testForLoopMetadataErrors() throws (testing::TestError) {
113 +
    for program in [
114 +
        "fn f() { for x in true {} }",
115 +
        "fn f(end: u32) { for x in ..end {} }",
116 +
        "fn f(start: u32, end: u32) { let range = start..end; for x in range {} }",
117 +
    ] {
118 +
        let mut arena = testArena();
119 +
        let storage: 'test = &mut arena in {
120 +
            let mut res = testResolver(storage);
121 +
            let result = try resolveProgramStr(&mut res, program);
122 +
            try expectErrorKind(&result, super::ErrorKind::ExpectedIterable);
123 +
        }
124 +
    }
125 +
    let mut arena = testArena();
126 +
    let storage: 'test = &mut arena in {
127 +
        let mut res = testResolver(storage);
128 +
        let result = try resolveProgramStr(&mut res, "fn f(s: *unsafe [u32]) { for x in s {} }");
129 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
130 +
    }
131 +
    try expectAnalyzeOk("unsafe fn f(s: *unsafe [u32]) { for x in s {} }");
132 +
}
133 +
58 134
/// Slice operations retain initialized element, capacity, and access metadata.
59 135
@test unsafe fn testSliceRangeMetadata() throws (testing::TestError) {
60 136
    let mut arena = testArena();
61 137
    let storage: 'test = &mut arena in {
62 138
        let mut res = testResolver(storage);
test/tests/regions.for.metadata.rad added +45 -0
1 +
//! returns: 0
2 +
3 +
/// Iterate arrays, regional slices, and ranges through each loop exit path.
4 +
@default fn main() -> u32 {
5 +
    let values: [u32; 4] = [2, 4, 6, 8];
6 +
    let mut sum: u32 = 0;
7 +
    for value, index in values {
8 +
        assert value == (index + 1) * 2;
9 +
        set sum += value;
10 +
    }
11 +
    assert sum == 20;
12 +
    let view: 'items = &values[..] in {
13 +
        set sum = 0;
14 +
        for value, index in view {
15 +
            if index == 0 { continue; }
16 +
            if index == 3 { break; }
17 +
            set sum += value;
18 +
        }
19 +
        assert sum == 10;
20 +
        for _ in &view[2..2] {
21 +
            assert false;
22 +
        }
23 +
        assert sum == 10;
24 +
    }
25 +
    let start: i32 = -2;
26 +
    let end: i32 = 3;
27 +
    let mut signed: i32 = 0;
28 +
    for value, index in start..end {
29 +
        assert value == start + index as i32;
30 +
        set signed += value;
31 +
    }
32 +
    assert signed == 0;
33 +
    let high: u64 = 4294967296;
34 +
    let mut count: u32 = 0;
35 +
    for value, index in high..(high + 3) {
36 +
        assert value == high + index as u64;
37 +
        set count += 1;
38 +
    }
39 +
    assert count == 3;
40 +
    let empty: [u32; 0] = [];
41 +
    for _ in empty { assert false; }
42 +
    for _ in high..high { assert false; }
43 +
    assert count == 3;
44 +
    return 0;
45 +
}