authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-01 12:39:47-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-01 12:39:47-07:00
log4c13d020dbecbd7664b99765de33f230e98f3322
treea4e9adc1b9ee5ca6a2db85a1e23f74847f75f878
parent717b0e827511b55375de82258f570709c07cc59d

stage2: proper split of requireRuntimeBlock and requireFunctionBlock

* improve the ZIR generated of variable decls - utilize the same ZIR for the type and init value when possible - init value gets a result location with the variable type. no manual coercion is required. * no longer use return instructions to extract values out of comptime blocks. Instead run the analysis and then look at the corresponding analyzed instruction, relying on the comptime mechanism to report errors when something could not be comptime evaluated.

4 files changed, 98 insertions(+), 89 deletions(-)

src-self-hosted/Module.zig+62-60
...@@ -1308,7 +1308,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1308,7 +1308,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1308 .return_type = return_type_inst,1308 .return_type = return_type_inst,
1309 .param_types = param_types,1309 .param_types = param_types,
1310 }, .{});1310 }, .{});
1311 _ = try astgen.addZIRUnOp(self, &fn_type_scope.base, fn_src, .@"return", fn_type_inst);
13121311
1313 // We need the memory for the Type to go into the arena for the Decl1312 // We need the memory for the Type to go into the arena for the Decl
1314 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);1313 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
...@@ -1325,7 +1324,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1325,7 +1324,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1325 };1324 };
1326 defer block_scope.instructions.deinit(self.gpa);1325 defer block_scope.instructions.deinit(self.gpa);
13271326
1328 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{1327 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
1329 .instructions = fn_type_scope.instructions.items,1328 .instructions = fn_type_scope.instructions.items,
1330 });1329 });
1331 const new_func = try decl_arena.allocator.create(Fn);1330 const new_func = try decl_arena.allocator.create(Fn);
...@@ -1492,35 +1491,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1492,35 +1491,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1492 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});1491 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
1493 }1492 }
14941493
1495 const explicit_type = blk: {1494 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
1496 const type_node = var_decl.getTypeNode() orelse
1497 break :blk null;
1498
1499 // Temporary arena for the zir instructions.
1500 var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1501 defer type_scope_arena.deinit();
1502 var type_scope: Scope.GenZIR = .{
1503 .decl = decl,
1504 .arena = &type_scope_arena.allocator,
1505 .parent = decl.scope,
1506 };
1507 defer type_scope.instructions.deinit(self.gpa);
1508
1509 const src = tree.token_locs[type_node.firstToken()].start;
1510 const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
1511 .ty = Type.initTag(.type),
1512 .val = Value.initTag(.type_type),
1513 });
1514 const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
1515 _ = try astgen.addZIRUnOp(self, &type_scope.base, src, .@"return", var_type);
1516
1517 break :blk try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{
1518 .instructions = type_scope.instructions.items,
1519 });
1520 };
1521
1522 var var_type: Type = undefined;
1523 const value: ?Value = if (var_decl.getInitNode()) |init_node| blk: {
1524 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);1495 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1525 defer gen_scope_arena.deinit();1496 defer gen_scope_arena.deinit();
1526 var gen_scope: Scope.GenZIR = .{1497 var gen_scope: Scope.GenZIR = .{
...@@ -1529,10 +1500,19 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1529,10 +1500,19 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1529 .parent = decl.scope,1500 .parent = decl.scope,
1530 };1501 };
1531 defer gen_scope.instructions.deinit(self.gpa);1502 defer gen_scope.instructions.deinit(self.gpa);
1532 const src = tree.token_locs[init_node.firstToken()].start;
15331503
1534 const init_inst = try astgen.expr(self, &gen_scope.base, .none, init_node);1504 const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
1535 _ = try astgen.addZIRUnOp(self, &gen_scope.base, src, .@"return", init_inst);1505 const src = tree.token_locs[type_node.firstToken()].start;
1506 const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
1507 .ty = Type.initTag(.type),
1508 .val = Value.initTag(.type_type),
1509 });
1510 const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
1511 break :rl .{ .ty = var_type };
1512 } else .none;
1513
1514 const src = tree.token_locs[init_node.firstToken()].start;
1515 const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
15361516
1537 var inner_block: Scope.Block = .{1517 var inner_block: Scope.Block = .{
1538 .parent = null,1518 .parent = null,
...@@ -1545,38 +1525,53 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1545,38 +1525,53 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1545 defer inner_block.instructions.deinit(self.gpa);1525 defer inner_block.instructions.deinit(self.gpa);
1546 try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });1526 try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
15471527
1548 for (inner_block.instructions.items) |inst| {1528 // The result location guarantees the type coercion.
1549 if (inst.castTag(.ret)) |ret| {1529 const analyzed_init_inst = init_inst.analyzed_inst.?;
1550 const coerced = if (explicit_type) |some|1530 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
1551 try self.coerce(&inner_block.base, some, ret.operand)1531 const val = analyzed_init_inst.value().?;
1552 else1532
1553 ret.operand;1533 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
1554 const val = coerced.value() orelse1534 break :vi .{
1555 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});1535 .ty = ty,
15561536 .val = try val.copy(block_scope.arena),
1557 var_type = explicit_type orelse try ret.operand.ty.copy(block_scope.arena);1537 };
1558 break :blk try val.copy(block_scope.arena);
1559 } else {
1560 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
1561 }
1562 }
1563 unreachable;
1564 } else if (!is_extern) {1538 } else if (!is_extern) {
1565 return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});1539 return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
1566 } else if (explicit_type) |some| blk: {1540 } else if (var_decl.getTypeNode()) |type_node| vi: {
1567 var_type = some;1541 // Temporary arena for the zir instructions.
1568 break :blk null;1542 var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1543 defer type_scope_arena.deinit();
1544 var type_scope: Scope.GenZIR = .{
1545 .decl = decl,
1546 .arena = &type_scope_arena.allocator,
1547 .parent = decl.scope,
1548 };
1549 defer type_scope.instructions.deinit(self.gpa);
1550
1551 const src = tree.token_locs[type_node.firstToken()].start;
1552 const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
1553 .ty = Type.initTag(.type),
1554 .val = Value.initTag(.type_type),
1555 });
1556 const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
1557 const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
1558 .instructions = type_scope.instructions.items,
1559 });
1560 break :vi .{
1561 .ty = ty,
1562 .val = null,
1563 };
1569 } else {1564 } else {
1570 return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});1565 return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
1571 };1566 };
15721567
1573 if (is_mutable and !var_type.isValidVarType(is_extern)) {1568 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
1574 return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_type});1569 return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
1575 }1570 }
15761571
1577 var type_changed = true;1572 var type_changed = true;
1578 if (decl.typedValueManaged()) |tvm| {1573 if (decl.typedValueManaged()) |tvm| {
1579 type_changed = !tvm.typed_value.ty.eql(var_type);1574 type_changed = !tvm.typed_value.ty.eql(var_info.ty);
15801575
1581 tvm.deinit(self.gpa);1576 tvm.deinit(self.gpa);
1582 }1577 }
...@@ -1585,7 +1580,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1585,7 +1580,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1585 const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);1580 const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
1586 new_variable.* = .{1581 new_variable.* = .{
1587 .owner_decl = decl,1582 .owner_decl = decl,
1588 .init = value orelse undefined,1583 .init = var_info.val orelse undefined,
1589 .is_extern = is_extern,1584 .is_extern = is_extern,
1590 .is_mutable = is_mutable,1585 .is_mutable = is_mutable,
1591 .is_threadlocal = is_threadlocal,1586 .is_threadlocal = is_threadlocal,
...@@ -1596,7 +1591,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1596,7 +1591,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1596 decl.typed_value = .{1591 decl.typed_value = .{
1597 .most_recent = .{1592 .most_recent = .{
1598 .typed_value = .{1593 .typed_value = .{
1599 .ty = var_type,1594 .ty = var_info.ty,
1600 .val = Value.initPayload(&var_payload.base),1595 .val = Value.initPayload(&var_payload.base),
1601 },1596 },
1602 .arena = decl_arena_state,1597 .arena = decl_arena_state,
...@@ -2096,12 +2091,19 @@ pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanage...@@ -2096,12 +2091,19 @@ pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanage
2096 return gop.entry.*;2091 return gop.entry.*;
2097}2092}
20982093
2099/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.2094pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
2100pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
2101 return scope.cast(Scope.Block) orelse2095 return scope.cast(Scope.Block) orelse
2102 return self.fail(scope, src, "instruction illegal outside function body", .{});2096 return self.fail(scope, src, "instruction illegal outside function body", .{});
2103}2097}
21042098
2099pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
2100 const block = try self.requireFunctionBlock(scope, src);
2101 if (block.is_comptime) {
2102 return self.fail(scope, src, "unable to resolve comptime value", .{});
2103 }
2104 return block;
2105}
2106
2105pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {2107pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
2106 return (try self.resolveDefinedValue(scope, base)) orelse2108 return (try self.resolveDefinedValue(scope, base)) orelse
2107 return self.fail(scope, base.src, "unable to resolve comptime value", .{});2109 return self.fail(scope, base.src, "unable to resolve comptime value", .{});
src-self-hosted/test.zig+11-11
...@@ -474,15 +474,15 @@ pub const TestContext = struct {...@@ -474,15 +474,15 @@ pub const TestContext = struct {
474 var all_errors = try module.getAllErrorsAlloc();474 var all_errors = try module.getAllErrorsAlloc();
475 defer all_errors.deinit(allocator);475 defer all_errors.deinit(allocator);
476 if (all_errors.list.len != 0) {476 if (all_errors.list.len != 0) {
477 std.debug.warn("\nErrors occurred updating the module:\n================\n", .{});477 std.debug.print("\nErrors occurred updating the module:\n================\n", .{});
478 for (all_errors.list) |err| {478 for (all_errors.list) |err| {
479 std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });479 std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
480 }480 }
481 if (case.cbe) {481 if (case.cbe) {
482 const C = module.bin_file.cast(link.File.C).?;482 const C = module.bin_file.cast(link.File.C).?;
483 std.debug.warn("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});483 std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
484 }484 }
485 std.debug.warn("Test failed.\n", .{});485 std.debug.print("Test failed.\n", .{});
486 std.process.exit(1);486 std.process.exit(1);
487 }487 }
488 }488 }
...@@ -497,12 +497,12 @@ pub const TestContext = struct {...@@ -497,12 +497,12 @@ pub const TestContext = struct {
497 var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");497 var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
498498
499 if (expected_output.len != out.len) {499 if (expected_output.len != out.len) {
500 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });500 std.debug.print("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
501 std.process.exit(1);501 std.process.exit(1);
502 }502 }
503 for (expected_output) |e, i| {503 for (expected_output) |e, i| {
504 if (out[i] != e) {504 if (out[i] != e) {
505 std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });505 std.debug.print("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
506 std.process.exit(1);506 std.process.exit(1);
507 }507 }
508 }508 }
...@@ -526,12 +526,12 @@ pub const TestContext = struct {...@@ -526,12 +526,12 @@ pub const TestContext = struct {
526 defer test_node.end();526 defer test_node.end();
527527
528 if (expected_output.len != out_zir.items.len) {528 if (expected_output.len != out_zir.items.len) {
529 std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });529 std.debug.print("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
530 std.process.exit(1);530 std.process.exit(1);
531 }531 }
532 for (expected_output) |e, i| {532 for (expected_output) |e, i| {
533 if (out_zir.items[i] != e) {533 if (out_zir.items[i] != e) {
534 std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });534 std.debug.print("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
535 std.process.exit(1);535 std.process.exit(1);
536 }536 }
537 }537 }
...@@ -554,7 +554,7 @@ pub const TestContext = struct {...@@ -554,7 +554,7 @@ pub const TestContext = struct {
554 break;554 break;
555 }555 }
556 } else {556 } else {
557 std.debug.warn("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });557 std.debug.print("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
558 std.process.exit(1);558 std.process.exit(1);
559 }559 }
560 }560 }
...@@ -562,7 +562,7 @@ pub const TestContext = struct {...@@ -562,7 +562,7 @@ pub const TestContext = struct {
562 for (handled_errors) |h, i| {562 for (handled_errors) |h, i| {
563 if (!h) {563 if (!h) {
564 const er = e[i];564 const er = e[i];
565 std.debug.warn("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });565 std.debug.print("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
566 std.process.exit(1);566 std.process.exit(1);
567 }567 }
568 }568 }
...@@ -643,7 +643,7 @@ pub const TestContext = struct {...@@ -643,7 +643,7 @@ pub const TestContext = struct {
643 switch (exec_result.term) {643 switch (exec_result.term) {
644 .Exited => |code| {644 .Exited => |code| {
645 if (code != 0) {645 if (code != 0) {
646 std.debug.warn("elf file exited with code {}\n", .{code});646 std.debug.print("elf file exited with code {}\n", .{code});
647 return error.BinaryBadExitCode;647 return error.BinaryBadExitCode;
648 }648 }
649 },649 },
src-self-hosted/zir_sema.zig+10-12
...@@ -150,18 +150,16 @@ pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {...@@ -150,18 +150,16 @@ pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
150 }150 }
151}151}
152152
153/// TODO improve this to use .block_comptime_flat153pub fn analyzeBodyValueAsType(
154pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {154 mod: *Module,
155 block_scope: *Scope.Block,
156 zir_result_inst: *zir.Inst,
157 body: zir.Module.Body,
158) !Type {
155 try analyzeBody(mod, &block_scope.base, body);159 try analyzeBody(mod, &block_scope.base, body);
156 for (block_scope.instructions.items) |inst| {160 const result_inst = zir_result_inst.analyzed_inst.?;
157 if (inst.castTag(.ret)) |ret| {161 const val = try mod.resolveConstValue(&block_scope.base, result_inst);
158 const val = try mod.resolveConstValue(&block_scope.base, ret.operand);162 return val.toType(block_scope.base.arena());
159 return val.toType(block_scope.base.arena());
160 } else {
161 return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
162 }
163 }
164 unreachable;
165}163}
166164
167pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {165pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {
...@@ -366,7 +364,7 @@ fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!...@@ -366,7 +364,7 @@ fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
366}364}
367365
368fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {366fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
369 const b = try mod.requireRuntimeBlock(scope, inst.base.src);367 const b = try mod.requireFunctionBlock(scope, inst.base.src);
370 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;368 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
371 const ret_type = fn_ty.fnReturnType();369 const ret_type = fn_ty.fnReturnType();
372 return mod.constType(scope, inst.base.src, ret_type);370 return mod.constType(scope, inst.base.src, ret_type);
test/stage2/test.zig+15-6
...@@ -967,10 +967,19 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -967,10 +967,19 @@ pub fn addCases(ctx: *TestContext) !void {
967 \\fn entry() void {}967 \\fn entry() void {}
968 , &[_][]const u8{":2:4: error: redefinition of 'entry'"});968 , &[_][]const u8{":2:4: error: redefinition of 'entry'"});
969969
970 ctx.compileError("extern variable has no type", linux_x64,970 {
971 \\comptime {971 var case = ctx.obj("extern variable has no type", linux_x64);
972 \\ _ = foo;972 case.addError(
973 \\}973 \\comptime {
974 \\extern var foo;974 \\ _ = foo;
975 , &[_][]const u8{":4:1: error: unable to infer variable type"});975 \\}
976 \\extern var foo;
977 , &[_][]const u8{":2:5: error: unable to resolve comptime value"});
978 case.addError(
979 \\export fn entry() void {
980 \\ _ = foo;
981 \\}
982 \\extern var foo;
983 , &[_][]const u8{":4:1: error: unable to infer variable type"});
984 }
976}985}