| ... | @@ -121,6 +121,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr | ... | @@ -121,6 +121,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 121 | .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), | 121 | .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), |
| 122 | .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), | 122 | .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), |
| 123 | .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?), | 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 | .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), | 126 | .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), |
| 125 | .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), | 127 | .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), |
| 126 | .BoolAnd => return mod.failNode(scope, node, "TODO implement astgen.expr for .BoolAnd", .{}), | 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,7 +152,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 150 | .For => return mod.failNode(scope, node, "TODO implement astgen.expr for .For", .{}), | 152 | .For => return mod.failNode(scope, node, "TODO implement astgen.expr for .For", .{}), |
| 151 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), | 153 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), |
| 152 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), | 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 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), | 155 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), |
| 155 | .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), | 156 | .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), |
| 156 | .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), | 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,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 | pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void { | 220 | pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void { |
| 171 | const tracy = trace(@src()); | 221 | const tracy = trace(@src()); |
| 172 | defer tracy.end(); | 222 | defer tracy.end(); |
| ... | @@ -183,24 +233,44 @@ fn labeledBlockExpr( | ... | @@ -183,24 +233,44 @@ fn labeledBlockExpr( |
| 183 | const tracy = trace(@src()); | 233 | const tracy = trace(@src()); |
| 184 | defer tracy.end(); | 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 | var block_scope: Scope.GenZIR = .{ | 254 | var block_scope: Scope.GenZIR = .{ |
| 187 | .parent = parent_scope, | 255 | .parent = parent_scope, |
| 188 | .decl = parent_scope.decl().?, | 256 | .decl = parent_scope.decl().?, |
| 189 | .arena = parent_scope.arena(), | 257 | .arena = gen_zir.arena, |
| 190 | .instructions = .{}, | 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 | defer block_scope.instructions.deinit(mod.gpa); | 266 | defer block_scope.instructions.deinit(mod.gpa); |
| 194 | | 267 | |
| 195 | try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); | 268 | try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); |
| 196 | | 269 | |
| 197 | const tree = parent_scope.tree(); | 270 | block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); |
| 198 | const src = tree.token_locs[block_node.lbrace].start; | 271 | try gen_zir.instructions.append(mod.gpa, &block_inst.base); |
| 199 | const block = try addZIRInstBlock(mod, parent_scope, src, .{ | | |
| 200 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | | |
| 201 | }); | | |
| 202 | | 272 | |
| 203 | return &block.base; | 273 | return &block_inst.base; |
| 204 | } | 274 | } |
| 205 | | 275 | |
| 206 | fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void { | 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,7 +414,7 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne |
| 344 | if (infix_node.lhs.castTag(.Identifier)) |ident| { | 414 | if (infix_node.lhs.castTag(.Identifier)) |ident| { |
| 345 | // This intentionally does not support @"_" syntax. | 415 | // This intentionally does not support @"_" syntax. |
| 346 | const ident_name = scope.tree().tokenSlice(ident.token); | 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 | _ = try expr(mod, scope, .discard, infix_node.rhs); | 418 | _ = try expr(mod, scope, .discard, infix_node.rhs); |
| 349 | return; | 419 | return; |
| 350 | } | 420 | } |
| ... | @@ -404,12 +474,20 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si | ... | @@ -404,12 +474,20 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si |
| 404 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); | 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 | /// Identifier token -> String (allocated in scope.arena()) | 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 | const tree = scope.tree(); | 487 | const tree = scope.tree(); |
| 410 | | 488 | |
| 411 | const ident_name = tree.tokenSlice(token); | 489 | const ident_name = tree.tokenSlice(token); |
| 412 | if (std.mem.startsWith(u8, ident_name, "@")) { | 490 | if (mem.startsWith(u8, ident_name, "@")) { |
| 413 | const raw_string = ident_name[1..]; | 491 | const raw_string = ident_name[1..]; |
| 414 | var bad_index: usize = undefined; | 492 | var bad_index: usize = undefined; |
| 415 | return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { | 493 | return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { |