compiler: Construct for-loop metadata in checked code
50908ae883e3e5f97c9663b13800d3393f53bbd20249c5717273e128723e695e
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 | + | } |