Commit 4c13d020db
Changed files (4)
src-self-hosted
test
stage2
src-self-hosted/Module.zig
@@ -1308,7 +1308,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.return_type = return_type_inst,
.param_types = param_types,
}, .{});
- _ = try astgen.addZIRUnOp(self, &fn_type_scope.base, fn_src, .@"return", fn_type_inst);
// We need the memory for the Type to go into the arena for the Decl
var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
@@ -1325,7 +1324,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
};
defer block_scope.instructions.deinit(self.gpa);
- const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{
+ const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
.instructions = fn_type_scope.instructions.items,
});
const new_func = try decl_arena.allocator.create(Fn);
@@ -1492,35 +1491,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
}
- const explicit_type = blk: {
- const type_node = var_decl.getTypeNode() orelse
- break :blk null;
-
- // Temporary arena for the zir instructions.
- var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer type_scope_arena.deinit();
- var type_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &type_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer type_scope.instructions.deinit(self.gpa);
-
- const src = tree.token_locs[type_node.firstToken()].start;
- const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
- _ = try astgen.addZIRUnOp(self, &type_scope.base, src, .@"return", var_type);
-
- break :blk try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{
- .instructions = type_scope.instructions.items,
- });
- };
-
- var var_type: Type = undefined;
- const value: ?Value = if (var_decl.getInitNode()) |init_node| blk: {
+ const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
defer gen_scope_arena.deinit();
var gen_scope: Scope.GenZIR = .{
@@ -1529,10 +1500,19 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.parent = decl.scope,
};
defer gen_scope.instructions.deinit(self.gpa);
- const src = tree.token_locs[init_node.firstToken()].start;
- const init_inst = try astgen.expr(self, &gen_scope.base, .none, init_node);
- _ = try astgen.addZIRUnOp(self, &gen_scope.base, src, .@"return", init_inst);
+ const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
+ const src = tree.token_locs[type_node.firstToken()].start;
+ const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
+ break :rl .{ .ty = var_type };
+ } else .none;
+
+ const src = tree.token_locs[init_node.firstToken()].start;
+ const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
var inner_block: Scope.Block = .{
.parent = null,
@@ -1545,38 +1525,53 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
defer inner_block.instructions.deinit(self.gpa);
try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
- for (inner_block.instructions.items) |inst| {
- if (inst.castTag(.ret)) |ret| {
- const coerced = if (explicit_type) |some|
- try self.coerce(&inner_block.base, some, ret.operand)
- else
- ret.operand;
- const val = coerced.value() orelse
- return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
-
- var_type = explicit_type orelse try ret.operand.ty.copy(block_scope.arena);
- break :blk try val.copy(block_scope.arena);
- } else {
- return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
- }
- }
- unreachable;
+ // The result location guarantees the type coercion.
+ const analyzed_init_inst = init_inst.analyzed_inst.?;
+ // The is_comptime in the Scope.Block guarantees the result is comptime-known.
+ const val = analyzed_init_inst.value().?;
+
+ const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
+ break :vi .{
+ .ty = ty,
+ .val = try val.copy(block_scope.arena),
+ };
} else if (!is_extern) {
return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
- } else if (explicit_type) |some| blk: {
- var_type = some;
- break :blk null;
+ } else if (var_decl.getTypeNode()) |type_node| vi: {
+ // Temporary arena for the zir instructions.
+ var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer type_scope_arena.deinit();
+ var type_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &type_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer type_scope.instructions.deinit(self.gpa);
+
+ const src = tree.token_locs[type_node.firstToken()].start;
+ const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
+ const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
+ .instructions = type_scope.instructions.items,
+ });
+ break :vi .{
+ .ty = ty,
+ .val = null,
+ };
} else {
return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
};
- if (is_mutable and !var_type.isValidVarType(is_extern)) {
- return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_type});
+ if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
+ return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
}
var type_changed = true;
if (decl.typedValueManaged()) |tvm| {
- type_changed = !tvm.typed_value.ty.eql(var_type);
+ type_changed = !tvm.typed_value.ty.eql(var_info.ty);
tvm.deinit(self.gpa);
}
@@ -1585,7 +1580,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
new_variable.* = .{
.owner_decl = decl,
- .init = value orelse undefined,
+ .init = var_info.val orelse undefined,
.is_extern = is_extern,
.is_mutable = is_mutable,
.is_threadlocal = is_threadlocal,
@@ -1596,7 +1591,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
decl.typed_value = .{
.most_recent = .{
.typed_value = .{
- .ty = var_type,
+ .ty = var_info.ty,
.val = Value.initPayload(&var_payload.base),
},
.arena = decl_arena_state,
@@ -2096,12 +2091,19 @@ pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanage
return gop.entry.*;
}
-/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.
-pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
+pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
return scope.cast(Scope.Block) orelse
return self.fail(scope, src, "instruction illegal outside function body", .{});
}
+pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
+ const block = try self.requireFunctionBlock(scope, src);
+ if (block.is_comptime) {
+ return self.fail(scope, src, "unable to resolve comptime value", .{});
+ }
+ return block;
+}
+
pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
return (try self.resolveDefinedValue(scope, base)) orelse
return self.fail(scope, base.src, "unable to resolve comptime value", .{});
src-self-hosted/test.zig
@@ -474,15 +474,15 @@ pub const TestContext = struct {
var all_errors = try module.getAllErrorsAlloc();
defer all_errors.deinit(allocator);
if (all_errors.list.len != 0) {
- std.debug.warn("\nErrors occurred updating the module:\n================\n", .{});
+ std.debug.print("\nErrors occurred updating the module:\n================\n", .{});
for (all_errors.list) |err| {
- std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
+ std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
}
if (case.cbe) {
const C = module.bin_file.cast(link.File.C).?;
- std.debug.warn("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
+ std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
}
- std.debug.warn("Test failed.\n", .{});
+ std.debug.print("Test failed.\n", .{});
std.process.exit(1);
}
}
@@ -497,12 +497,12 @@ pub const TestContext = struct {
var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
if (expected_output.len != out.len) {
- std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
+ std.debug.print("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
std.process.exit(1);
}
for (expected_output) |e, i| {
if (out[i] != e) {
- std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
+ std.debug.print("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
std.process.exit(1);
}
}
@@ -526,12 +526,12 @@ pub const TestContext = struct {
defer test_node.end();
if (expected_output.len != out_zir.items.len) {
- 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 });
+ 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 });
std.process.exit(1);
}
for (expected_output) |e, i| {
if (out_zir.items[i] != e) {
- 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 });
+ 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 });
std.process.exit(1);
}
}
@@ -554,7 +554,7 @@ pub const TestContext = struct {
break;
}
} else {
- std.debug.warn("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
+ std.debug.print("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
std.process.exit(1);
}
}
@@ -562,7 +562,7 @@ pub const TestContext = struct {
for (handled_errors) |h, i| {
if (!h) {
const er = e[i];
- std.debug.warn("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
+ std.debug.print("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
std.process.exit(1);
}
}
@@ -643,7 +643,7 @@ pub const TestContext = struct {
switch (exec_result.term) {
.Exited => |code| {
if (code != 0) {
- std.debug.warn("elf file exited with code {}\n", .{code});
+ std.debug.print("elf file exited with code {}\n", .{code});
return error.BinaryBadExitCode;
}
},
src-self-hosted/zir_sema.zig
@@ -150,18 +150,16 @@ pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
}
}
-/// TODO improve this to use .block_comptime_flat
-pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
+pub fn analyzeBodyValueAsType(
+ mod: *Module,
+ block_scope: *Scope.Block,
+ zir_result_inst: *zir.Inst,
+ body: zir.Module.Body,
+) !Type {
try analyzeBody(mod, &block_scope.base, body);
- for (block_scope.instructions.items) |inst| {
- if (inst.castTag(.ret)) |ret| {
- const val = try mod.resolveConstValue(&block_scope.base, ret.operand);
- return val.toType(block_scope.base.arena());
- } else {
- return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
- }
- }
- unreachable;
+ const result_inst = zir_result_inst.analyzed_inst.?;
+ const val = try mod.resolveConstValue(&block_scope.base, result_inst);
+ return val.toType(block_scope.base.arena());
}
pub 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!
}
fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
+ const b = try mod.requireFunctionBlock(scope, inst.base.src);
const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
const ret_type = fn_ty.fnReturnType();
return mod.constType(scope, inst.base.src, ret_type);
test/stage2/test.zig
@@ -967,10 +967,19 @@ pub fn addCases(ctx: *TestContext) !void {
\\fn entry() void {}
, &[_][]const u8{":2:4: error: redefinition of 'entry'"});
- ctx.compileError("extern variable has no type", linux_x64,
- \\comptime {
- \\ _ = foo;
- \\}
- \\extern var foo;
- , &[_][]const u8{":4:1: error: unable to infer variable type"});
+ {
+ var case = ctx.obj("extern variable has no type", linux_x64);
+ case.addError(
+ \\comptime {
+ \\ _ = foo;
+ \\}
+ \\extern var foo;
+ , &[_][]const u8{":2:5: error: unable to resolve comptime value"});
+ case.addError(
+ \\export fn entry() void {
+ \\ _ = foo;
+ \\}
+ \\extern var foo;
+ , &[_][]const u8{":4:1: error: unable to infer variable type"});
+ }
}