self-hosted: compile errors for return in wrong place

* outside fn definition
 * inside defer expression
This commit is contained in:
Andrew Kelley 2018-07-18 17:40:59 -04:00
parent aa3b41247f
commit bd1c55d2c2
4 changed files with 555 additions and 175 deletions

View File

@ -207,6 +207,8 @@ pub const Compilation = struct {
destroy_handle: promise,
have_err_ret_tracing: bool,
const CompileErrList = std.ArrayList(*errmsg.Msg);
// TODO handle some of these earlier and report them in a way other than error codes
@ -379,6 +381,7 @@ pub const Compilation = struct {
.override_libc = null,
.destroy_handle = undefined,
.have_err_ret_tracing = false,
});
errdefer {
comp.arena_allocator.deinit();
@ -660,7 +663,11 @@ pub const Compilation = struct {
while (it.next()) |decl_ptr| {
const decl = decl_ptr.*;
switch (decl.id) {
ast.Node.Id.Comptime => @panic("TODO"),
ast.Node.Id.Comptime => {
const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
try decl_group.call(addCompTimeBlock, self, parsed_file, &decls.base, comptime_node);
},
ast.Node.Id.VarDecl => @panic("TODO"),
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
@ -709,6 +716,69 @@ pub const Compilation = struct {
}
}
/// caller takes ownership of resulting Code
async fn genAndAnalyzeCode(
comp: *Compilation,
parsed_file: *ParsedFile,
scope: *Scope,
node: *ast.Node,
expected_type: ?*Type,
) !?*ir.Code {
const unanalyzed_code = (await (async ir.gen(
comp,
node,
scope,
parsed_file,
) catch unreachable)) catch |err| switch (err) {
// This poison value should not cause the errdefers to run. It simply means
// that self.compile_errors is populated.
// TODO https://github.com/ziglang/zig/issues/769
error.SemanticAnalysisFailed => return null,
else => return err,
};
defer unanalyzed_code.destroy(comp.gpa());
if (comp.verbose_ir) {
std.debug.warn("unanalyzed:\n");
unanalyzed_code.dump();
}
const analyzed_code = (await (async ir.analyze(
comp,
parsed_file,
unanalyzed_code,
expected_type,
) catch unreachable)) catch |err| switch (err) {
// This poison value should not cause the errdefers to run. It simply means
// that self.compile_errors is populated.
// TODO https://github.com/ziglang/zig/issues/769
error.SemanticAnalysisFailed => return null,
else => return err,
};
errdefer analyzed_code.destroy(comp.gpa());
return analyzed_code;
}
async fn addCompTimeBlock(
comp: *Compilation,
parsed_file: *ParsedFile,
scope: *Scope,
comptime_node: *ast.Node.Comptime,
) !void {
const void_type = Type.Void.get(comp);
defer void_type.base.base.deref(comp);
const analyzed_code = (try await (async genAndAnalyzeCode(
comp,
parsed_file,
scope,
comptime_node.expr,
&void_type.base,
) catch unreachable)) orelse return;
analyzed_code.destroy(comp.gpa());
}
async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
const is_export = decl.isExported(&decl.parsed_file.tree);
@ -931,38 +1001,12 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
const unanalyzed_code = (await (async ir.gen(
comp,
body_node,
const analyzed_code = (try await (async comp.genAndAnalyzeCode(
fn_decl.base.parsed_file,
&fndef_scope.base,
Span.token(body_node.lastToken()),
fn_decl.base.parsed_file,
) catch unreachable)) catch |err| switch (err) {
// This poison value should not cause the errdefers to run. It simply means
// that self.compile_errors is populated.
// TODO https://github.com/ziglang/zig/issues/769
error.SemanticAnalysisFailed => return {},
else => return err,
};
defer unanalyzed_code.destroy(comp.gpa());
if (comp.verbose_ir) {
std.debug.warn("unanalyzed:\n");
unanalyzed_code.dump();
}
const analyzed_code = (await (async ir.analyze(
comp,
fn_decl.base.parsed_file,
unanalyzed_code,
body_node,
null,
) catch unreachable)) catch |err| switch (err) {
// This poison value should not cause the errdefers to run. It simply means
// that self.compile_errors is populated.
// TODO https://github.com/ziglang/zig/issues/769
error.SemanticAnalysisFailed => return {},
else => return err,
};
) catch unreachable)) orelse return;
errdefer analyzed_code.destroy(comp.gpa());
if (comp.verbose_ir) {

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,24 @@ pub const Scope = struct {
}
}
pub fn findDeferExpr(base: *Scope) ?*DeferExpr {
var scope = base;
while (true) {
switch (scope.id) {
Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
Id.FnDef,
Id.Decls,
=> return null,
Id.Block,
Id.Defer,
Id.CompTime,
=> scope = scope.parent orelse return null,
}
}
}
pub const Id = enum {
Decls,
Block,
@ -90,10 +108,10 @@ pub const Scope = struct {
pub const Block = struct {
base: Scope,
incoming_values: std.ArrayList(*ir.Instruction),
incoming_values: std.ArrayList(*ir.Inst),
incoming_blocks: std.ArrayList(*ir.BasicBlock),
end_block: *ir.BasicBlock,
is_comptime: *ir.Instruction,
is_comptime: *ir.Inst,
safety: Safety,
@ -242,6 +260,7 @@ pub const Scope = struct {
pub const DeferExpr = struct {
base: Scope,
expr_node: *ast.Node,
reported_err: bool,
/// Creates a DeferExpr scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {
@ -252,6 +271,7 @@ pub const Scope = struct {
.ref_count = 1,
},
.expr_node = expr_node,
.reported_err = false,
});
errdefer comp.gpa().destroy(self);

View File

@ -9,4 +9,16 @@ pub fn addCases(ctx: *TestContext) !void {
try ctx.testCompileError(
\\fn() void {}
, "1.zig", 1, 1, "missing function name");
try ctx.testCompileError(
\\comptime {
\\ return;
\\}
, "1.zig", 2, 5, "return expression outside function definition");
try ctx.testCompileError(
\\export fn entry() void {
\\ defer return;
\\}
, "1.zig", 2, 11, "cannot return from defer expression");
}