authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-15 00:52:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-15 00:52:25-07:00
log0f3f96c85095876e7e6f3f00e60915ec41f63700
tree294c35e9f610c31d263d407501531d11efddff5c
parentf356cba704e8e45832ac363b5bf205a63f39257a

stage2: astgen for labeled blocks and labeled breaks


3 files changed, 98 insertions(+), 14 deletions(-)

src-self-hosted/Module.zig+7-1
......@@ -737,7 +737,13 @@ pub const Scope = struct {
737737 arena: *Allocator,
738738 /// The first N instructions in a function body ZIR are arg instructions.
739739 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
740 label: ?ast.TokenIndex = null,
740 label: ?Label = null,
741
742 pub const Label = struct {
743 token: ast.TokenIndex,
744 block_inst: *zir.Inst.Block,
745 result_loc: astgen.ResultLoc,
746 };
741747 };
742748
743749 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
src-self-hosted/astgen.zig+90-12
......@@ -121,6 +121,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
121121 .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
122122 .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
123123 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?),
124 .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
125
124126 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
125127 .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}),
126128 .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
150152 .For => return mod.failNode(scope, node, "TODO implement astgen.expr for .For", .{}),
151153 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
152154 .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", .{}),
154155 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
155156 .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}),
156157 .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
167168 }
168169}
169170
171fn 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
170220pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {
171221 const tracy = trace(@src());
172222 defer tracy.end();
......@@ -183,24 +233,44 @@ fn labeledBlockExpr(
183233 const tracy = trace(@src());
184234 defer tracy.end();
185235
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
186254 var block_scope: Scope.GenZIR = .{
187255 .parent = parent_scope,
188256 .decl = parent_scope.decl().?,
189 .arena = parent_scope.arena(),
257 .arena = gen_zir.arena,
190258 .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 }),
192265 };
193266 defer block_scope.instructions.deinit(mod.gpa);
194267
195268 try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
196269
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);
202272
203 return &block.base;
273 return &block_inst.base;
204274}
205275
206276fn 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
344414 if (infix_node.lhs.castTag(.Identifier)) |ident| {
345415 // This intentionally does not support @"_" syntax.
346416 const ident_name = scope.tree().tokenSlice(ident.token);
347 if (std.mem.eql(u8, ident_name, "_")) {
417 if (mem.eql(u8, ident_name, "_")) {
348418 _ = try expr(mod, scope, .discard, infix_node.rhs);
349419 return;
350420 }
......@@ -404,12 +474,20 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si
404474 return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr));
405475}
406476
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.
479fn 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
407485/// Identifier token -> String (allocated in scope.arena())
408pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
486fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
409487 const tree = scope.tree();
410488
411489 const ident_name = tree.tokenSlice(token);
412 if (std.mem.startsWith(u8, ident_name, "@")) {
490 if (mem.startsWith(u8, ident_name, "@")) {
413491 const raw_string = ident_name[1..];
414492 var bad_index: usize = undefined;
415493 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
src-self-hosted/zir_sema.zig+1-1
......@@ -504,7 +504,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr
504504 .decl = parent_block.decl,
505505 .instructions = .{},
506506 .arena = parent_block.arena,
507 // TODO @as here is working around a miscompilation compiler bug :(
507 // TODO @as here is working around a stage1 miscompilation bug :(
508508 .label = @as(?Scope.Block.Label, Scope.Block.Label{
509509 .zir_block = inst,
510510 .results = .{},