From 0f3f96c85095876e7e6f3f00e60915ec41f63700 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 15 Aug 2020 00:52:25 -0700 Subject: [PATCH] stage2: astgen for labeled blocks and labeled breaks --- src-self-hosted/Module.zig | 8 ++- src-self-hosted/astgen.zig | 102 ++++++++++++++++++++++++++++++----- src-self-hosted/zir_sema.zig | 2 +- 3 files changed, 98 insertions(+), 14 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 9ae477312a..2fad7e7a00 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -737,7 +737,13 @@ pub const Scope = struct { arena: *Allocator, /// The first N instructions in a function body ZIR are arg instructions. instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, - label: ?ast.TokenIndex = null, + label: ?Label = null, + + pub const Label = struct { + token: ast.TokenIndex, + block_inst: *zir.Inst.Block, + result_loc: astgen.ResultLoc, + }; }; /// This is always a `const` local and importantly the `inst` is a value type, not a pointer. diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 4dec87f364..1747d77df3 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -121,6 +121,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?), + .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)), + .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), .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 .For => return mod.failNode(scope, node, "TODO implement astgen.expr for .For", .{}), .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), - .Break => return mod.failNode(scope, node, "TODO implement astgen.expr for .Break", .{}), .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), .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 } } +fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { + const tree = parent_scope.tree(); + const src = tree.token_locs[node.ltoken].start; + + if (node.getLabel()) |break_label| { + // Look for the label in the scope. + var scope = parent_scope; + while (true) { + switch (scope.tag) { + .gen_zir => { + const gen_zir = scope.cast(Scope.GenZIR).?; + if (gen_zir.label) |label| { + if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) { + if (node.getRHS()) |rhs| { + // Most result location types can be forwarded directly; however + // if we need to write to a pointer which has an inferred type, + // proper type inference requires peer type resolution on the block's + // break operand expressions. + const branch_rl: ResultLoc = switch (label.result_loc) { + .discard, .none, .ty, .ptr, .lvalue => label.result_loc, + .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst }, + }; + const operand = try expr(mod, parent_scope, branch_rl, rhs); + return try addZIRInst(mod, scope, src, zir.Inst.Break, .{ + .block = label.block_inst, + .operand = operand, + }, .{}); + } else { + return try addZIRInst(mod, scope, src, zir.Inst.BreakVoid, .{ + .block = label.block_inst, + }, .{}); + } + } + } + scope = gen_zir.parent; + }, + .local_val => scope = scope.cast(Scope.LocalVal).?.parent, + .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + else => { + const label_name = try identifierTokenString(mod, parent_scope, break_label); + return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name}); + }, + } + } + } else { + return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{}); + } +} + pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -183,24 +233,44 @@ fn labeledBlockExpr( const tracy = trace(@src()); defer tracy.end(); + const tree = parent_scope.tree(); + const src = tree.token_locs[block_node.lbrace].start; + + // Create the Block ZIR instruction so that we can put it into the GenZIR struct + // so that break statements can reference it. + const gen_zir = parent_scope.getGenZIR(); + const block_inst = try gen_zir.arena.create(zir.Inst.Block); + block_inst.* = .{ + .base = .{ + .tag = .block, + .src = src, + }, + .positionals = .{ + .body = .{ .instructions = undefined }, + }, + .kw_args = .{}, + }; + var block_scope: Scope.GenZIR = .{ .parent = parent_scope, .decl = parent_scope.decl().?, - .arena = parent_scope.arena(), + .arena = gen_zir.arena, .instructions = .{}, - .label = block_node.label, + // TODO @as here is working around a stage1 miscompilation bug :( + .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ + .token = block_node.label, + .block_inst = block_inst, + .result_loc = rl, + }), }; defer block_scope.instructions.deinit(mod.gpa); try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements()); - const tree = parent_scope.tree(); - const src = tree.token_locs[block_node.lbrace].start; - const block = try addZIRInstBlock(mod, parent_scope, src, .{ - .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), - }); + block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); + try gen_zir.instructions.append(mod.gpa, &block_inst.base); - return &block.base; + return &block_inst.base; } 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 if (infix_node.lhs.castTag(.Identifier)) |ident| { // This intentionally does not support @"_" syntax. const ident_name = scope.tree().tokenSlice(ident.token); - if (std.mem.eql(u8, ident_name, "_")) { + if (mem.eql(u8, ident_name, "_")) { _ = try expr(mod, scope, .discard, infix_node.rhs); return; } @@ -404,12 +474,20 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); } +/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. +/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. +fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { + const ident_name_1 = try identifierTokenString(mod, scope, token1); + const ident_name_2 = try identifierTokenString(mod, scope, token2); + return mem.eql(u8, ident_name_1, ident_name_2); +} + /// Identifier token -> String (allocated in scope.arena()) -pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { +fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { const tree = scope.tree(); const ident_name = tree.tokenSlice(token); - if (std.mem.startsWith(u8, ident_name, "@")) { + if (mem.startsWith(u8, ident_name, "@")) { const raw_string = ident_name[1..]; var bad_index: usize = undefined; return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 862df4ec9a..2fe9e5cfba 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -504,7 +504,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, - // TODO @as here is working around a miscompilation compiler bug :( + // TODO @as here is working around a stage1 miscompilation bug :( .label = @as(?Scope.Block.Label, Scope.Block.Label{ .zir_block = inst, .results = .{},