authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-31 23:34:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-31 23:34:58-07:00
log717b0e827511b55375de82258f570709c07cc59d
treee546a29c60ebb9ada0188f76e1696077007e73d0
parent26140678a5c72604f2baac3cb9d1e5f7b37b6b8d

stage2: introduce the ability for Scope.Block to be comptime

This gives zir_sema analysis the ability to check if the current scope is expected to be comptime.

6 files changed, 162 insertions(+), 25 deletions(-)

src-self-hosted/Module.zig+8-3
...@@ -725,6 +725,7 @@ pub const Scope = struct {...@@ -725,6 +725,7 @@ pub const Scope = struct {
725 /// Points to the arena allocator of DeclAnalysis725 /// Points to the arena allocator of DeclAnalysis
726 arena: *Allocator,726 arena: *Allocator,
727 label: ?Label = null,727 label: ?Label = null,
728 is_comptime: bool,
728729
729 pub const Label = struct {730 pub const Label = struct {
730 zir_block: *zir.Inst.Block,731 zir_block: *zir.Inst.Block,
...@@ -1320,6 +1321,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1320,6 +1321,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1320 .decl = decl,1321 .decl = decl,
1321 .instructions = .{},1322 .instructions = .{},
1322 .arena = &decl_arena.allocator,1323 .arena = &decl_arena.allocator,
1324 .is_comptime = false,
1323 };1325 };
1324 defer block_scope.instructions.deinit(self.gpa);1326 defer block_scope.instructions.deinit(self.gpa);
13251327
...@@ -1457,6 +1459,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1457,6 +1459,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1457 .decl = decl,1459 .decl = decl,
1458 .instructions = .{},1460 .instructions = .{},
1459 .arena = &decl_arena.allocator,1461 .arena = &decl_arena.allocator,
1462 .is_comptime = true,
1460 };1463 };
1461 defer block_scope.instructions.deinit(self.gpa);1464 defer block_scope.instructions.deinit(self.gpa);
14621465
...@@ -1528,7 +1531,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1528,7 +1531,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1528 defer gen_scope.instructions.deinit(self.gpa);1531 defer gen_scope.instructions.deinit(self.gpa);
1529 const src = tree.token_locs[init_node.firstToken()].start;1532 const src = tree.token_locs[init_node.firstToken()].start;
15301533
1531 // TODO comptime scope here
1532 const init_inst = try astgen.expr(self, &gen_scope.base, .none, init_node);1534 const init_inst = try astgen.expr(self, &gen_scope.base, .none, init_node);
1533 _ = try astgen.addZIRUnOp(self, &gen_scope.base, src, .@"return", init_inst);1535 _ = try astgen.addZIRUnOp(self, &gen_scope.base, src, .@"return", init_inst);
15341536
...@@ -1538,6 +1540,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1538,6 +1540,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1538 .decl = decl,1540 .decl = decl,
1539 .instructions = .{},1541 .instructions = .{},
1540 .arena = &gen_scope_arena.allocator,1542 .arena = &gen_scope_arena.allocator,
1543 .is_comptime = true,
1541 };1544 };
1542 defer inner_block.instructions.deinit(self.gpa);1545 defer inner_block.instructions.deinit(self.gpa);
1543 try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });1546 try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
...@@ -1628,8 +1631,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1628,8 +1631,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1628 };1631 };
1629 defer gen_scope.instructions.deinit(self.gpa);1632 defer gen_scope.instructions.deinit(self.gpa);
16301633
1631 // TODO comptime scope here1634 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
1632 _ = try astgen.expr(self, &gen_scope.base, .none, comptime_decl.expr);
16331635
1634 var block_scope: Scope.Block = .{1636 var block_scope: Scope.Block = .{
1635 .parent = null,1637 .parent = null,
...@@ -1637,6 +1639,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1637,6 +1639,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1637 .decl = decl,1639 .decl = decl,
1638 .instructions = .{},1640 .instructions = .{},
1639 .arena = &analysis_arena.allocator,1641 .arena = &analysis_arena.allocator,
1642 .is_comptime = true,
1640 };1643 };
1641 defer block_scope.instructions.deinit(self.gpa);1644 defer block_scope.instructions.deinit(self.gpa);
16421645
...@@ -2007,6 +2010,7 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -2007,6 +2010,7 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
2007 .decl = decl,2010 .decl = decl,
2008 .instructions = .{},2011 .instructions = .{},
2009 .arena = &arena.allocator,2012 .arena = &arena.allocator,
2013 .is_comptime = false,
2010 };2014 };
2011 defer inner_block.instructions.deinit(self.gpa);2015 defer inner_block.instructions.deinit(self.gpa);
20122016
...@@ -3432,6 +3436,7 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic...@@ -3432,6 +3436,7 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
3432 .decl = parent_block.decl,3436 .decl = parent_block.decl,
3433 .instructions = .{},3437 .instructions = .{},
3434 .arena = parent_block.arena,3438 .arena = parent_block.arena,
3439 .is_comptime = parent_block.is_comptime,
3435 };3440 };
3436 defer fail_block.instructions.deinit(mod.gpa);3441 defer fail_block.instructions.deinit(mod.gpa);
34373442
src-self-hosted/astgen.zig+73-16
...@@ -258,7 +258,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -258,7 +258,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
258 .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),258 .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
259 .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),259 .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
260 .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),260 .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
261 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?),261 .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
262 .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),262 .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
263 .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),263 .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
264 .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),264 .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
...@@ -276,6 +276,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -276,6 +276,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
276 .For => return forExpr(mod, scope, rl, node.castTag(.For).?),276 .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
277 .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),277 .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
278 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),278 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
279 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
279280
280 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),281 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
281 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),282 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
...@@ -294,11 +295,46 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -294,11 +295,46 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
294 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),295 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
295 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),296 .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
296 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),297 .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
297 .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}),
298 .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),298 .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
299 }299 }
300}300}
301301
302fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {
303 const tracy = trace(@src());
304 defer tracy.end();
305
306 return comptimeExpr(mod, scope, rl, node.expr);
307}
308
309pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
310 const tree = parent_scope.tree();
311 const src = tree.token_locs[node.firstToken()].start;
312
313 // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one.
314 if (node.castTag(.LabeledBlock)) |block_node| {
315 return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
316 }
317
318 // Make a scope to collect generated instructions in the sub-expression.
319 var block_scope: Scope.GenZIR = .{
320 .parent = parent_scope,
321 .decl = parent_scope.decl().?,
322 .arena = parent_scope.arena(),
323 .instructions = .{},
324 };
325 defer block_scope.instructions.deinit(mod.gpa);
326
327 // No need to capture the result here because block_comptime_flat implies that the final
328 // instruction is the block's result value.
329 _ = try expr(mod, &block_scope.base, rl, node);
330
331 const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
332 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
333 });
334
335 return &block.base;
336}
337
302fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {338fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
303 const tree = parent_scope.tree();339 const tree = parent_scope.tree();
304 const src = tree.token_locs[node.ltoken].start;340 const src = tree.token_locs[node.ltoken].start;
...@@ -360,10 +396,13 @@ fn labeledBlockExpr(...@@ -360,10 +396,13 @@ fn labeledBlockExpr(
360 parent_scope: *Scope,396 parent_scope: *Scope,
361 rl: ResultLoc,397 rl: ResultLoc,
362 block_node: *ast.Node.LabeledBlock,398 block_node: *ast.Node.LabeledBlock,
399 zir_tag: zir.Inst.Tag,
363) InnerError!*zir.Inst {400) InnerError!*zir.Inst {
364 const tracy = trace(@src());401 const tracy = trace(@src());
365 defer tracy.end();402 defer tracy.end();
366403
404 assert(zir_tag == .block or zir_tag == .block_comptime);
405
367 const tree = parent_scope.tree();406 const tree = parent_scope.tree();
368 const src = tree.token_locs[block_node.lbrace].start;407 const src = tree.token_locs[block_node.lbrace].start;
369408
...@@ -373,7 +412,7 @@ fn labeledBlockExpr(...@@ -373,7 +412,7 @@ fn labeledBlockExpr(
373 const block_inst = try gen_zir.arena.create(zir.Inst.Block);412 const block_inst = try gen_zir.arena.create(zir.Inst.Block);
374 block_inst.* = .{413 block_inst.* = .{
375 .base = .{414 .base = .{
376 .tag = .block,415 .tag = zir_tag,
377 .src = src,416 .src = src,
378 },417 },
379 .positionals = .{418 .positionals = .{
...@@ -773,7 +812,7 @@ fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch)...@@ -773,7 +812,7 @@ fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch)
773 .else_body = undefined, // populated below812 .else_body = undefined, // populated below
774 }, .{});813 }, .{});
775814
776 const block = try addZIRInstBlock(mod, scope, src, .{815 const block = try addZIRInstBlock(mod, scope, src, .block, .{
777 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),816 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
778 });817 });
779818
...@@ -946,7 +985,7 @@ fn boolBinOp(...@@ -946,7 +985,7 @@ fn boolBinOp(
946 .else_body = undefined, // populated below985 .else_body = undefined, // populated below
947 }, .{});986 }, .{});
948987
949 const block = try addZIRInstBlock(mod, scope, src, .{988 const block = try addZIRInstBlock(mod, scope, src, .block, .{
950 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),989 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
951 });990 });
952991
...@@ -1095,7 +1134,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn...@@ -1095,7 +1134,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
1095 .else_body = undefined, // populated below1134 .else_body = undefined, // populated below
1096 }, .{});1135 }, .{});
10971136
1098 const block = try addZIRInstBlock(mod, scope, if_src, .{1137 const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
1099 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),1138 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
1100 });1139 });
11011140
...@@ -1218,7 +1257,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W...@@ -1218,7 +1257,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1218 .then_body = undefined, // populated below1257 .then_body = undefined, // populated below
1219 .else_body = undefined, // populated below1258 .else_body = undefined, // populated below
1220 }, .{});1259 }, .{});
1221 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .{1260 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
1222 .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),1261 .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
1223 });1262 });
1224 // TODO avoid emitting the continue expr when there1263 // TODO avoid emitting the continue expr when there
...@@ -1231,7 +1270,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W...@@ -1231,7 +1270,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
1231 const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{1270 const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
1232 .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),1271 .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
1233 });1272 });
1234 const while_block = try addZIRInstBlock(mod, scope, while_src, .{1273 const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
1235 .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),1274 .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
1236 });1275 });
12371276
...@@ -1365,7 +1404,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)...@@ -1365,7 +1404,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
1365 .then_body = undefined, // populated below1404 .then_body = undefined, // populated below
1366 .else_body = undefined, // populated below1405 .else_body = undefined, // populated below
1367 }, .{});1406 }, .{});
1368 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .{1407 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
1369 .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),1408 .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
1370 });1409 });
13711410
...@@ -1382,7 +1421,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)...@@ -1382,7 +1421,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
1382 const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{1421 const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
1383 .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),1422 .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
1384 });1423 });
1385 const for_block = try addZIRInstBlock(mod, scope, for_src, .{1424 const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
1386 .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),1425 .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
1387 });1426 });
13881427
...@@ -2260,6 +2299,30 @@ pub fn addZIRBinOp(...@@ -2260,6 +2299,30 @@ pub fn addZIRBinOp(
2260 return &inst.base;2299 return &inst.base;
2261}2300}
22622301
2302pub fn addZIRInstBlock(
2303 mod: *Module,
2304 scope: *Scope,
2305 src: usize,
2306 tag: zir.Inst.Tag,
2307 body: zir.Module.Body,
2308) !*zir.Inst.Block {
2309 const gen_zir = scope.getGenZIR();
2310 try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
2311 const inst = try gen_zir.arena.create(zir.Inst.Block);
2312 inst.* = .{
2313 .base = .{
2314 .tag = tag,
2315 .src = src,
2316 },
2317 .positionals = .{
2318 .body = body,
2319 },
2320 .kw_args = .{},
2321 };
2322 gen_zir.instructions.appendAssumeCapacity(&inst.base);
2323 return inst;
2324}
2325
2263pub fn addZIRInst(2326pub fn addZIRInst(
2264 mod: *Module,2327 mod: *Module,
2265 scope: *Scope,2328 scope: *Scope,
...@@ -2278,12 +2341,6 @@ pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: Typ...@@ -2278,12 +2341,6 @@ pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: Typ
2278 return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});2341 return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
2279}2342}
22802343
2281/// TODO The existence of this function is a workaround for a bug in stage1.
2282pub fn addZIRInstBlock(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
2283 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
2284 return addZIRInstSpecial(mod, scope, src, zir.Inst.Block, P{ .body = body }, .{});
2285}
2286
2287/// TODO The existence of this function is a workaround for a bug in stage1.2344/// TODO The existence of this function is a workaround for a bug in stage1.
2288pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop {2345pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop {
2289 const P = std.meta.fieldInfo(zir.Inst.Loop, "positionals").field_type;2346 const P = std.meta.fieldInfo(zir.Inst.Loop, "positionals").field_type;
src-self-hosted/ir.zig+9-1
...@@ -189,7 +189,7 @@ pub const Inst = struct {...@@ -189,7 +189,7 @@ pub const Inst = struct {
189 }189 }
190190
191 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {191 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
192 return switch (self.base.tag) {192 return switch (base.tag) {
193 .cmp_lt => .lt,193 .cmp_lt => .lt,
194 .cmp_lte => .lte,194 .cmp_lte => .lte,
195 .cmp_eq => .eq,195 .cmp_eq => .eq,
...@@ -220,6 +220,14 @@ pub const Inst = struct {...@@ -220,6 +220,14 @@ pub const Inst = struct {
220 unreachable;220 unreachable;
221 }221 }
222222
223 pub fn breakBlock(base: *Inst) ?*Block {
224 return switch (base.tag) {
225 .br => base.castTag(.br).?.block,
226 .brvoid => base.castTag(.brvoid).?.block,
227 else => null,
228 };
229 }
230
223 pub const NoOp = struct {231 pub const NoOp = struct {
224 base: Inst,232 base: Inst,
225233
src-self-hosted/zir.zig+16-1
...@@ -78,6 +78,13 @@ pub const Inst = struct {...@@ -78,6 +78,13 @@ pub const Inst = struct {
78 bitor,78 bitor,
79 /// A labeled block of code, which can return a value.79 /// A labeled block of code, which can return a value.
80 block,80 block,
81 /// A block of code, which can return a value. There are no instructions that break out of
82 /// this block; it is implied that the final instruction is the result.
83 block_flat,
84 /// Same as `block` but additionally makes the inner instructions execute at comptime.
85 block_comptime,
86 /// Same as `block_flat` but additionally makes the inner instructions execute at comptime.
87 block_comptime_flat,
81 /// Boolean NOT. See also `bitnot`.88 /// Boolean NOT. See also `bitnot`.
82 boolnot,89 boolnot,
83 /// Return a value from a `Block`.90 /// Return a value from a `Block`.
...@@ -338,9 +345,14 @@ pub const Inst = struct {...@@ -338,9 +345,14 @@ pub const Inst = struct {
338 .merge_error_sets,345 .merge_error_sets,
339 => BinOp,346 => BinOp,
340347
348 .block,
349 .block_flat,
350 .block_comptime,
351 .block_comptime_flat,
352 => Block,
353
341 .arg => Arg,354 .arg => Arg,
342 .array_type_sentinel => ArrayTypeSentinel,355 .array_type_sentinel => ArrayTypeSentinel,
343 .block => Block,
344 .@"break" => Break,356 .@"break" => Break,
345 .breakvoid => BreakVoid,357 .breakvoid => BreakVoid,
346 .call => Call,358 .call => Call,
...@@ -392,6 +404,9 @@ pub const Inst = struct {...@@ -392,6 +404,9 @@ pub const Inst = struct {
392 .bitcast_result_ptr,404 .bitcast_result_ptr,
393 .bitor,405 .bitor,
394 .block,406 .block,
407 .block_flat,
408 .block_comptime,
409 .block_comptime_flat,
395 .boolnot,410 .boolnot,
396 .breakpoint,411 .breakpoint,
397 .call,412 .call,
src-self-hosted/zir_sema.zig+55-3
...@@ -31,7 +31,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -31,7 +31,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
31 .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),31 .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
32 .bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),32 .bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
33 .bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),33 .bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
34 .block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?),34 .block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?, false),
35 .block_comptime => return analyzeInstBlock(mod, scope, old_inst.castTag(.block_comptime).?, true),
36 .block_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_flat).?, false),
37 .block_comptime_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_comptime_flat).?, true),
35 .@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?),38 .@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?),
36 .breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),39 .breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),
37 .breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?),40 .breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?),
...@@ -147,6 +150,7 @@ pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {...@@ -147,6 +150,7 @@ pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
147 }150 }
148}151}
149152
153/// TODO improve this to use .block_comptime_flat
150pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {154pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
151 try analyzeBody(mod, &block_scope.base, body);155 try analyzeBody(mod, &block_scope.base, body);
152 for (block_scope.instructions.items) |inst| {156 for (block_scope.instructions.items) |inst| {
...@@ -517,6 +521,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError...@@ -517,6 +521,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
517 .decl = parent_block.decl,521 .decl = parent_block.decl,
518 .instructions = .{},522 .instructions = .{},
519 .arena = parent_block.arena,523 .arena = parent_block.arena,
524 .is_comptime = parent_block.is_comptime,
520 };525 };
521 defer child_block.instructions.deinit(mod.gpa);526 defer child_block.instructions.deinit(mod.gpa);
522527
...@@ -529,7 +534,29 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError...@@ -529,7 +534,29 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
529 return &loop_inst.base;534 return &loop_inst.base;
530}535}
531536
532fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {537fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
538 const parent_block = scope.cast(Scope.Block).?;
539
540 var child_block: Scope.Block = .{
541 .parent = parent_block,
542 .func = parent_block.func,
543 .decl = parent_block.decl,
544 .instructions = .{},
545 .arena = parent_block.arena,
546 .label = null,
547 .is_comptime = parent_block.is_comptime or is_comptime,
548 };
549 defer child_block.instructions.deinit(mod.gpa);
550
551 try analyzeBody(mod, &child_block.base, inst.positionals.body);
552
553 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
554 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
555
556 return copied_instructions[copied_instructions.len - 1];
557}
558
559fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
533 const parent_block = scope.cast(Scope.Block).?;560 const parent_block = scope.cast(Scope.Block).?;
534561
535 // Reserve space for a Block instruction so that generated Break instructions can562 // Reserve space for a Block instruction so that generated Break instructions can
...@@ -557,6 +584,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr...@@ -557,6 +584,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr
557 .results = .{},584 .results = .{},
558 .block_inst = block_inst,585 .block_inst = block_inst,
559 }),586 }),
587 .is_comptime = is_comptime or parent_block.is_comptime,
560 };588 };
561 const label = &child_block.label.?;589 const label = &child_block.label.?;
562590
...@@ -569,6 +597,28 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr...@@ -569,6 +597,28 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr
569 assert(child_block.instructions.items.len != 0);597 assert(child_block.instructions.items.len != 0);
570 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());598 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
571599
600 if (label.results.items.len == 0) {
601 // No need for a block instruction. We can put the new instructions directly into the parent block.
602 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
603 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
604 return copied_instructions[copied_instructions.len - 1];
605 }
606 if (label.results.items.len == 1) {
607 const last_inst_index = child_block.instructions.items.len - 1;
608 const last_inst = child_block.instructions.items[last_inst_index];
609 if (last_inst.breakBlock()) |br_block| {
610 if (br_block == block_inst) {
611 // No need for a block instruction. We can put the new instructions directly into the parent block.
612 // Here we omit the break instruction.
613 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
614 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
615 return label.results.items[0];
616 }
617 }
618 }
619 // It should be impossible to have the number of results be > 1 in a comptime scope.
620 assert(!child_block.is_comptime); // We should have already got a compile error in the condbr condition.
621
572 // Need to set the type and emit the Block instruction. This allows machine code generation622 // Need to set the type and emit the Block instruction. This allows machine code generation
573 // to emit a jump instruction to after the block when it encounters the break.623 // to emit a jump instruction to after the block when it encounters the break.
574 try parent_block.instructions.append(mod.gpa, &block_inst.base);624 try parent_block.instructions.append(mod.gpa, &block_inst.base);
...@@ -1083,7 +1133,7 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne...@@ -1083,7 +1133,7 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
1083 const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);1133 const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
1084 const uncasted_index = try resolveInst(mod, scope, inst.positionals.index);1134 const uncasted_index = try resolveInst(mod, scope, inst.positionals.index);
1085 const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index);1135 const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index);
1086 1136
1087 const elem_ty = switch (array_ptr.ty.zigTypeTag()) {1137 const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
1088 .Pointer => array_ptr.ty.elemType(),1138 .Pointer => array_ptr.ty.elemType(),
1089 else => return mod.fail(scope, inst.positionals.array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),1139 else => return mod.fail(scope, inst.positionals.array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
...@@ -1376,6 +1426,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1376,6 +1426,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1376 .decl = parent_block.decl,1426 .decl = parent_block.decl,
1377 .instructions = .{},1427 .instructions = .{},
1378 .arena = parent_block.arena,1428 .arena = parent_block.arena,
1429 .is_comptime = parent_block.is_comptime,
1379 };1430 };
1380 defer true_block.instructions.deinit(mod.gpa);1431 defer true_block.instructions.deinit(mod.gpa);
1381 try analyzeBody(mod, &true_block.base, inst.positionals.then_body);1432 try analyzeBody(mod, &true_block.base, inst.positionals.then_body);
...@@ -1386,6 +1437,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1386,6 +1437,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1386 .decl = parent_block.decl,1437 .decl = parent_block.decl,
1387 .instructions = .{},1438 .instructions = .{},
1388 .arena = parent_block.arena,1439 .arena = parent_block.arena,
1440 .is_comptime = parent_block.is_comptime,
1389 };1441 };
1390 defer false_block.instructions.deinit(mod.gpa);1442 defer false_block.instructions.deinit(mod.gpa);
1391 try analyzeBody(mod, &false_block.base, inst.positionals.else_body);1443 try analyzeBody(mod, &false_block.base, inst.positionals.else_body);
test/stage2/test.zig+1-1
...@@ -274,7 +274,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -274,7 +274,7 @@ pub fn addCases(ctx: *TestContext) !void {
274 }274 }
275275
276 {276 {
277 var case = ctx.exe("substracting numbers at runtime", linux_x64);277 var case = ctx.exe("subtracting numbers at runtime", linux_x64);
278 case.addCompareOutput(278 case.addCompareOutput(
279 \\export fn _start() noreturn {279 \\export fn _start() noreturn {
280 \\ sub(7, 4);280 \\ sub(7, 4);