| ... | ... | @@ -121,6 +121,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 121 | 121 | .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), |
| 122 | 122 | .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), |
| 123 | 123 | .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?), |
| 124 | .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)), |
| 125 | |
| 124 | 126 | .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), |
| 125 | 127 | .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), |
| 126 | 128 | .BoolAnd => return mod.failNode(scope, node, "TODO implement astgen.expr for .BoolAnd", .{}), |
| ... | ... | @@ -150,7 +152,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 150 | 152 | .For => return mod.failNode(scope, node, "TODO implement astgen.expr for .For", .{}), |
| 151 | 153 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), |
| 152 | 154 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), |
| 153 | | .Break => return mod.failNode(scope, node, "TODO implement astgen.expr for .Break", .{}), |
| 154 | 155 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), |
| 155 | 156 | .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), |
| 156 | 157 | .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), |
| ... | ... | @@ -167,6 +168,55 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 167 | 168 | } |
| 168 | 169 | } |
| 169 | 170 | |
| 171 | fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { |
| 172 | const tree = parent_scope.tree(); |
| 173 | const src = tree.token_locs[node.ltoken].start; |
| 174 | |
| 175 | if (node.getLabel()) |break_label| { |
| 176 | // Look for the label in the scope. |
| 177 | var scope = parent_scope; |
| 178 | while (true) { |
| 179 | switch (scope.tag) { |
| 180 | .gen_zir => { |
| 181 | const gen_zir = scope.cast(Scope.GenZIR).?; |
| 182 | if (gen_zir.label) |label| { |
| 183 | if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { |
| 184 | if (node.getRHS()) |rhs| { |
| 185 | // Most result location types can be forwarded directly; however |
| 186 | // if we need to write to a pointer which has an inferred type, |
| 187 | // proper type inference requires peer type resolution on the block's |
| 188 | // break operand expressions. |
| 189 | const branch_rl: ResultLoc = switch (label.result_loc) { |
| 190 | .discard, .none, .ty, .ptr, .lvalue => label.result_loc, |
| 191 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst }, |
| 192 | }; |
| 193 | const operand = try expr(mod, parent_scope, branch_rl, rhs); |
| 194 | return try addZIRInst(mod, scope, src, zir.Inst.Break, .{ |
| 195 | .block = label.block_inst, |
| 196 | .operand = operand, |
| 197 | }, .{}); |
| 198 | } else { |
| 199 | return try addZIRInst(mod, scope, src, zir.Inst.BreakVoid, .{ |
| 200 | .block = label.block_inst, |
| 201 | }, .{}); |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | scope = gen_zir.parent; |
| 206 | }, |
| 207 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, |
| 208 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, |
| 209 | else => { |
| 210 | const label_name = try identifierTokenString(mod, parent_scope, break_label); |
| 211 | return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name}); |
| 212 | }, |
| 213 | } |
| 214 | } |
| 215 | } else { |
| 216 | return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{}); |
| 217 | } |
| 218 | } |
| 219 | |
| 170 | 220 | pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void { |
| 171 | 221 | const tracy = trace(@src()); |
| 172 | 222 | defer tracy.end(); |
| ... | ... | @@ -183,24 +233,44 @@ fn labeledBlockExpr( |
| 183 | 233 | const tracy = trace(@src()); |
| 184 | 234 | defer tracy.end(); |
| 185 | 235 | |
| 236 | const tree = parent_scope.tree(); |
| 237 | const src = tree.token_locs[block_node.lbrace].start; |
| 238 | |
| 239 | // Create the Block ZIR instruction so that we can put it into the GenZIR struct |
| 240 | // so that break statements can reference it. |
| 241 | const gen_zir = parent_scope.getGenZIR(); |
| 242 | const block_inst = try gen_zir.arena.create(zir.Inst.Block); |
| 243 | block_inst.* = .{ |
| 244 | .base = .{ |
| 245 | .tag = .block, |
| 246 | .src = src, |
| 247 | }, |
| 248 | .positionals = .{ |
| 249 | .body = .{ .instructions = undefined }, |
| 250 | }, |
| 251 | .kw_args = .{}, |
| 252 | }; |
| 253 | |
| 186 | 254 | var block_scope: Scope.GenZIR = .{ |
| 187 | 255 | .parent = parent_scope, |
| 188 | 256 | .decl = parent_scope.decl().?, |
| 189 | | .arena = parent_scope.arena(), |
| 257 | .arena = gen_zir.arena, |
| 190 | 258 | .instructions = .{}, |
| 191 | | .label = block_node.label, |
| 259 | // TODO @as here is working around a stage1 miscompilation bug :( |
| 260 | .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ |
| 261 | .token = block_node.label, |
| 262 | .block_inst = block_inst, |
| 263 | .result_loc = rl, |
| 264 | }), |
| 192 | 265 | }; |
| 193 | 266 | defer block_scope.instructions.deinit(mod.gpa); |
| 194 | 267 | |
| 195 | 268 | try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); |
| 196 | 269 | |
| 197 | | const tree = parent_scope.tree(); |
| 198 | | const src = tree.token_locs[block_node.lbrace].start; |
| 199 | | const block = try addZIRInstBlock(mod, parent_scope, src, .{ |
| 200 | | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), |
| 201 | | }); |
| 270 | block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); |
| 271 | try gen_zir.instructions.append(mod.gpa, &block_inst.base); |
| 202 | 272 | |
| 203 | | return &block.base; |
| 273 | return &block_inst.base; |
| 204 | 274 | } |
| 205 | 275 | |
| 206 | 276 | fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void { |
| ... | ... | @@ -344,7 +414,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne |
| 344 | 414 | if (infix_node.lhs.castTag(.Identifier)) |ident| { |
| 345 | 415 | // This intentionally does not support @"_" syntax. |
| 346 | 416 | const ident_name = scope.tree().tokenSlice(ident.token); |
| 347 | | if (std.mem.eql(u8, ident_name, "_")) { |
| 417 | if (mem.eql(u8, ident_name, "_")) { |
| 348 | 418 | _ = try expr(mod, scope, .discard, infix_node.rhs); |
| 349 | 419 | return; |
| 350 | 420 | } |
| ... | ... | @@ -404,12 +474,20 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si |
| 404 | 474 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); |
| 405 | 475 | } |
| 406 | 476 | |
| 477 | /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. |
| 478 | /// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. |
| 479 | fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { |
| 480 | const ident_name_1 = try identifierTokenString(mod, scope, token1); |
| 481 | const ident_name_2 = try identifierTokenString(mod, scope, token2); |
| 482 | return mem.eql(u8, ident_name_1, ident_name_2); |
| 483 | } |
| 484 | |
| 407 | 485 | /// Identifier token -> String (allocated in scope.arena()) |
| 408 | | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 486 | fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 409 | 487 | const tree = scope.tree(); |
| 410 | 488 | |
| 411 | 489 | const ident_name = tree.tokenSlice(token); |
| 412 | | if (std.mem.startsWith(u8, ident_name, "@")) { |
| 490 | if (mem.startsWith(u8, ident_name, "@")) { |
| 413 | 491 | const raw_string = ident_name[1..]; |
| 414 | 492 | var bad_index: usize = undefined; |
| 415 | 493 | return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { |