stage2: add implicit return void where applicable

This commit is contained in:
Andrew Kelley 2020-07-12 23:50:25 -07:00
parent c306392b44
commit 25b1c00c72
4 changed files with 58 additions and 32 deletions

View File

@ -1210,6 +1210,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
try self.astGenBlock(&gen_scope.base, body_block);
const last_inst = gen_scope.instructions.items[gen_scope.instructions.items.len - 1];
if (!last_inst.tag.isNoReturn()) {
const src = tree.token_locs[body_block.rbrace].start;
_ = try self.addZIRInst(&gen_scope.base, src, zir.Inst.ReturnVoid, .{}, .{});
}
const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
fn_zir.* = .{
.body = .{
@ -2686,7 +2692,7 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
// Blocks must terminate with noreturn instruction.
assert(child_block.instructions.items.len != 0);
assert(child_block.instructions.items[child_block.instructions.items.len - 1].tag.isNoReturn());
assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
// Need to set the type and emit the Block instruction. This allows machine code generation
// to emit a jump instruction to after the block when it encounters the break.
@ -3271,7 +3277,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
defer false_block.instructions.deinit(self.gpa);
try self.analyzeBody(&false_block.base, inst.positionals.false_body);
return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){
return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.noreturn), Inst.CondBr, Inst.Args(Inst.CondBr){
.condition = cond,
.true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },
.false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },

View File

@ -60,36 +60,6 @@ pub const Inst = struct {
retvoid,
sub,
unreach,
/// Returns whether the instruction is one of the control flow "noreturn" types.
/// Function calls do not count. When ZIR is generated, the compiler automatically
/// emits an `Unreach` after a function call with the `noreturn` return type.
pub fn isNoReturn(tag: Tag) bool {
return switch (tag) {
.add,
.arg,
.assembly,
.bitcast,
.block,
.breakpoint,
.call,
.cmp,
.constant,
.isnonnull,
.isnull,
.ptrtoint,
.sub,
=> false,
.br,
.brvoid,
.condbr,
.ret,
.retvoid,
.unreach,
=> true,
};
}
};
pub fn cast(base: *Inst, comptime T: type) ?*T {

View File

@ -468,6 +468,10 @@ pub const Type = extern union {
};
}
pub fn isNoReturn(self: Type) bool {
return self.zigTypeTag() == .NoReturn;
}
/// Asserts that hasCodeGenBits() is true.
pub fn abiAlignment(self: Type, target: Target) u32 {
return switch (self.tag()) {

View File

@ -81,6 +81,52 @@ pub const Inst = struct {
condbr,
isnull,
isnonnull,
/// Returns whether the instruction is one of the control flow "noreturn" types.
/// Function calls do not count.
pub fn isNoReturn(tag: Tag) bool {
return switch (tag) {
.arg,
.block,
.breakpoint,
.call,
.@"const",
.declref,
.declref_str,
.declval,
.declval_in_module,
.str,
.int,
.inttype,
.ptrtoint,
.fieldptr,
.deref,
.as,
.@"asm",
.@"fn",
.fntype,
.@"export",
.primitive,
.intcast,
.bitcast,
.elemptr,
.add,
.sub,
.cmp,
.isnull,
.isnonnull,
=> false,
.condbr,
.@"unreachable",
.@"return",
.returnvoid,
.@"break",
.breakvoid,
.compileerror,
=> true,
};
}
};
pub fn TagToType(tag: Tag) type {