Commit 0f3f96c850
Changed files (3)
src-self-hosted
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) {
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.
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 = .{},