AstGen: basic defer implementation

This commit is contained in:
Andrew Kelley 2021-04-20 16:32:10 -07:00
parent b4aec0e31d
commit a59bcae59f
4 changed files with 546 additions and 349 deletions

View File

@ -1,3 +1,7 @@
* defer
- `break`
- `continue`
* nested function decl: how to refer to params?
* look for cached zir code
* save zir code to cache
* keep track of file dependencies/dependants
@ -13,6 +17,15 @@
on each usingnamespace decl
* handle usingnamespace cycles
* compile error for return inside defer expression
* when block has noreturn statement
- avoid emitting defers
- compile error for unreachable code
* detect `return error.Foo` and emit ZIR that unconditionally generates errdefers
* `return`: check return operand and generate errdefers if necessary
* have failed_trees and just put the file in there
- this way we can emit all the parse errors not just the first one
- but maybe we want just the first one?

File diff suppressed because it is too large Load Diff

View File

@ -514,31 +514,26 @@ pub const Scope = struct {
pub const NameHash = [16]u8;
pub fn cast(base: *Scope, comptime T: type) ?*T {
if (T == Defer) {
switch (base.tag) {
.defer_normal, .defer_error => return @fieldParentPtr(T, "base", base),
else => return null,
}
}
if (base.tag != T.base_tag)
return null;
return @fieldParentPtr(T, "base", base);
}
/// Returns the arena Allocator associated with the Decl of the Scope.
pub fn arena(scope: *Scope) *Allocator {
switch (scope.tag) {
.block => return scope.cast(Block).?.sema.arena,
.gen_zir => return scope.cast(GenZir).?.astgen.arena,
.local_val => return scope.cast(LocalVal).?.gen_zir.astgen.arena,
.local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.arena,
.file => unreachable,
.namespace => unreachable,
.decl_ref => unreachable,
}
}
pub fn ownerDecl(scope: *Scope) ?*Decl {
return switch (scope.tag) {
.block => scope.cast(Block).?.sema.owner_decl,
.gen_zir => unreachable,
.local_val => unreachable,
.local_ptr => unreachable,
.defer_normal => unreachable,
.defer_error => unreachable,
.file => null,
.namespace => null,
.decl_ref => scope.cast(DeclRef).?.decl,
@ -551,6 +546,8 @@ pub const Scope = struct {
.gen_zir => unreachable,
.local_val => unreachable,
.local_ptr => unreachable,
.defer_normal => unreachable,
.defer_error => unreachable,
.file => null,
.namespace => null,
.decl_ref => scope.cast(DeclRef).?.decl,
@ -564,25 +561,14 @@ pub const Scope = struct {
.gen_zir => unreachable,
.local_val => unreachable,
.local_ptr => unreachable,
.defer_normal => unreachable,
.defer_error => unreachable,
.file => return scope.cast(File).?.namespace,
.namespace => return scope.cast(Namespace).?,
.decl_ref => return scope.cast(DeclRef).?.decl.namespace,
}
}
/// Asserts the scope is a child of a `GenZir` and returns it.
pub fn getGenZir(scope: *Scope) *GenZir {
return switch (scope.tag) {
.block => unreachable,
.gen_zir => scope.cast(GenZir).?,
.local_val => return scope.cast(LocalVal).?.gen_zir,
.local_ptr => return scope.cast(LocalPtr).?.gen_zir,
.file => unreachable,
.namespace => unreachable,
.decl_ref => unreachable,
};
}
/// Asserts the scope has a parent which is a Namespace or File and
/// returns the sub_file_path field.
pub fn subFilePath(base: *Scope) []const u8 {
@ -593,6 +579,8 @@ pub const Scope = struct {
.gen_zir => unreachable,
.local_val => unreachable,
.local_ptr => unreachable,
.defer_normal => unreachable,
.defer_error => unreachable,
.decl_ref => unreachable,
}
}
@ -604,9 +592,11 @@ pub const Scope = struct {
cur = switch (cur.tag) {
.namespace => return @fieldParentPtr(Namespace, "base", cur).file_scope,
.file => return @fieldParentPtr(File, "base", cur),
.gen_zir => @fieldParentPtr(GenZir, "base", cur).parent,
.local_val => @fieldParentPtr(LocalVal, "base", cur).parent,
.local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent,
.gen_zir => return @fieldParentPtr(GenZir, "base", cur).astgen.file,
.local_val => return @fieldParentPtr(LocalVal, "base", cur).gen_zir.astgen.file,
.local_ptr => return @fieldParentPtr(LocalPtr, "base", cur).gen_zir.astgen.file,
.defer_normal => @fieldParentPtr(Defer, "base", cur).parent,
.defer_error => @fieldParentPtr(Defer, "base", cur).parent,
.block => return @fieldParentPtr(Block, "base", cur).src_decl.namespace.file_scope,
.decl_ref => return @fieldParentPtr(DeclRef, "base", cur).decl.namespace.file_scope,
};
@ -634,6 +624,8 @@ pub const Scope = struct {
/// `Decl` for use with `srcDecl` and `ownerDecl`.
/// Has no parents or children.
decl_ref,
defer_normal,
defer_error,
};
/// The container that structs, enums, unions, and opaques have.
@ -1709,7 +1701,7 @@ pub const Scope = struct {
pub const LocalVal = struct {
pub const base_tag: Tag = .local_val;
base: Scope = Scope{ .tag = base_tag },
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`.
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`.
parent: *Scope,
gen_zir: *GenZir,
name: []const u8,
@ -1724,7 +1716,7 @@ pub const Scope = struct {
pub const LocalPtr = struct {
pub const base_tag: Tag = .local_ptr;
base: Scope = Scope{ .tag = base_tag },
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`.
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`.
parent: *Scope,
gen_zir: *GenZir,
name: []const u8,
@ -1733,6 +1725,13 @@ pub const Scope = struct {
token_src: ast.TokenIndex,
};
pub const Defer = struct {
base: Scope,
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`.
parent: *Scope,
defer_node: ast.Node.Index,
};
pub const DeclRef = struct {
pub const base_tag: Tag = .decl_ref;
base: Scope = Scope{ .tag = base_tag },
@ -3821,7 +3820,7 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In
}
mod.failed_decls.putAssumeCapacityNoClobber(block.sema.owner_decl, err_msg);
},
.gen_zir, .local_val, .local_ptr => unreachable,
.gen_zir, .local_val, .local_ptr, .defer_normal, .defer_error => unreachable,
.file => unreachable,
.namespace => unreachable,
.decl_ref => {

View File

@ -2304,6 +2304,7 @@ const Writer = struct {
.byte_swap,
.bit_reverse,
.elem_type,
.bitcast_result_ptr,
=> try self.writeUnNode(stream, inst),
.ref,
@ -2424,6 +2425,7 @@ const Writer = struct {
.splat,
.reduce,
.atomic_load,
.bitcast,
=> try self.writePlNodeBin(stream, inst),
.call,
@ -2509,13 +2511,40 @@ const Writer = struct {
.switch_capture_else_ref,
=> try self.writeSwitchCapture(stream, inst),
.bitcast,
.bitcast_result_ptr,
.extended,
=> try stream.writeAll("TODO)"),
.extended => try self.writeExtended(stream, inst),
}
}
fn writeExtended(self: *Writer, stream: anytype, inst: Inst.Index) !void {
const extended = self.code.instructions.items(.data)[inst].extended;
try stream.print("{s}(", .{@tagName(extended.opcode)});
switch (extended.opcode) {
.ret_ptr,
.ret_type,
.this,
.ret_addr,
.error_return_trace,
.frame,
.frame_address,
.builtin_src,
=> try self.writeExtNode(stream, extended),
.func,
.c_undef,
.c_include,
.c_define,
.wasm_memory_size,
.wasm_memory_grow,
=> try stream.writeAll("TODO))"),
}
}
fn writeExtNode(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void {
const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
try stream.writeAll(")) ");
try self.writeSrc(stream, src);
}
fn writeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].bin;
try self.writeInstRef(stream, inst_data.lhs);