also looking at this
fix(types): define the index variable of an enumerating for loop
#935
2 files
+57
-0
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -2079,6 +2079,16 @@ pub const TypeChecker = struct { | ||
| 2079 | 2079 | iterable_type.Array.element_type.* |
| 2080 | 2080 | else |
| 2081 | 2081 | Type.Int; |
| 2082 | // `for i, item in items` also binds `i`: for an array, string | |
| 2083 | // or integer iterable it is the position, an int, defined | |
| 2084 | // before the item as the interpreter does. A map binds its | |
| 2085 | // value there, and the checker cannot type that yet, so other | |
| 2086 | // iterables are left as they were. | |
| 2087 | if (for_stmt.index) |index_name| { | |
| 2088 | if (iterable_type == .Array or iterable_type == .String or isIntegerType(iterable_type)) { | |
| 2089 | try loop_env.define(index_name, Type.Int); | |
| 2090 | } | |
| 2091 | } | |
| 2082 | 2092 | try loop_env.define(for_stmt.iterator, iterator_type); |
| 2083 | 2093 | |
| 2084 | 2094 | // Check body with loop scope |
| @@ -130,6 +130,53 @@ test "checker accepts a correctly typed function return" { | ||
| 130 | 130 | )); |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | fn checkSourceInArena(source: []const u8) !bool { | |
| 134 | // Arena: the checker does not free for-loop scopes yet. | |
| 135 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | |
| 136 | defer arena.deinit(); | |
| 137 | const allocator = arena.allocator(); | |
| 138 | var lexer = home.lexer.Lexer.init(allocator, source); | |
| 139 | const tokens = try lexer.tokenize(); | |
| 140 | var parser = try home.parser.Parser.init(allocator, tokens.items); | |
| 141 | const program = try parser.parse(); | |
| 142 | var checker = home.types.TypeChecker.init(allocator, program); | |
| 143 | return checker.check(); | |
| 144 | } | |
| 145 | ||
| 146 | test "checker defines the index variable of an enumerating for loop" { | |
| 147 | try std.testing.expect(try checkSourceInArena( | |
| 148 | \\fn run() { | |
| 149 | \\ for i, x in [10, 20] { | |
| 150 | \\ let next = i + 1 | |
| 151 | \\ } | |
| 152 | \\} | |
| 153 | )); | |
| 154 | // For a map the first name is bound to the value, not an int position. | |
| 155 | try std.testing.expect(!try checkSourceInArena( | |
| 156 | \\fn run() { | |
| 157 | \\ for value, key in {"k": true} { | |
| 158 | \\ let out = value + 1 | |
| 159 | \\ } | |
| 160 | \\} | |
| 161 | )); | |
| 162 | try std.testing.expect(!try checkSourceInArena( | |
| 163 | \\fn run() { | |
| 164 | \\ let data = {"k": true} as ?Map<string, bool> | |
| 165 | \\ for value, key in data { | |
| 166 | \\ let out = value + 1 | |
| 167 | \\ } | |
| 168 | \\} | |
| 169 | )); | |
| 170 | // Same name twice: the item is bound last, as at run time. | |
| 171 | try std.testing.expect(try checkSourceInArena( | |
| 172 | \\fn run() { | |
| 173 | \\ for x, x in ["hello"] { | |
| 174 | \\ let out = x + "!" | |
| 175 | \\ } | |
| 176 | \\} | |
| 177 | )); | |
| 178 | } | |
| 179 | ||
| 133 | 180 | test "checker recognizes never as the bottom type" { |
| 134 | 181 | try std.testing.expect(try checkSource( |
| 135 | 182 | \\fn halt(): never { |