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 {...@@ -737,7 +737,13 @@ pub const Scope = struct {
737 arena: *Allocator,737 arena: *Allocator,
738 /// The first N instructions in a function body ZIR are arg instructions.738 /// The first N instructions in a function body ZIR are arg instructions.
739 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},739 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 };
741 };747 };
742748
743 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.749 /// 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...@@ -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}
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
170pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {220pub 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();
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
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);
194267
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());
196269
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 });
202272
203 return &block.base;273 return &block_inst.base;
204}274}
205275
206fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void {276fn 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}
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
407/// Identifier token -> String (allocated in scope.arena())485/// 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 {
409 const tree = scope.tree();487 const tree = scope.tree();
410488
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) {
src-self-hosted/zir_sema.zig+1-1
...@@ -504,7 +504,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr...@@ -504,7 +504,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr
504 .decl = parent_block.decl,504 .decl = parent_block.decl,
505 .instructions = .{},505 .instructions = .{},
506 .arena = parent_block.arena,506 .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 :(
508 .label = @as(?Scope.Block.Label, Scope.Block.Label{508 .label = @as(?Scope.Block.Label, Scope.Block.Label{
509 .zir_block = inst,509 .zir_block = inst,
510 .results = .{},510 .results = .{},