Merge pull request #9325 from ziglang/stage2-inferred-error-sets
Stage2 inferred error sets and `@panic`
This commit is contained in:
@@ -677,6 +677,13 @@ pub const panic: PanicFn = if (@hasDecl(root, "panic")) root.panic else default_
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn {
|
||||
@setCold(true);
|
||||
// Until self-hosted catches up with stage1 language features, we have a simpler
|
||||
// default panic function:
|
||||
if (builtin.zig_is_stage2) {
|
||||
while (true) {
|
||||
@breakpoint();
|
||||
}
|
||||
}
|
||||
if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) {
|
||||
root.os.panic(msg, error_return_trace);
|
||||
unreachable;
|
||||
|
||||
@@ -483,10 +483,20 @@ pub fn HashMap(
|
||||
return self.unmanaged.getOrPutValueContext(self.allocator, key, value, self.ctx);
|
||||
}
|
||||
|
||||
/// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`.
|
||||
pub const ensureCapacity = ensureTotalCapacity;
|
||||
|
||||
/// Increases capacity, guaranteeing that insertions up until the
|
||||
/// `expected_count` will not cause an allocation, and therefore cannot fail.
|
||||
pub fn ensureCapacity(self: *Self, expected_count: Size) !void {
|
||||
return self.unmanaged.ensureCapacityContext(self.allocator, expected_count, self.ctx);
|
||||
pub fn ensureTotalCapacity(self: *Self, expected_count: Size) !void {
|
||||
return self.unmanaged.ensureTotalCapacityContext(self.allocator, expected_count, self.ctx);
|
||||
}
|
||||
|
||||
/// Increases capacity, guaranteeing that insertions up until
|
||||
/// `additional_count` **more** items will not cause an allocation, and
|
||||
/// therefore cannot fail.
|
||||
pub fn ensureUnusedCapacity(self: *Self, additional_count: Size) !void {
|
||||
return self.unmanaged.ensureUnusedCapacityContext(self.allocator, additional_count, self.ctx);
|
||||
}
|
||||
|
||||
/// Returns the number of total elements which may be present before it is
|
||||
@@ -821,16 +831,26 @@ pub fn HashMapUnmanaged(
|
||||
return new_cap;
|
||||
}
|
||||
|
||||
pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_size: Size) !void {
|
||||
/// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`.
|
||||
pub const ensureCapacity = ensureTotalCapacity;
|
||||
|
||||
pub fn ensureTotalCapacity(self: *Self, allocator: *Allocator, new_size: Size) !void {
|
||||
if (@sizeOf(Context) != 0)
|
||||
@compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureCapacityContext instead.");
|
||||
return ensureCapacityContext(self, allocator, new_size, undefined);
|
||||
@compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureTotalCapacityContext instead.");
|
||||
return ensureTotalCapacityContext(self, allocator, new_size, undefined);
|
||||
}
|
||||
pub fn ensureCapacityContext(self: *Self, allocator: *Allocator, new_size: Size, ctx: Context) !void {
|
||||
pub fn ensureTotalCapacityContext(self: *Self, allocator: *Allocator, new_size: Size, ctx: Context) !void {
|
||||
if (new_size > self.size)
|
||||
try self.growIfNeeded(allocator, new_size - self.size, ctx);
|
||||
}
|
||||
|
||||
pub fn ensureUnusedCapacity(self: *Self, allocator: *Allocator, additional_size: Size) !void {
|
||||
return ensureUnusedCapacityContext(self, allocator, additional_size, undefined);
|
||||
}
|
||||
pub fn ensureUnusedCapacityContext(self: *Self, allocator: *Allocator, additional_size: Size, ctx: Context) !void {
|
||||
return ensureTotalCapacityContext(self, allocator, self.capacity() + additional_size, ctx);
|
||||
}
|
||||
|
||||
pub fn clearRetainingCapacity(self: *Self) void {
|
||||
if (self.metadata) |_| {
|
||||
self.initMetadatas();
|
||||
|
||||
221
src/AstGen.zig
221
src/AstGen.zig
@@ -786,7 +786,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
|
||||
rl,
|
||||
node,
|
||||
node_datas[node].lhs,
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.err_union_payload_unsafe_ptr,
|
||||
.err_union_code_ptr,
|
||||
node_datas[node].rhs,
|
||||
@@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
|
||||
rl,
|
||||
node,
|
||||
node_datas[node].lhs,
|
||||
.is_err,
|
||||
.is_non_err,
|
||||
.err_union_payload_unsafe,
|
||||
.err_union_code,
|
||||
node_datas[node].rhs,
|
||||
@@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
|
||||
rl,
|
||||
node,
|
||||
node_datas[node].lhs,
|
||||
.is_null_ptr,
|
||||
.is_non_null_ptr,
|
||||
.optional_payload_unsafe_ptr,
|
||||
undefined,
|
||||
node_datas[node].rhs,
|
||||
@@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
|
||||
rl,
|
||||
node,
|
||||
node_datas[node].lhs,
|
||||
.is_null,
|
||||
.is_non_null,
|
||||
.optional_payload_unsafe,
|
||||
undefined,
|
||||
node_datas[node].rhs,
|
||||
@@ -1860,7 +1860,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod
|
||||
}
|
||||
}
|
||||
|
||||
try genDefers(gz, parent_scope, scope, .none);
|
||||
try genDefers(gz, parent_scope, scope, .normal_only);
|
||||
try checkUsed(gz, parent_scope, scope);
|
||||
}
|
||||
|
||||
@@ -1948,11 +1948,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
|
||||
.float128,
|
||||
.int_type,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err,
|
||||
.is_non_err_ptr,
|
||||
.mod_rem,
|
||||
.mul,
|
||||
.mulwrap,
|
||||
@@ -2102,6 +2100,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
|
||||
.@"resume",
|
||||
.@"await",
|
||||
.await_nosuspend,
|
||||
.ret_err_value_code,
|
||||
.extended,
|
||||
=> break :b false,
|
||||
|
||||
@@ -2113,6 +2112,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
|
||||
.compile_error,
|
||||
.ret_node,
|
||||
.ret_coerce,
|
||||
.ret_err_value,
|
||||
.@"unreachable",
|
||||
.repeat,
|
||||
.repeat_inline,
|
||||
@@ -2162,13 +2162,63 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
|
||||
return noreturn_src_node;
|
||||
}
|
||||
|
||||
fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct {
|
||||
have_any: bool,
|
||||
have_normal: bool,
|
||||
have_err: bool,
|
||||
need_err_code: bool,
|
||||
} {
|
||||
const tree = astgen.tree;
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
|
||||
var have_normal = false;
|
||||
var have_err = false;
|
||||
var need_err_code = false;
|
||||
var scope = inner_scope;
|
||||
while (scope != outer_scope) {
|
||||
switch (scope.tag) {
|
||||
.gen_zir => scope = scope.cast(GenZir).?.parent,
|
||||
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
|
||||
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
|
||||
.defer_normal => {
|
||||
const defer_scope = scope.cast(Scope.Defer).?;
|
||||
scope = defer_scope.parent;
|
||||
|
||||
have_normal = true;
|
||||
},
|
||||
.defer_error => {
|
||||
const defer_scope = scope.cast(Scope.Defer).?;
|
||||
scope = defer_scope.parent;
|
||||
|
||||
have_err = true;
|
||||
|
||||
const have_err_payload = node_datas[defer_scope.defer_node].lhs != 0;
|
||||
need_err_code = need_err_code or have_err_payload;
|
||||
},
|
||||
.namespace => unreachable,
|
||||
.top => unreachable,
|
||||
}
|
||||
}
|
||||
return .{
|
||||
.have_any = have_normal or have_err,
|
||||
.have_normal = have_normal,
|
||||
.have_err = have_err,
|
||||
.need_err_code = need_err_code,
|
||||
};
|
||||
}
|
||||
|
||||
const DefersToEmit = union(enum) {
|
||||
both: Zir.Inst.Ref, // err code
|
||||
both_sans_err,
|
||||
normal_only,
|
||||
};
|
||||
|
||||
fn genDefers(
|
||||
gz: *GenZir,
|
||||
outer_scope: *Scope,
|
||||
inner_scope: *Scope,
|
||||
err_code: Zir.Inst.Ref,
|
||||
which_ones: DefersToEmit,
|
||||
) InnerError!void {
|
||||
_ = err_code;
|
||||
const astgen = gz.astgen;
|
||||
const tree = astgen.tree;
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
@@ -2191,12 +2241,37 @@ fn genDefers(
|
||||
.defer_error => {
|
||||
const defer_scope = scope.cast(Scope.Defer).?;
|
||||
scope = defer_scope.parent;
|
||||
if (err_code == .none) continue;
|
||||
const expr_node = node_datas[defer_scope.defer_node].rhs;
|
||||
const prev_in_defer = gz.in_defer;
|
||||
gz.in_defer = true;
|
||||
defer gz.in_defer = prev_in_defer;
|
||||
_ = try unusedResultExpr(gz, defer_scope.parent, expr_node);
|
||||
switch (which_ones) {
|
||||
.both_sans_err => {
|
||||
const expr_node = node_datas[defer_scope.defer_node].rhs;
|
||||
const prev_in_defer = gz.in_defer;
|
||||
gz.in_defer = true;
|
||||
defer gz.in_defer = prev_in_defer;
|
||||
_ = try unusedResultExpr(gz, defer_scope.parent, expr_node);
|
||||
},
|
||||
.both => |err_code| {
|
||||
const expr_node = node_datas[defer_scope.defer_node].rhs;
|
||||
const payload_token = node_datas[defer_scope.defer_node].lhs;
|
||||
const prev_in_defer = gz.in_defer;
|
||||
gz.in_defer = true;
|
||||
defer gz.in_defer = prev_in_defer;
|
||||
var local_val_scope: Scope.LocalVal = undefined;
|
||||
const sub_scope = if (payload_token == 0) defer_scope.parent else blk: {
|
||||
const ident_name = try astgen.identAsString(payload_token);
|
||||
local_val_scope = .{
|
||||
.parent = defer_scope.parent,
|
||||
.gen_zir = gz,
|
||||
.name = ident_name,
|
||||
.inst = err_code,
|
||||
.token_src = payload_token,
|
||||
.id_cat = .@"capture",
|
||||
};
|
||||
break :blk &local_val_scope.base;
|
||||
};
|
||||
_ = try unusedResultExpr(gz, sub_scope, expr_node);
|
||||
},
|
||||
.normal_only => continue,
|
||||
}
|
||||
},
|
||||
.namespace => unreachable,
|
||||
.top => unreachable,
|
||||
@@ -4544,8 +4619,8 @@ fn tryExpr(
|
||||
};
|
||||
const err_ops = switch (rl) {
|
||||
// zig fmt: off
|
||||
.ref => [3]Zir.Inst.Tag{ .is_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },
|
||||
else => [3]Zir.Inst.Tag{ .is_err, .err_union_code, .err_union_payload_unsafe },
|
||||
.ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },
|
||||
else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe },
|
||||
// zig fmt: on
|
||||
};
|
||||
// This could be a pointer or value depending on the `operand_rl` parameter.
|
||||
@@ -4563,20 +4638,20 @@ fn tryExpr(
|
||||
var then_scope = parent_gz.makeSubBlock(scope);
|
||||
defer then_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
const err_code = try then_scope.addUnNode(err_ops[1], operand, node);
|
||||
try genDefers(&then_scope, &fn_block.base, scope, err_code);
|
||||
const then_result = try then_scope.addUnNode(.ret_node, err_code, node);
|
||||
block_scope.break_count += 1;
|
||||
// This could be a pointer or value depending on `err_ops[2]`.
|
||||
const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node);
|
||||
const then_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
|
||||
var else_scope = parent_gz.makeSubBlock(scope);
|
||||
defer else_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
block_scope.break_count += 1;
|
||||
// This could be a pointer or value depending on `err_ops[2]`.
|
||||
const unwrapped_payload = try else_scope.addUnNode(err_ops[2], operand, node);
|
||||
const else_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
const err_code = try else_scope.addUnNode(err_ops[1], operand, node);
|
||||
try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
|
||||
const else_result = try else_scope.addUnNode(.ret_node, err_code, node);
|
||||
|
||||
return finishThenElseBlock(
|
||||
parent_gz,
|
||||
@@ -4634,18 +4709,28 @@ fn orelseCatchExpr(
|
||||
var then_scope = parent_gz.makeSubBlock(scope);
|
||||
defer then_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
// This could be a pointer or value depending on `unwrap_op`.
|
||||
const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
|
||||
const then_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
|
||||
var else_scope = parent_gz.makeSubBlock(scope);
|
||||
defer else_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
var err_val_scope: Scope.LocalVal = undefined;
|
||||
const then_sub_scope = blk: {
|
||||
const payload = payload_token orelse break :blk &then_scope.base;
|
||||
const else_sub_scope = blk: {
|
||||
const payload = payload_token orelse break :blk &else_scope.base;
|
||||
if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
|
||||
return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
|
||||
}
|
||||
const err_name = try astgen.identAsString(payload);
|
||||
err_val_scope = .{
|
||||
.parent = &then_scope.base,
|
||||
.gen_zir = &then_scope,
|
||||
.parent = &else_scope.base,
|
||||
.gen_zir = &else_scope,
|
||||
.name = err_name,
|
||||
.inst = try then_scope.addUnNode(unwrap_code_op, operand, node),
|
||||
.inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
|
||||
.token_src = payload,
|
||||
.id_cat = .@"capture",
|
||||
};
|
||||
@@ -4653,23 +4738,13 @@ fn orelseCatchExpr(
|
||||
};
|
||||
|
||||
block_scope.break_count += 1;
|
||||
const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs);
|
||||
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
|
||||
const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);
|
||||
try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
|
||||
|
||||
// We hold off on the break instructions as well as copying the then/else
|
||||
// instructions into place until we know whether to keep store_to_block_ptr
|
||||
// instructions or not.
|
||||
|
||||
var else_scope = parent_gz.makeSubBlock(scope);
|
||||
defer else_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
// This could be a pointer or value depending on `unwrap_op`.
|
||||
const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node);
|
||||
const else_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
|
||||
return finishThenElseBlock(
|
||||
parent_gz,
|
||||
rl,
|
||||
@@ -4887,7 +4962,7 @@ fn ifExpr(
|
||||
if (if_full.error_token) |_| {
|
||||
const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
|
||||
const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);
|
||||
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err;
|
||||
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
|
||||
break :c .{
|
||||
.inst = err_union,
|
||||
.bool_bit = try block_scope.addUnNode(tag, err_union, node),
|
||||
@@ -5144,7 +5219,7 @@ fn whileExpr(
|
||||
if (while_full.error_token) |_| {
|
||||
const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
|
||||
const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr);
|
||||
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err;
|
||||
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
|
||||
break :c .{
|
||||
.inst = err_union,
|
||||
.bool_bit = try continue_scope.addUnNode(tag, err_union, node),
|
||||
@@ -6090,17 +6165,37 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
const astgen = gz.astgen;
|
||||
const tree = astgen.tree;
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
|
||||
if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});
|
||||
|
||||
const defer_outer = &astgen.fn_block.?.base;
|
||||
|
||||
const operand_node = node_datas[node].lhs;
|
||||
if (operand_node == 0) {
|
||||
// Returning a void value; skip error defers.
|
||||
try genDefers(gz, &astgen.fn_block.?.base, scope, .none);
|
||||
try genDefers(gz, defer_outer, scope, .normal_only);
|
||||
_ = try gz.addUnNode(.ret_node, .void_value, node);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
}
|
||||
|
||||
if (node_tags[operand_node] == .error_value) {
|
||||
// Hot path for `return error.Foo`. This bypasses result location logic as well as logic
|
||||
// for detecting whether to add something to the function's inferred error set.
|
||||
const ident_token = node_datas[operand_node].rhs;
|
||||
const err_name_str_index = try astgen.identAsString(ident_token);
|
||||
const defer_counts = countDefers(astgen, defer_outer, scope);
|
||||
if (!defer_counts.need_err_code) {
|
||||
try genDefers(gz, defer_outer, scope, .both_sans_err);
|
||||
_ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
}
|
||||
const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
|
||||
try genDefers(gz, defer_outer, scope, .{ .both = err_code });
|
||||
_ = try gz.addUnNode(.ret_node, err_code, node);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
}
|
||||
|
||||
const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{
|
||||
.ptr = try gz.addNodeExtended(.ret_ptr, node),
|
||||
} else .{
|
||||
@@ -6111,34 +6206,46 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
switch (nodeMayEvalToError(tree, operand_node)) {
|
||||
.never => {
|
||||
// Returning a value that cannot be an error; skip error defers.
|
||||
try genDefers(gz, &astgen.fn_block.?.base, scope, .none);
|
||||
try genDefers(gz, defer_outer, scope, .normal_only);
|
||||
_ = try gz.addUnNode(.ret_node, operand, node);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
},
|
||||
.always => {
|
||||
// Value is always an error. Emit both error defers and regular defers.
|
||||
const err_code = try gz.addUnNode(.err_union_code, operand, node);
|
||||
try genDefers(gz, &astgen.fn_block.?.base, scope, err_code);
|
||||
try genDefers(gz, defer_outer, scope, .{ .both = err_code });
|
||||
_ = try gz.addUnNode(.ret_node, operand, node);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
},
|
||||
.maybe => {
|
||||
const defer_counts = countDefers(astgen, defer_outer, scope);
|
||||
if (!defer_counts.have_err) {
|
||||
// Only regular defers; no branch needed.
|
||||
try genDefers(gz, defer_outer, scope, .normal_only);
|
||||
_ = try gz.addUnNode(.ret_node, operand, node);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
}
|
||||
|
||||
// Emit conditional branch for generating errdefers.
|
||||
const is_err = try gz.addUnNode(.is_err, operand, node);
|
||||
const is_non_err = try gz.addUnNode(.is_non_err, operand, node);
|
||||
const condbr = try gz.addCondBr(.condbr, node);
|
||||
|
||||
var then_scope = gz.makeSubBlock(scope);
|
||||
defer then_scope.instructions.deinit(astgen.gpa);
|
||||
const err_code = try then_scope.addUnNode(.err_union_code, operand, node);
|
||||
try genDefers(&then_scope, &astgen.fn_block.?.base, scope, err_code);
|
||||
|
||||
try genDefers(&then_scope, defer_outer, scope, .normal_only);
|
||||
_ = try then_scope.addUnNode(.ret_node, operand, node);
|
||||
|
||||
var else_scope = gz.makeSubBlock(scope);
|
||||
defer else_scope.instructions.deinit(astgen.gpa);
|
||||
try genDefers(&else_scope, &astgen.fn_block.?.base, scope, .none);
|
||||
|
||||
const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
|
||||
.both = try else_scope.addUnNode(.err_union_code, operand, node),
|
||||
};
|
||||
try genDefers(&else_scope, defer_outer, scope, which_ones);
|
||||
_ = try else_scope.addUnNode(.ret_node, operand, node);
|
||||
|
||||
try setCondBrPayload(condbr, is_err, &then_scope, &else_scope);
|
||||
try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
|
||||
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
},
|
||||
@@ -6885,7 +6992,7 @@ fn builtinCall(
|
||||
.field => {
|
||||
const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
|
||||
if (rl == .ref) {
|
||||
return try gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
|
||||
return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
|
||||
.lhs = try expr(gz, scope, .ref, params[0]),
|
||||
.field_name = field_name,
|
||||
});
|
||||
|
||||
@@ -755,6 +755,7 @@ pub const Fn = struct {
|
||||
rbrace_column: u16,
|
||||
|
||||
state: Analysis,
|
||||
is_cold: bool = false,
|
||||
|
||||
pub const Analysis = enum {
|
||||
queued,
|
||||
@@ -776,8 +777,19 @@ pub const Fn = struct {
|
||||
}
|
||||
|
||||
pub fn deinit(func: *Fn, gpa: *Allocator) void {
|
||||
_ = func;
|
||||
_ = gpa;
|
||||
if (func.getInferredErrorSet()) |map| {
|
||||
map.deinit(gpa);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getInferredErrorSet(func: *Fn) ?*std.StringHashMapUnmanaged(void) {
|
||||
const ret_ty = func.owner_decl.ty.fnReturnType();
|
||||
if (ret_ty.zigTypeTag() == .ErrorUnion) {
|
||||
if (ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
|
||||
return &payload.data.map;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3453,6 +3465,9 @@ pub fn clearDecl(
|
||||
for (decl.dependencies.keys()) |dep| {
|
||||
dep.removeDependant(decl);
|
||||
if (dep.dependants.count() == 0 and !dep.deletion_flag) {
|
||||
log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{
|
||||
decl, decl.name, dep, dep.name,
|
||||
});
|
||||
// We don't recursively perform a deletion here, because during the update,
|
||||
// another reference to it may turn up.
|
||||
dep.deletion_flag = true;
|
||||
|
||||
312
src/Sema.zig
312
src/Sema.zig
@@ -225,12 +225,10 @@ pub fn analyzeBody(
|
||||
.float => try sema.zirFloat(block, inst),
|
||||
.float128 => try sema.zirFloat128(block, inst),
|
||||
.int_type => try sema.zirIntType(block, inst),
|
||||
.is_err => try sema.zirIsErr(block, inst),
|
||||
.is_err_ptr => try sema.zirIsErrPtr(block, inst),
|
||||
.is_non_null => try sema.zirIsNull(block, inst, true),
|
||||
.is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true),
|
||||
.is_null => try sema.zirIsNull(block, inst, false),
|
||||
.is_null_ptr => try sema.zirIsNullPtr(block, inst, false),
|
||||
.is_non_err => try sema.zirIsNonErr(block, inst),
|
||||
.is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst),
|
||||
.is_non_null => try sema.zirIsNonNull(block, inst),
|
||||
.is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst),
|
||||
.loop => try sema.zirLoop(block, inst),
|
||||
.merge_error_sets => try sema.zirMergeErrorSets(block, inst),
|
||||
.negate => try sema.zirNegate(block, inst, .sub),
|
||||
@@ -244,6 +242,7 @@ pub fn analyzeBody(
|
||||
.ptr_type => try sema.zirPtrType(block, inst),
|
||||
.ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
|
||||
.ref => try sema.zirRef(block, inst),
|
||||
.ret_err_value_code => try sema.zirRetErrValueCode(block, inst),
|
||||
.shl => try sema.zirShl(block, inst),
|
||||
.shr => try sema.zirShr(block, inst),
|
||||
.slice_end => try sema.zirSliceEnd(block, inst),
|
||||
@@ -380,8 +379,9 @@ pub fn analyzeBody(
|
||||
.condbr => return sema.zirCondbr(block, inst),
|
||||
.@"break" => return sema.zirBreak(block, inst),
|
||||
.compile_error => return sema.zirCompileError(block, inst),
|
||||
.ret_coerce => return sema.zirRetTok(block, inst, true),
|
||||
.ret_coerce => return sema.zirRetCoerce(block, inst, true),
|
||||
.ret_node => return sema.zirRetNode(block, inst),
|
||||
.ret_err_value => return sema.zirRetErrValue(block, inst),
|
||||
.@"unreachable" => return sema.zirUnreachable(block, inst),
|
||||
.repeat => return sema.zirRepeat(block, inst),
|
||||
.panic => return sema.zirPanic(block, inst),
|
||||
@@ -587,6 +587,19 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.In
|
||||
return sema.inst_map.get(@intCast(u32, i)).?;
|
||||
}
|
||||
|
||||
fn resolveConstBool(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
zir_ref: Zir.Inst.Ref,
|
||||
) !bool {
|
||||
const air_inst = try sema.resolveInst(zir_ref);
|
||||
const wanted_type = Type.initTag(.bool);
|
||||
const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
|
||||
const val = try sema.resolveConstValue(block, src, coerced_inst);
|
||||
return val.toBool();
|
||||
}
|
||||
|
||||
fn resolveConstString(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
@@ -1754,8 +1767,9 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
|
||||
fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src: LazySrcLoc = inst_data.src();
|
||||
return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirPanic", .{});
|
||||
//return always_noreturn;
|
||||
const msg_inst = try sema.resolveInst(inst_data.operand);
|
||||
|
||||
return sema.panicWithMsg(block, src, msg_inst);
|
||||
}
|
||||
|
||||
fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
@@ -2028,8 +2042,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne
|
||||
|
||||
fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src: LazySrcLoc = inst_data.src();
|
||||
return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetCold", .{});
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand);
|
||||
const func = sema.func orelse return; // does nothing outside a function
|
||||
func.is_cold = is_cold;
|
||||
}
|
||||
|
||||
fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
|
||||
@@ -2041,11 +2057,7 @@ fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
|
||||
fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
|
||||
const op = try sema.resolveInst(inst_data.operand);
|
||||
const op_coerced = try sema.coerce(block, Type.initTag(.bool), op, operand_src);
|
||||
const b = (try sema.resolveConstValue(block, operand_src, op_coerced)).toBool();
|
||||
block.want_safety = b;
|
||||
block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);
|
||||
}
|
||||
|
||||
fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
|
||||
@@ -2190,21 +2202,27 @@ fn zirCall(
|
||||
const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index);
|
||||
const args = sema.code.refSlice(extra.end, extra.data.args_len);
|
||||
|
||||
return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, ensure_result_used, args);
|
||||
const func = try sema.resolveInst(extra.data.callee);
|
||||
// TODO handle function calls of generic functions
|
||||
const resolved_args = try sema.arena.alloc(*Inst, args.len);
|
||||
for (args) |zir_arg, i| {
|
||||
// the args are already casted to the result of a param type instruction.
|
||||
resolved_args[i] = try sema.resolveInst(zir_arg);
|
||||
}
|
||||
|
||||
return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
|
||||
}
|
||||
|
||||
fn analyzeCall(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
zir_func: Zir.Inst.Ref,
|
||||
func: *ir.Inst,
|
||||
func_src: LazySrcLoc,
|
||||
call_src: LazySrcLoc,
|
||||
modifier: std.builtin.CallOptions.Modifier,
|
||||
ensure_result_used: bool,
|
||||
zir_args: []const Zir.Inst.Ref,
|
||||
args: []const *ir.Inst,
|
||||
) InnerError!*ir.Inst {
|
||||
const func = try sema.resolveInst(zir_func);
|
||||
|
||||
if (func.ty.zigTypeTag() != .Fn)
|
||||
return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty});
|
||||
|
||||
@@ -2221,22 +2239,22 @@ fn analyzeCall(
|
||||
const fn_params_len = func.ty.fnParamLen();
|
||||
if (func.ty.fnIsVarArgs()) {
|
||||
assert(cc == .C);
|
||||
if (zir_args.len < fn_params_len) {
|
||||
if (args.len < fn_params_len) {
|
||||
// TODO add error note: declared here
|
||||
return sema.mod.fail(
|
||||
&block.base,
|
||||
func_src,
|
||||
"expected at least {d} argument(s), found {d}",
|
||||
.{ fn_params_len, zir_args.len },
|
||||
.{ fn_params_len, args.len },
|
||||
);
|
||||
}
|
||||
} else if (fn_params_len != zir_args.len) {
|
||||
} else if (fn_params_len != args.len) {
|
||||
// TODO add error note: declared here
|
||||
return sema.mod.fail(
|
||||
&block.base,
|
||||
func_src,
|
||||
"expected {d} argument(s), found {d}",
|
||||
.{ fn_params_len, zir_args.len },
|
||||
.{ fn_params_len, args.len },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2256,13 +2274,6 @@ fn analyzeCall(
|
||||
}),
|
||||
}
|
||||
|
||||
// TODO handle function calls of generic functions
|
||||
const casted_args = try sema.arena.alloc(*Inst, zir_args.len);
|
||||
for (zir_args) |zir_arg, i| {
|
||||
// the args are already casted to the result of a param type instruction.
|
||||
casted_args[i] = try sema.resolveInst(zir_arg);
|
||||
}
|
||||
|
||||
const ret_type = func.ty.fnReturnType();
|
||||
|
||||
const is_comptime_call = block.is_comptime or modifier == .compile_time;
|
||||
@@ -2323,7 +2334,7 @@ fn analyzeCall(
|
||||
defer sema.func = parent_func;
|
||||
|
||||
const parent_param_inst_list = sema.param_inst_list;
|
||||
sema.param_inst_list = casted_args;
|
||||
sema.param_inst_list = args;
|
||||
defer sema.param_inst_list = parent_param_inst_list;
|
||||
|
||||
const parent_next_arg_index = sema.next_arg_index;
|
||||
@@ -2357,7 +2368,7 @@ fn analyzeCall(
|
||||
break :res result;
|
||||
} else res: {
|
||||
try sema.requireRuntimeBlock(block, call_src);
|
||||
break :res try block.addCall(call_src, ret_type, func, casted_args);
|
||||
break :res try block.addCall(call_src, ret_type, func, args);
|
||||
};
|
||||
|
||||
if (ensure_result_used) {
|
||||
@@ -2968,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
|
||||
if (operand.ty.zigTypeTag() != .ErrorUnion)
|
||||
return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
|
||||
|
||||
const result_ty = operand.ty.castTag(.error_union).?.data.error_set;
|
||||
|
||||
if (operand.value()) |val| {
|
||||
assert(val.getError() != null);
|
||||
const data = val.castTag(.error_union).?.data;
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = operand.ty.castTag(.error_union).?.data.error_set,
|
||||
.ty = result_ty,
|
||||
.val = data,
|
||||
});
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand);
|
||||
return block.addUnOp(src, result_ty, .unwrap_errunion_err, operand);
|
||||
}
|
||||
|
||||
/// Pointer in, value out
|
||||
@@ -2994,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
|
||||
if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
|
||||
return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
|
||||
|
||||
const result_ty = operand.ty.elemType().castTag(.error_union).?.data.error_set;
|
||||
|
||||
if (operand.value()) |pointer_val| {
|
||||
const val = try pointer_val.pointerDeref(sema.arena);
|
||||
assert(val.getError() != null);
|
||||
const data = val.castTag(.error_union).?.data;
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = operand.ty.elemType().castTag(.error_union).?.data.error_set,
|
||||
.ty = result_ty,
|
||||
.val = data,
|
||||
});
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand);
|
||||
return block.addUnOp(src, result_ty, .unwrap_errunion_err_ptr, operand);
|
||||
}
|
||||
|
||||
fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
|
||||
@@ -3081,28 +3096,31 @@ fn funcCommon(
|
||||
) InnerError!*Inst {
|
||||
const src: LazySrcLoc = .{ .node_offset = src_node_offset };
|
||||
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
|
||||
const return_type = try sema.resolveType(block, ret_ty_src, zir_return_type);
|
||||
const bare_return_type = try sema.resolveType(block, ret_ty_src, zir_return_type);
|
||||
|
||||
const mod = sema.mod;
|
||||
|
||||
const new_func = if (body_inst == 0) undefined else try sema.gpa.create(Module.Fn);
|
||||
errdefer if (body_inst != 0) sema.gpa.destroy(new_func);
|
||||
|
||||
const fn_ty: Type = fn_ty: {
|
||||
// Hot path for some common function types.
|
||||
if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and
|
||||
!inferred_error_set)
|
||||
{
|
||||
if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
|
||||
if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
|
||||
break :fn_ty Type.initTag(.fn_noreturn_no_args);
|
||||
}
|
||||
|
||||
if (return_type.zigTypeTag() == .Void and cc == .Unspecified) {
|
||||
if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) {
|
||||
break :fn_ty Type.initTag(.fn_void_no_args);
|
||||
}
|
||||
|
||||
if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
|
||||
if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
|
||||
break :fn_ty Type.initTag(.fn_naked_noreturn_no_args);
|
||||
}
|
||||
|
||||
if (return_type.zigTypeTag() == .Void and cc == .C) {
|
||||
if (bare_return_type.zigTypeTag() == .Void and cc == .C) {
|
||||
break :fn_ty Type.initTag(.fn_ccc_void_no_args);
|
||||
}
|
||||
}
|
||||
@@ -3120,9 +3138,16 @@ fn funcCommon(
|
||||
return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{});
|
||||
}
|
||||
|
||||
if (inferred_error_set) {
|
||||
return mod.fail(&block.base, src, "TODO implement functions with inferred error sets", .{});
|
||||
}
|
||||
const return_type = if (!inferred_error_set) bare_return_type else blk: {
|
||||
const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, .{
|
||||
.func = new_func,
|
||||
.map = .{},
|
||||
});
|
||||
break :blk try Type.Tag.error_union.create(sema.arena, .{
|
||||
.error_set = error_set_ty,
|
||||
.payload = bare_return_type,
|
||||
});
|
||||
};
|
||||
|
||||
break :fn_ty try Type.Tag.function.create(sema.arena, .{
|
||||
.param_types = param_types,
|
||||
@@ -3188,7 +3213,6 @@ fn funcCommon(
|
||||
const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued;
|
||||
|
||||
const fn_payload = try sema.arena.create(Value.Payload.Function);
|
||||
const new_func = try sema.gpa.create(Module.Fn);
|
||||
new_func.* = .{
|
||||
.state = anal_state,
|
||||
.zir_body_inst = body_inst,
|
||||
@@ -4542,6 +4566,12 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
|
||||
return mod.constType(sema.arena, src, file_root_decl.ty);
|
||||
}
|
||||
|
||||
fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
_ = block;
|
||||
_ = inst;
|
||||
return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});
|
||||
}
|
||||
|
||||
fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@@ -5273,11 +5303,10 @@ fn zirBoolBr(
|
||||
return &block_inst.base;
|
||||
}
|
||||
|
||||
fn zirIsNull(
|
||||
fn zirIsNonNull(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: Zir.Inst.Index,
|
||||
invert_logic: bool,
|
||||
) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@@ -5285,14 +5314,13 @@ fn zirIsNull(
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
return sema.analyzeIsNull(block, src, operand, invert_logic);
|
||||
return sema.analyzeIsNull(block, src, operand, true);
|
||||
}
|
||||
|
||||
fn zirIsNullPtr(
|
||||
fn zirIsNonNullPtr(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: Zir.Inst.Index,
|
||||
invert_logic: bool,
|
||||
) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@@ -5301,19 +5329,19 @@ fn zirIsNullPtr(
|
||||
const src = inst_data.src();
|
||||
const ptr = try sema.resolveInst(inst_data.operand);
|
||||
const loaded = try sema.analyzeLoad(block, src, ptr, src);
|
||||
return sema.analyzeIsNull(block, src, loaded, invert_logic);
|
||||
return sema.analyzeIsNull(block, src, loaded, true);
|
||||
}
|
||||
|
||||
fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
fn zirIsNonErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
return sema.analyzeIsErr(block, inst_data.src(), operand);
|
||||
return sema.analyzeIsNonErr(block, inst_data.src(), operand);
|
||||
}
|
||||
|
||||
fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -5321,7 +5349,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
|
||||
const src = inst_data.src();
|
||||
const ptr = try sema.resolveInst(inst_data.operand);
|
||||
const loaded = try sema.analyzeLoad(block, src, ptr, src);
|
||||
return sema.analyzeIsErr(block, src, loaded);
|
||||
return sema.analyzeIsNonErr(block, src, loaded);
|
||||
}
|
||||
|
||||
fn zirCondbr(
|
||||
@@ -5388,7 +5416,31 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
|
||||
}
|
||||
}
|
||||
|
||||
fn zirRetTok(
|
||||
fn zirRetErrValue(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: Zir.Inst.Index,
|
||||
) InnerError!Zir.Inst.Index {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
|
||||
const err_name = inst_data.get(sema.code);
|
||||
const src = inst_data.src();
|
||||
|
||||
// Add the error tag to the inferred error set of the in-scope function.
|
||||
if (sema.func) |func| {
|
||||
if (func.getInferredErrorSet()) |map| {
|
||||
_ = try map.getOrPut(sema.gpa, err_name);
|
||||
}
|
||||
}
|
||||
// Return the error code from the function.
|
||||
const kv = try sema.mod.getErrorValue(err_name);
|
||||
const result_inst = try sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = try Type.Tag.error_set_single.create(sema.arena, kv.key),
|
||||
.val = try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }),
|
||||
});
|
||||
return sema.analyzeRet(block, result_inst, src, true);
|
||||
}
|
||||
|
||||
fn zirRetCoerce(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: Zir.Inst.Index,
|
||||
@@ -6195,6 +6247,10 @@ fn zirFuncExtended(
|
||||
src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
|
||||
}
|
||||
|
||||
const is_var_args = small.is_var_args;
|
||||
const is_inferred_error = small.is_inferred_error;
|
||||
const is_extern = small.is_extern;
|
||||
|
||||
return sema.funcCommon(
|
||||
block,
|
||||
extra.data.src_node,
|
||||
@@ -6203,9 +6259,9 @@ fn zirFuncExtended(
|
||||
extra.data.return_type,
|
||||
cc,
|
||||
align_val,
|
||||
small.is_var_args,
|
||||
small.is_inferred_error,
|
||||
small.is_extern,
|
||||
is_var_args,
|
||||
is_inferred_error,
|
||||
is_extern,
|
||||
src_locs,
|
||||
lib_name,
|
||||
);
|
||||
@@ -6357,15 +6413,75 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:
|
||||
try parent_block.instructions.append(sema.gpa, &block_inst.base);
|
||||
}
|
||||
|
||||
fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !Zir.Inst.Index {
|
||||
_ = sema;
|
||||
_ = panic_id;
|
||||
// TODO Once we have a panic function to call, call it here instead of breakpoint.
|
||||
_ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
|
||||
_ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
|
||||
fn panicWithMsg(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
msg_inst: *ir.Inst,
|
||||
) !Zir.Inst.Index {
|
||||
const mod = sema.mod;
|
||||
const arena = sema.arena;
|
||||
|
||||
const this_feature_is_implemented_in_the_backend =
|
||||
mod.comp.bin_file.options.object_format == .c;
|
||||
if (!this_feature_is_implemented_in_the_backend) {
|
||||
// TODO implement this feature in all the backends and then delete this branch
|
||||
_ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
|
||||
_ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
|
||||
return always_noreturn;
|
||||
}
|
||||
const panic_fn = try sema.getBuiltin(block, src, "panic");
|
||||
const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
|
||||
const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
|
||||
const ptr_stack_trace_ty = try mod.simplePtrType(arena, stack_trace_ty, true, .One);
|
||||
const null_stack_trace = try mod.constInst(arena, src, .{
|
||||
.ty = try mod.optionalType(arena, ptr_stack_trace_ty),
|
||||
.val = Value.initTag(.null_value),
|
||||
});
|
||||
const args = try arena.create([2]*ir.Inst);
|
||||
args.* = .{ msg_inst, null_stack_trace };
|
||||
_ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);
|
||||
return always_noreturn;
|
||||
}
|
||||
|
||||
fn safetyPanic(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
panic_id: PanicId,
|
||||
) !Zir.Inst.Index {
|
||||
const msg = switch (panic_id) {
|
||||
.unreach => "reached unreachable code",
|
||||
.unwrap_null => "attempt to use null value",
|
||||
.unwrap_errunion => "unreachable error occurred",
|
||||
.cast_to_null => "cast causes pointer to be null",
|
||||
.incorrect_alignment => "incorrect alignment",
|
||||
.invalid_error_code => "invalid error code",
|
||||
};
|
||||
|
||||
const msg_inst = msg_inst: {
|
||||
// TODO instead of making a new decl for every panic in the entire compilation,
|
||||
// introduce the concept of a reference-counted decl for these
|
||||
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||
errdefer new_decl_arena.deinit();
|
||||
|
||||
const decl_ty = try Type.Tag.array_u8.create(&new_decl_arena.allocator, msg.len);
|
||||
const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, msg);
|
||||
|
||||
const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
|
||||
.ty = decl_ty,
|
||||
.val = decl_val,
|
||||
});
|
||||
errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
break :msg_inst try sema.analyzeDeclRef(block, .unneeded, new_decl);
|
||||
};
|
||||
|
||||
const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src);
|
||||
|
||||
return sema.panicWithMsg(block, src, casted_msg_inst);
|
||||
}
|
||||
|
||||
fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void {
|
||||
sema.branch_count += 1;
|
||||
if (sema.branch_count > sema.branch_quota) {
|
||||
@@ -7102,20 +7218,25 @@ fn analyzeIsNull(
|
||||
return block.addUnOp(src, result_ty, inst_tag, operand);
|
||||
}
|
||||
|
||||
fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst {
|
||||
fn analyzeIsNonErr(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
operand: *Inst,
|
||||
) InnerError!*Inst {
|
||||
const ot = operand.ty.zigTypeTag();
|
||||
if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false);
|
||||
if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true);
|
||||
if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true);
|
||||
if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false);
|
||||
assert(ot == .ErrorUnion);
|
||||
const result_ty = Type.initTag(.bool);
|
||||
if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {
|
||||
if (err_union.isUndef()) {
|
||||
return sema.mod.constUndef(sema.arena, src, result_ty);
|
||||
}
|
||||
return sema.mod.constBool(sema.arena, src, err_union.getError() != null);
|
||||
return sema.mod.constBool(sema.arena, src, err_union.getError() == null);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, result_ty, .is_err, operand);
|
||||
return block.addUnOp(src, result_ty, .is_non_err, operand);
|
||||
}
|
||||
|
||||
fn analyzeSlice(
|
||||
@@ -7377,15 +7498,13 @@ fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst)
|
||||
}
|
||||
|
||||
fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst {
|
||||
// TODO deal with inferred error sets
|
||||
const err_union = dest_type.castTag(.error_union).?;
|
||||
if (inst.value()) |val| {
|
||||
const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: {
|
||||
if (inst.ty.zigTypeTag() != .ErrorSet) {
|
||||
_ = try sema.coerce(block, err_union.data.payload, inst, inst.src);
|
||||
break :blk val;
|
||||
} else switch (err_union.data.error_set.tag()) {
|
||||
.anyerror => val,
|
||||
.error_set_single => blk: {
|
||||
.anyerror => {},
|
||||
.error_set_single => {
|
||||
const expected_name = val.castTag(.@"error").?.data.name;
|
||||
const n = err_union.data.error_set.castTag(.error_set_single).?.data;
|
||||
if (!mem.eql(u8, expected_name, n)) {
|
||||
@@ -7396,9 +7515,8 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
|
||||
.{ err_union.data.error_set, inst.ty },
|
||||
);
|
||||
}
|
||||
break :blk val;
|
||||
},
|
||||
.error_set => blk: {
|
||||
.error_set => {
|
||||
const expected_name = val.castTag(.@"error").?.data.name;
|
||||
const error_set = err_union.data.error_set.castTag(.error_set).?.data;
|
||||
const names = error_set.names_ptr[0..error_set.names_len];
|
||||
@@ -7415,18 +7533,26 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
|
||||
.{ err_union.data.error_set, inst.ty },
|
||||
);
|
||||
}
|
||||
break :blk val;
|
||||
},
|
||||
.error_set_inferred => {
|
||||
const expected_name = val.castTag(.@"error").?.data.name;
|
||||
const map = &err_union.data.error_set.castTag(.error_set_inferred).?.data.map;
|
||||
if (!map.contains(expected_name)) {
|
||||
return sema.mod.fail(
|
||||
&block.base,
|
||||
inst.src,
|
||||
"expected type '{}', found type '{}'",
|
||||
.{ err_union.data.error_set, inst.ty },
|
||||
);
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
return sema.mod.constInst(sema.arena, inst.src, .{
|
||||
.ty = dest_type,
|
||||
// creating a SubValue for the error_union payload
|
||||
.val = try Value.Tag.error_union.create(
|
||||
sema.arena,
|
||||
to_wrap,
|
||||
),
|
||||
.val = try Value.Tag.error_union.create(sema.arena, val),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7573,12 +7699,12 @@ fn resolveBuiltinTypeFields(
|
||||
return sema.resolveTypeFields(block, src, resolved_ty);
|
||||
}
|
||||
|
||||
fn getBuiltinType(
|
||||
fn getBuiltin(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
name: []const u8,
|
||||
) InnerError!Type {
|
||||
) InnerError!*ir.Inst {
|
||||
const mod = sema.mod;
|
||||
const std_pkg = mod.root_pkg.table.get("std").?;
|
||||
const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
|
||||
@@ -7596,7 +7722,16 @@ fn getBuiltinType(
|
||||
builtin_ty.getNamespace().?,
|
||||
name,
|
||||
);
|
||||
const ty_inst = try sema.analyzeLoad(block, src, opt_ty_inst.?, src);
|
||||
return sema.analyzeLoad(block, src, opt_ty_inst.?, src);
|
||||
}
|
||||
|
||||
fn getBuiltinType(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
name: []const u8,
|
||||
) InnerError!Type {
|
||||
const ty_inst = try sema.getBuiltin(block, src, name);
|
||||
return sema.resolveAirAsType(block, src, ty_inst);
|
||||
}
|
||||
|
||||
@@ -7662,6 +7797,7 @@ fn typeHasOnePossibleValue(
|
||||
.error_union,
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.error_set_inferred,
|
||||
.@"opaque",
|
||||
.var_args_param,
|
||||
.manyptr_u8,
|
||||
|
||||
59
src/Zir.zig
59
src/Zir.zig
@@ -1,7 +1,7 @@
|
||||
//! Zig Intermediate Representation. Astgen.zig converts AST nodes to these
|
||||
//! untyped IR instructions. Next, Sema.zig processes these into TZIR.
|
||||
//! untyped IR instructions. Next, Sema.zig processes these into AIR.
|
||||
//! The minimum amount of information needed to represent a list of ZIR instructions.
|
||||
//! Once this structure is completed, it can be used to generate TZIR, followed by
|
||||
//! Once this structure is completed, it can be used to generate AIR, followed by
|
||||
//! machine code, without any memory access into the AST tree token list, node list,
|
||||
//! or source bytes. Exceptions include:
|
||||
//! * Compile errors, which may need to reach into these data structures to
|
||||
@@ -398,26 +398,20 @@ pub const Inst = struct {
|
||||
/// Return a boolean false if an optional is null. `x != null`
|
||||
/// Uses the `un_node` field.
|
||||
is_non_null,
|
||||
/// Return a boolean true if an optional is null. `x == null`
|
||||
/// Uses the `un_node` field.
|
||||
is_null,
|
||||
/// Return a boolean false if an optional is null. `x.* != null`
|
||||
/// Uses the `un_node` field.
|
||||
is_non_null_ptr,
|
||||
/// Return a boolean true if an optional is null. `x.* == null`
|
||||
/// Return a boolean false if value is an error
|
||||
/// Uses the `un_node` field.
|
||||
is_null_ptr,
|
||||
/// Return a boolean true if value is an error
|
||||
is_non_err,
|
||||
/// Return a boolean false if dereferenced pointer is an error
|
||||
/// Uses the `un_node` field.
|
||||
is_err,
|
||||
/// Return a boolean true if dereferenced pointer is an error
|
||||
/// Uses the `un_node` field.
|
||||
is_err_ptr,
|
||||
is_non_err_ptr,
|
||||
/// A labeled block of code that loops forever. At the end of the body will have either
|
||||
/// a `repeat` instruction or a `repeat_inline` instruction.
|
||||
/// Uses the `pl_node` field. The AST node is either a for loop or while loop.
|
||||
/// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema
|
||||
/// needs to emit more than 1 TZIR block for this instruction.
|
||||
/// This ZIR instruction is needed because AIR does not (yet?) match ZIR, and Sema
|
||||
/// needs to emit more than 1 AIR block for this instruction.
|
||||
/// The payload is `Block`.
|
||||
loop,
|
||||
/// Sends runtime control flow back to the beginning of the current block.
|
||||
@@ -466,6 +460,19 @@ pub const Inst = struct {
|
||||
/// Uses the `un_tok` union field.
|
||||
/// The operand needs to get coerced to the function's return type.
|
||||
ret_coerce,
|
||||
/// Sends control flow back to the function's callee.
|
||||
/// The return operand is `error.foo` where `foo` is given by the string.
|
||||
/// If the current function has an inferred error set, the error given by the
|
||||
/// name is added to it.
|
||||
/// Uses the `str_tok` union field.
|
||||
ret_err_value,
|
||||
/// A string name is provided which is an anonymous error set value.
|
||||
/// If the current function has an inferred error set, the error given by the
|
||||
/// name is added to it.
|
||||
/// Results in the error code. Note that control flow is not diverted with
|
||||
/// this instruction; a following 'ret' instruction will do the diversion.
|
||||
/// Uses the `str_tok` union field.
|
||||
ret_err_value_code,
|
||||
/// Create a pointer type that does not have a sentinel, alignment, or bit range specified.
|
||||
/// Uses the `ptr_type_simple` union field.
|
||||
ptr_type_simple,
|
||||
@@ -1033,11 +1040,9 @@ pub const Inst = struct {
|
||||
.float128,
|
||||
.int_type,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err,
|
||||
.is_non_err_ptr,
|
||||
.mod_rem,
|
||||
.mul,
|
||||
.mulwrap,
|
||||
@@ -1193,6 +1198,7 @@ pub const Inst = struct {
|
||||
.@"resume",
|
||||
.@"await",
|
||||
.await_nosuspend,
|
||||
.ret_err_value_code,
|
||||
.extended,
|
||||
=> false,
|
||||
|
||||
@@ -1203,6 +1209,7 @@ pub const Inst = struct {
|
||||
.compile_error,
|
||||
.ret_node,
|
||||
.ret_coerce,
|
||||
.ret_err_value,
|
||||
.@"unreachable",
|
||||
.repeat,
|
||||
.repeat_inline,
|
||||
@@ -1291,11 +1298,9 @@ pub const Inst = struct {
|
||||
.float128 = .pl_node,
|
||||
.int_type = .int_type,
|
||||
.is_non_null = .un_node,
|
||||
.is_null = .un_node,
|
||||
.is_non_null_ptr = .un_node,
|
||||
.is_null_ptr = .un_node,
|
||||
.is_err = .un_node,
|
||||
.is_err_ptr = .un_node,
|
||||
.is_non_err = .un_node,
|
||||
.is_non_err_ptr = .un_node,
|
||||
.loop = .pl_node,
|
||||
.repeat = .node,
|
||||
.repeat_inline = .node,
|
||||
@@ -1307,6 +1312,8 @@ pub const Inst = struct {
|
||||
.ref = .un_tok,
|
||||
.ret_node = .un_node,
|
||||
.ret_coerce = .un_tok,
|
||||
.ret_err_value = .str_tok,
|
||||
.ret_err_value_code = .str_tok,
|
||||
.ptr_type_simple = .ptr_type_simple,
|
||||
.ptr_type = .ptr_type,
|
||||
.slice_start = .pl_node,
|
||||
@@ -2840,11 +2847,9 @@ const Writer = struct {
|
||||
.err_union_code,
|
||||
.err_union_code_ptr,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err,
|
||||
.is_non_err_ptr,
|
||||
.typeof,
|
||||
.typeof_elem,
|
||||
.struct_init_empty,
|
||||
@@ -3077,6 +3082,8 @@ const Writer = struct {
|
||||
.decl_val,
|
||||
.import,
|
||||
.arg,
|
||||
.ret_err_value,
|
||||
.ret_err_value_code,
|
||||
=> try self.writeStrTok(stream, inst),
|
||||
|
||||
.func => try self.writeFunc(stream, inst, false),
|
||||
|
||||
34
src/air.zig
34
src/air.zig
@@ -90,8 +90,12 @@ pub const Inst = struct {
|
||||
is_non_null_ptr,
|
||||
/// E!T => bool
|
||||
is_err,
|
||||
/// E!T => bool (inverted logic)
|
||||
is_non_err,
|
||||
/// *E!T => bool
|
||||
is_err_ptr,
|
||||
/// *E!T => bool (inverted logic)
|
||||
is_non_err_ptr,
|
||||
bool_and,
|
||||
bool_or,
|
||||
/// Read a value from a pointer.
|
||||
@@ -154,7 +158,9 @@ pub const Inst = struct {
|
||||
.is_null,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_non_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
@@ -672,15 +678,15 @@ pub const Body = struct {
|
||||
/// For debugging purposes, prints a function representation to stderr.
|
||||
pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
|
||||
const allocator = old_module.gpa;
|
||||
var ctx: DumpTzir = .{
|
||||
var ctx: DumpAir = .{
|
||||
.allocator = allocator,
|
||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||
.old_module = &old_module,
|
||||
.module_fn = module_fn,
|
||||
.indent = 2,
|
||||
.inst_table = DumpTzir.InstTable.init(allocator),
|
||||
.partial_inst_table = DumpTzir.InstTable.init(allocator),
|
||||
.const_table = DumpTzir.InstTable.init(allocator),
|
||||
.inst_table = DumpAir.InstTable.init(allocator),
|
||||
.partial_inst_table = DumpAir.InstTable.init(allocator),
|
||||
.const_table = DumpAir.InstTable.init(allocator),
|
||||
};
|
||||
defer ctx.inst_table.deinit();
|
||||
defer ctx.partial_inst_table.deinit();
|
||||
@@ -695,12 +701,12 @@ pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
|
||||
.dependency_failure => std.debug.print("(dependency_failure)", .{}),
|
||||
.success => {
|
||||
const writer = std.io.getStdErr().writer();
|
||||
ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
|
||||
ctx.dump(module_fn.body, writer) catch @panic("failed to dump AIR");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const DumpTzir = struct {
|
||||
const DumpAir = struct {
|
||||
allocator: *std.mem.Allocator,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
old_module: *const Module,
|
||||
@@ -718,7 +724,7 @@ const DumpTzir = struct {
|
||||
/// TODO: Improve this code to include a stack of Body and store the instructions
|
||||
/// in there. Now we are putting all the instructions in a function local table,
|
||||
/// however instructions that are in a Body can be thown away when the Body ends.
|
||||
fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void {
|
||||
fn dump(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) !void {
|
||||
// First pass to pre-populate the table so that we can show even invalid references.
|
||||
// Must iterate the same order we iterate the second time.
|
||||
// We also look for constants and put them in the const_table.
|
||||
@@ -737,7 +743,7 @@ const DumpTzir = struct {
|
||||
return dtz.dumpBody(body, writer);
|
||||
}
|
||||
|
||||
fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void {
|
||||
fn fetchInstsAndResolveConsts(dtz: *DumpAir, body: Body) error{OutOfMemory}!void {
|
||||
for (body.instructions) |inst| {
|
||||
try dtz.inst_table.put(inst, dtz.next_index);
|
||||
dtz.next_index += 1;
|
||||
@@ -759,7 +765,9 @@ const DumpTzir = struct {
|
||||
.is_null,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_non_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
@@ -865,7 +873,7 @@ const DumpTzir = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
|
||||
fn dumpBody(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
|
||||
for (body.instructions) |inst| {
|
||||
const my_index = dtz.next_partial_index;
|
||||
try dtz.partial_inst_table.put(inst, my_index);
|
||||
@@ -888,11 +896,13 @@ const DumpTzir = struct {
|
||||
.bitcast,
|
||||
.not,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
.is_null,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err,
|
||||
.is_non_err_ptr,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
@@ -1150,7 +1160,7 @@ const DumpTzir = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
|
||||
fn writeInst(dtz: *DumpAir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
|
||||
if (dtz.partial_inst_table.get(inst)) |operand_index| {
|
||||
try writer.print("%{d}", .{operand_index});
|
||||
return null;
|
||||
@@ -1166,7 +1176,7 @@ const DumpTzir = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn findConst(dtz: *DumpTzir, operand: *Inst) !void {
|
||||
fn findConst(dtz: *DumpAir, operand: *Inst) !void {
|
||||
if (operand.tag == .constant) {
|
||||
try dtz.const_table.put(operand, dtz.next_const_index);
|
||||
dtz.next_const_index += 1;
|
||||
|
||||
226
src/codegen.zig
226
src/codegen.zig
@@ -142,40 +142,52 @@ pub fn generateSymbol(
|
||||
),
|
||||
};
|
||||
},
|
||||
.Pointer => {
|
||||
// TODO populate .debug_info for the pointer
|
||||
if (typed_value.val.castTag(.decl_ref)) |payload| {
|
||||
const decl = payload.data;
|
||||
if (decl.analysis != .complete) return error.AnalysisFail;
|
||||
// TODO handle the dependency of this symbol on the decl's vaddr.
|
||||
// If the decl changes vaddr, then this symbol needs to get regenerated.
|
||||
const vaddr = bin_file.getDeclVAddr(decl);
|
||||
const endian = bin_file.options.target.cpu.arch.endian();
|
||||
switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
|
||||
16 => {
|
||||
try code.resize(2);
|
||||
mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
|
||||
},
|
||||
32 => {
|
||||
try code.resize(4);
|
||||
mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
|
||||
},
|
||||
64 => {
|
||||
try code.resize(8);
|
||||
mem.writeInt(u64, code.items[0..8], vaddr, endian);
|
||||
},
|
||||
else => unreachable,
|
||||
.Pointer => switch (typed_value.ty.ptrSize()) {
|
||||
.Slice => {
|
||||
return Result{
|
||||
.fail = try ErrorMsg.create(
|
||||
bin_file.allocator,
|
||||
src_loc,
|
||||
"TODO implement generateSymbol for slice {}",
|
||||
.{typed_value.val},
|
||||
),
|
||||
};
|
||||
},
|
||||
else => {
|
||||
// TODO populate .debug_info for the pointer
|
||||
if (typed_value.val.castTag(.decl_ref)) |payload| {
|
||||
const decl = payload.data;
|
||||
if (decl.analysis != .complete) return error.AnalysisFail;
|
||||
// TODO handle the dependency of this symbol on the decl's vaddr.
|
||||
// If the decl changes vaddr, then this symbol needs to get regenerated.
|
||||
const vaddr = bin_file.getDeclVAddr(decl);
|
||||
const endian = bin_file.options.target.cpu.arch.endian();
|
||||
switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
|
||||
16 => {
|
||||
try code.resize(2);
|
||||
mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
|
||||
},
|
||||
32 => {
|
||||
try code.resize(4);
|
||||
mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
|
||||
},
|
||||
64 => {
|
||||
try code.resize(8);
|
||||
mem.writeInt(u64, code.items[0..8], vaddr, endian);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
return Result{ .appended = {} };
|
||||
}
|
||||
return Result{ .appended = {} };
|
||||
}
|
||||
return Result{
|
||||
.fail = try ErrorMsg.create(
|
||||
bin_file.allocator,
|
||||
src_loc,
|
||||
"TODO implement generateSymbol for pointer {}",
|
||||
.{typed_value.val},
|
||||
),
|
||||
};
|
||||
return Result{
|
||||
.fail = try ErrorMsg.create(
|
||||
bin_file.allocator,
|
||||
src_loc,
|
||||
"TODO implement generateSymbol for pointer {}",
|
||||
.{typed_value.val},
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
.Int => {
|
||||
// TODO populate .debug_info for the integer
|
||||
@@ -847,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),
|
||||
.is_null => return self.genIsNull(inst.castTag(.is_null).?),
|
||||
.is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
|
||||
.is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?),
|
||||
.is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?),
|
||||
.is_err => return self.genIsErr(inst.castTag(.is_err).?),
|
||||
.is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
|
||||
.load => return self.genLoad(inst.castTag(.load).?),
|
||||
@@ -2244,10 +2258,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
try self.register_manager.getReg(reg, null);
|
||||
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
|
||||
},
|
||||
.stack_offset => {
|
||||
.stack_offset => |off| {
|
||||
// Here we need to emit instructions like this:
|
||||
// mov qword ptr [rsp + stack_offset], x
|
||||
return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
|
||||
try self.genSetStack(arg.src, arg.ty, off, arg_mcv);
|
||||
},
|
||||
.ptr_stack_offset => {
|
||||
return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
|
||||
@@ -2960,6 +2974,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
|
||||
}
|
||||
|
||||
fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
|
||||
switch (arch) {
|
||||
else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
|
||||
return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{});
|
||||
}
|
||||
|
||||
fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
|
||||
// A loop is a setup to be able to jump back to the beginning.
|
||||
const start_index = self.code.items.len;
|
||||
@@ -3444,9 +3468,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
},
|
||||
}
|
||||
},
|
||||
.embedded_in_code => |code_offset| {
|
||||
_ = code_offset;
|
||||
return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
|
||||
.embedded_in_code => {
|
||||
// TODO this and `.stack_offset` below need to get improved to support types greater than
|
||||
// register size, and do general memcpy
|
||||
const reg = try self.copyToTmpRegister(src, ty, mcv);
|
||||
return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
|
||||
},
|
||||
.register => |reg| {
|
||||
try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
|
||||
@@ -3456,6 +3482,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
|
||||
},
|
||||
.stack_offset => |off| {
|
||||
// TODO this and `.embedded_in_code` above need to get improved to support types greater than
|
||||
// register size, and do general memcpy
|
||||
|
||||
if (stack_offset == off)
|
||||
return; // Copy stack variable to itself; nothing to do.
|
||||
|
||||
@@ -4161,33 +4190,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
|
||||
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
|
||||
switch (typed_value.ty.zigTypeTag()) {
|
||||
.Pointer => {
|
||||
if (typed_value.val.castTag(.decl_ref)) |payload| {
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const decl = payload.data;
|
||||
const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
|
||||
const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
|
||||
return MCValue{ .memory = got_addr };
|
||||
} else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const decl = payload.data;
|
||||
const got_addr = blk: {
|
||||
const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
|
||||
const got = seg.sections.items[macho_file.got_section_index.?];
|
||||
break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
|
||||
};
|
||||
return MCValue{ .memory = got_addr };
|
||||
} else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
|
||||
const decl = payload.data;
|
||||
const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
|
||||
return MCValue{ .memory = got_addr };
|
||||
} else {
|
||||
return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
|
||||
.Pointer => switch (typed_value.ty.ptrSize()) {
|
||||
.Slice => {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
|
||||
const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val });
|
||||
const slice_len = typed_value.val.sliceLen();
|
||||
// Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
|
||||
// the Sema code needs to use anonymous Decls or alloca instructions to store data.
|
||||
const ptr_imm = ptr_mcv.memory;
|
||||
_ = slice_len;
|
||||
_ = ptr_imm;
|
||||
// We need more general support for const data being stored in memory to make this work.
|
||||
return self.fail(src, "TODO codegen for const slices", .{});
|
||||
},
|
||||
else => {
|
||||
if (typed_value.val.castTag(.decl_ref)) |payload| {
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
|
||||
const decl = payload.data;
|
||||
const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
|
||||
const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
|
||||
return MCValue{ .memory = got_addr };
|
||||
} else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
|
||||
const decl = payload.data;
|
||||
const got_addr = blk: {
|
||||
const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
|
||||
const got = seg.sections.items[macho_file.got_section_index.?];
|
||||
break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes;
|
||||
};
|
||||
return MCValue{ .memory = got_addr };
|
||||
} else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
|
||||
const decl = payload.data;
|
||||
const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
|
||||
return MCValue{ .memory = got_addr };
|
||||
} else {
|
||||
return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typed_value.val.tag() == .int_u64) {
|
||||
return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
|
||||
}
|
||||
return self.fail(src, "TODO codegen more kinds of const pointers", .{});
|
||||
if (typed_value.val.tag() == .int_u64) {
|
||||
return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
|
||||
}
|
||||
return self.fail(src, "TODO codegen more kinds of const pointers", .{});
|
||||
},
|
||||
},
|
||||
.Int => {
|
||||
const info = typed_value.ty.intInfo(self.target.*);
|
||||
@@ -4264,27 +4308,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
var next_stack_offset: u32 = 0;
|
||||
|
||||
for (param_types) |ty, i| {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Bool, .Int => {
|
||||
if (!ty.hasCodeGenBits()) {
|
||||
assert(cc != .C);
|
||||
result.args[i] = .{ .none = {} };
|
||||
} else {
|
||||
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
||||
if (next_int_reg >= c_abi_int_param_regs.len) {
|
||||
result.args[i] = .{ .stack_offset = next_stack_offset };
|
||||
next_stack_offset += param_size;
|
||||
} else {
|
||||
const aliased_reg = registerAlias(
|
||||
c_abi_int_param_regs[next_int_reg],
|
||||
param_size,
|
||||
);
|
||||
result.args[i] = .{ .register = aliased_reg };
|
||||
next_int_reg += 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}),
|
||||
if (!ty.hasCodeGenBits()) {
|
||||
assert(cc != .C);
|
||||
result.args[i] = .{ .none = {} };
|
||||
continue;
|
||||
}
|
||||
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
||||
const pass_in_reg = switch (ty.zigTypeTag()) {
|
||||
.Bool => true,
|
||||
.Int => param_size <= 8,
|
||||
.Pointer => ty.ptrSize() != .Slice,
|
||||
.Optional => ty.isPtrLikeOptional(),
|
||||
else => false,
|
||||
};
|
||||
if (pass_in_reg) {
|
||||
if (next_int_reg >= c_abi_int_param_regs.len) {
|
||||
result.args[i] = .{ .stack_offset = next_stack_offset };
|
||||
next_stack_offset += param_size;
|
||||
} else {
|
||||
const aliased_reg = registerAlias(
|
||||
c_abi_int_param_regs[next_int_reg],
|
||||
param_size,
|
||||
);
|
||||
result.args[i] = .{ .register = aliased_reg };
|
||||
next_int_reg += 1;
|
||||
}
|
||||
} else {
|
||||
// For simplicity of codegen, slices and other types are always pushed onto the stack.
|
||||
// TODO: look into optimizing this by passing things as registers sometimes,
|
||||
// such as ptr and len of slices as separate registers.
|
||||
// TODO: also we need to honor the C ABI for relevant types rather than passing on
|
||||
// the stack here.
|
||||
result.args[i] = .{ .stack_offset = next_stack_offset };
|
||||
next_stack_offset += param_size;
|
||||
}
|
||||
}
|
||||
result.stack_byte_count = next_stack_offset;
|
||||
|
||||
@@ -39,7 +39,12 @@ const BlockData = struct {
|
||||
};
|
||||
|
||||
pub const CValueMap = std.AutoHashMap(*Inst, CValue);
|
||||
pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.HashContext, std.hash_map.default_max_load_percentage);
|
||||
pub const TypedefMap = std.HashMap(
|
||||
Type,
|
||||
struct { name: []const u8, rendered: []u8 },
|
||||
Type.HashContext,
|
||||
std.hash_map.default_max_load_percentage,
|
||||
);
|
||||
|
||||
fn formatTypeAsCIdentifier(
|
||||
data: Type,
|
||||
@@ -151,14 +156,49 @@ pub const Object = struct {
|
||||
render_ty = render_ty.elemType();
|
||||
}
|
||||
|
||||
try o.dg.renderType(w, render_ty);
|
||||
if (render_ty.zigTypeTag() == .Fn) {
|
||||
const ret_ty = render_ty.fnReturnType();
|
||||
if (ret_ty.zigTypeTag() == .NoReturn) {
|
||||
// noreturn attribute is not allowed here.
|
||||
try w.writeAll("void");
|
||||
} else {
|
||||
try o.dg.renderType(w, ret_ty);
|
||||
}
|
||||
try w.writeAll(" (*");
|
||||
switch (mutability) {
|
||||
.Const => try w.writeAll("const "),
|
||||
.Mut => {},
|
||||
}
|
||||
try o.writeCValue(w, name);
|
||||
try w.writeAll(")(");
|
||||
const param_len = render_ty.fnParamLen();
|
||||
const is_var_args = render_ty.fnIsVarArgs();
|
||||
if (param_len == 0 and !is_var_args)
|
||||
try w.writeAll("void")
|
||||
else {
|
||||
var index: usize = 0;
|
||||
while (index < param_len) : (index += 1) {
|
||||
if (index > 0) {
|
||||
try w.writeAll(", ");
|
||||
}
|
||||
try o.dg.renderType(w, render_ty.fnParamType(index));
|
||||
}
|
||||
}
|
||||
if (is_var_args) {
|
||||
if (param_len != 0) try w.writeAll(", ");
|
||||
try w.writeAll("...");
|
||||
}
|
||||
try w.writeByte(')');
|
||||
} else {
|
||||
try o.dg.renderType(w, render_ty);
|
||||
|
||||
const const_prefix = switch (mutability) {
|
||||
.Const => "const ",
|
||||
.Mut => "",
|
||||
};
|
||||
try w.print(" {s}", .{const_prefix});
|
||||
try o.writeCValue(w, name);
|
||||
const const_prefix = switch (mutability) {
|
||||
.Const => "const ",
|
||||
.Mut => "",
|
||||
};
|
||||
try w.print(" {s}", .{const_prefix});
|
||||
try o.writeCValue(w, name);
|
||||
}
|
||||
try w.writeAll(suffix.items);
|
||||
}
|
||||
};
|
||||
@@ -196,35 +236,72 @@ pub const DeclGen = struct {
|
||||
return writer.print("{d}", .{val.toSignedInt()});
|
||||
return writer.print("{d}", .{val.toUnsignedInt()});
|
||||
},
|
||||
.Pointer => switch (val.tag()) {
|
||||
.null_value, .zero => try writer.writeAll("NULL"),
|
||||
.one => try writer.writeAll("1"),
|
||||
.decl_ref => {
|
||||
const decl = val.castTag(.decl_ref).?.data;
|
||||
.Pointer => switch (t.ptrSize()) {
|
||||
.Slice => {
|
||||
try writer.writeByte('(');
|
||||
try dg.renderType(writer, t);
|
||||
try writer.writeAll("){");
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
try dg.renderValue(writer, t.slicePtrFieldType(&buf), val);
|
||||
try writer.writeAll(", ");
|
||||
try writer.print("{d}", .{val.sliceLen()});
|
||||
try writer.writeAll("}");
|
||||
},
|
||||
else => switch (val.tag()) {
|
||||
.null_value, .zero => try writer.writeAll("NULL"),
|
||||
.one => try writer.writeAll("1"),
|
||||
.decl_ref => {
|
||||
const decl = val.castTag(.decl_ref).?.data;
|
||||
|
||||
// Determine if we must pointer cast.
|
||||
assert(decl.has_tv);
|
||||
if (t.eql(decl.ty)) {
|
||||
try writer.print("&{s}", .{decl.name});
|
||||
} else {
|
||||
try writer.writeAll("(");
|
||||
try dg.renderType(writer, t);
|
||||
try writer.print(")&{s}", .{decl.name});
|
||||
}
|
||||
// Determine if we must pointer cast.
|
||||
assert(decl.has_tv);
|
||||
if (t.eql(decl.ty)) {
|
||||
try writer.print("&{s}", .{decl.name});
|
||||
} else {
|
||||
try writer.writeAll("(");
|
||||
try dg.renderType(writer, t);
|
||||
try writer.print(")&{s}", .{decl.name});
|
||||
}
|
||||
},
|
||||
.function => {
|
||||
const func = val.castTag(.function).?.data;
|
||||
try writer.print("{s}", .{func.owner_decl.name});
|
||||
},
|
||||
.extern_fn => {
|
||||
const decl = val.castTag(.extern_fn).?.data;
|
||||
try writer.print("{s}", .{decl.name});
|
||||
},
|
||||
else => switch (t.ptrSize()) {
|
||||
.Slice => unreachable,
|
||||
.Many => {
|
||||
if (val.castTag(.ref_val)) |ref_val_payload| {
|
||||
const sub_val = ref_val_payload.data;
|
||||
if (sub_val.castTag(.bytes)) |bytes_payload| {
|
||||
const bytes = bytes_payload.data;
|
||||
try writer.writeByte('(');
|
||||
try dg.renderType(writer, t);
|
||||
// TODO: make our own C string escape instead of using std.zig.fmtEscapes
|
||||
try writer.print(")\"{}\"", .{std.zig.fmtEscapes(bytes)});
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
},
|
||||
.One => {
|
||||
var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
|
||||
defer arena.deinit();
|
||||
|
||||
const elem_ty = t.elemType();
|
||||
const elem_val = try val.pointerDeref(&arena.allocator);
|
||||
|
||||
try writer.writeAll("&");
|
||||
try dg.renderValue(writer, elem_ty, elem_val);
|
||||
},
|
||||
.C => unreachable,
|
||||
},
|
||||
},
|
||||
.function => {
|
||||
const func = val.castTag(.function).?.data;
|
||||
try writer.print("{s}", .{func.owner_decl.name});
|
||||
},
|
||||
.extern_fn => {
|
||||
const decl = val.castTag(.extern_fn).?.data;
|
||||
try writer.print("{s}", .{decl.name});
|
||||
},
|
||||
else => |e| return dg.fail(
|
||||
.{ .node_offset = 0 },
|
||||
"TODO: C backend: implement Pointer value {s}",
|
||||
.{@tagName(e)},
|
||||
),
|
||||
},
|
||||
.Array => {
|
||||
// First try specific tag representations for more efficiency.
|
||||
@@ -283,6 +360,12 @@ pub const DeclGen = struct {
|
||||
const error_type = t.errorUnionSet();
|
||||
const payload_type = t.errorUnionChild();
|
||||
const data = val.castTag(.error_union).?.data;
|
||||
|
||||
if (!payload_type.hasCodeGenBits()) {
|
||||
// We use the error type directly as the type.
|
||||
return dg.renderValue(writer, error_type, data);
|
||||
}
|
||||
|
||||
try writer.writeByte('(');
|
||||
try dg.renderType(writer, t);
|
||||
try writer.writeAll("){");
|
||||
@@ -329,6 +412,32 @@ pub const DeclGen = struct {
|
||||
},
|
||||
}
|
||||
},
|
||||
.Fn => switch (val.tag()) {
|
||||
.null_value, .zero => try writer.writeAll("NULL"),
|
||||
.one => try writer.writeAll("1"),
|
||||
.decl_ref => {
|
||||
const decl = val.castTag(.decl_ref).?.data;
|
||||
|
||||
// Determine if we must pointer cast.
|
||||
assert(decl.has_tv);
|
||||
if (t.eql(decl.ty)) {
|
||||
try writer.print("&{s}", .{decl.name});
|
||||
} else {
|
||||
try writer.writeAll("(");
|
||||
try dg.renderType(writer, t);
|
||||
try writer.print(")&{s}", .{decl.name});
|
||||
}
|
||||
},
|
||||
.function => {
|
||||
const func = val.castTag(.function).?.data;
|
||||
try writer.print("{s}", .{func.owner_decl.name});
|
||||
},
|
||||
.extern_fn => {
|
||||
const decl = val.castTag(.extern_fn).?.data;
|
||||
try writer.print("{s}", .{decl.name});
|
||||
},
|
||||
else => unreachable,
|
||||
},
|
||||
else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{
|
||||
@tagName(e),
|
||||
}),
|
||||
@@ -339,6 +448,12 @@ pub const DeclGen = struct {
|
||||
if (!is_global) {
|
||||
try w.writeAll("static ");
|
||||
}
|
||||
if (dg.decl.val.castTag(.function)) |func_payload| {
|
||||
const func: *Module.Fn = func_payload.data;
|
||||
if (func.is_cold) {
|
||||
try w.writeAll("ZIG_COLD ");
|
||||
}
|
||||
}
|
||||
try dg.renderType(w, dg.decl.ty.fnReturnType());
|
||||
const decl_name = mem.span(dg.decl.name);
|
||||
try w.print(" {s}(", .{decl_name});
|
||||
@@ -413,7 +528,35 @@ pub const DeclGen = struct {
|
||||
|
||||
.Pointer => {
|
||||
if (t.isSlice()) {
|
||||
return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{});
|
||||
if (dg.typedefs.get(t)) |some| {
|
||||
return w.writeAll(some.name);
|
||||
}
|
||||
|
||||
var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
|
||||
defer buffer.deinit();
|
||||
const bw = buffer.writer();
|
||||
|
||||
try bw.writeAll("typedef struct { ");
|
||||
const elem_type = t.elemType();
|
||||
try dg.renderType(bw, elem_type);
|
||||
try bw.writeAll(" *");
|
||||
if (t.isConstPtr()) {
|
||||
try bw.writeAll("const ");
|
||||
}
|
||||
if (t.isVolatilePtr()) {
|
||||
try bw.writeAll("volatile ");
|
||||
}
|
||||
try bw.writeAll("ptr; size_t len; } ");
|
||||
const name_index = buffer.items.len;
|
||||
try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
|
||||
|
||||
const rendered = buffer.toOwnedSlice();
|
||||
errdefer dg.typedefs.allocator.free(rendered);
|
||||
const name = rendered[name_index .. rendered.len - 2];
|
||||
|
||||
try dg.typedefs.ensureUnusedCapacity(1);
|
||||
try w.writeAll(name);
|
||||
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
|
||||
} else {
|
||||
try dg.renderType(w, t.elemType());
|
||||
try w.writeAll(" *");
|
||||
@@ -446,13 +589,13 @@ pub const DeclGen = struct {
|
||||
try dg.renderType(bw, child_type);
|
||||
try bw.writeAll(" payload; bool is_null; } ");
|
||||
const name_index = buffer.items.len;
|
||||
try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)});
|
||||
try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
|
||||
|
||||
const rendered = buffer.toOwnedSlice();
|
||||
errdefer dg.typedefs.allocator.free(rendered);
|
||||
const name = rendered[name_index .. rendered.len - 2];
|
||||
|
||||
try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
|
||||
try dg.typedefs.ensureUnusedCapacity(1);
|
||||
try w.writeAll(name);
|
||||
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
|
||||
},
|
||||
@@ -465,7 +608,11 @@ pub const DeclGen = struct {
|
||||
return w.writeAll(some.name);
|
||||
}
|
||||
const child_type = t.errorUnionChild();
|
||||
const set_type = t.errorUnionSet();
|
||||
const err_set_type = t.errorUnionSet();
|
||||
|
||||
if (!child_type.hasCodeGenBits()) {
|
||||
return dg.renderType(w, err_set_type);
|
||||
}
|
||||
|
||||
var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
|
||||
defer buffer.deinit();
|
||||
@@ -475,13 +622,20 @@ pub const DeclGen = struct {
|
||||
try dg.renderType(bw, child_type);
|
||||
try bw.writeAll(" payload; uint16_t error; } ");
|
||||
const name_index = buffer.items.len;
|
||||
try bw.print("zig_err_union_{s}_{s}_t;\n", .{ typeToCIdentifier(set_type), typeToCIdentifier(child_type) });
|
||||
if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
|
||||
const func = inf_err_set_payload.data.func;
|
||||
try bw.print("zig_E_{s};\n", .{func.owner_decl.name});
|
||||
} else {
|
||||
try bw.print("zig_E_{s}_{s};\n", .{
|
||||
typeToCIdentifier(err_set_type), typeToCIdentifier(child_type),
|
||||
});
|
||||
}
|
||||
|
||||
const rendered = buffer.toOwnedSlice();
|
||||
errdefer dg.typedefs.allocator.free(rendered);
|
||||
const name = rendered[name_index .. rendered.len - 2];
|
||||
|
||||
try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
|
||||
try dg.typedefs.ensureUnusedCapacity(1);
|
||||
try w.writeAll(name);
|
||||
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
|
||||
},
|
||||
@@ -514,7 +668,7 @@ pub const DeclGen = struct {
|
||||
errdefer dg.typedefs.allocator.free(rendered);
|
||||
const name = rendered[name_start .. rendered.len - 2];
|
||||
|
||||
try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
|
||||
try dg.typedefs.ensureUnusedCapacity(1);
|
||||
try w.writeAll(name);
|
||||
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
|
||||
},
|
||||
@@ -526,7 +680,28 @@ pub const DeclGen = struct {
|
||||
try dg.renderType(w, int_tag_ty);
|
||||
},
|
||||
.Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}),
|
||||
.Fn => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Fn", .{}),
|
||||
.Fn => {
|
||||
try dg.renderType(w, t.fnReturnType());
|
||||
try w.writeAll(" (*)(");
|
||||
const param_len = t.fnParamLen();
|
||||
const is_var_args = t.fnIsVarArgs();
|
||||
if (param_len == 0 and !is_var_args)
|
||||
try w.writeAll("void")
|
||||
else {
|
||||
var index: usize = 0;
|
||||
while (index < param_len) : (index += 1) {
|
||||
if (index > 0) {
|
||||
try w.writeAll(", ");
|
||||
}
|
||||
try dg.renderType(w, t.fnParamType(index));
|
||||
}
|
||||
}
|
||||
if (is_var_args) {
|
||||
if (param_len != 0) try w.writeAll(", ");
|
||||
try w.writeAll("...");
|
||||
}
|
||||
try w.writeByte(')');
|
||||
},
|
||||
.Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}),
|
||||
.Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}),
|
||||
.AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}),
|
||||
@@ -569,23 +744,27 @@ pub fn genDecl(o: *Object) !void {
|
||||
.val = o.dg.decl.val,
|
||||
};
|
||||
if (tv.val.castTag(.function)) |func_payload| {
|
||||
const is_global = o.dg.declIsGlobal(tv);
|
||||
const fwd_decl_writer = o.dg.fwd_decl.writer();
|
||||
if (is_global) {
|
||||
try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
|
||||
}
|
||||
try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
|
||||
try fwd_decl_writer.writeAll(";\n");
|
||||
|
||||
const func: *Module.Fn = func_payload.data;
|
||||
try o.indent_writer.insertNewline();
|
||||
try o.dg.renderFunctionSignature(o.writer(), is_global);
|
||||
if (func.owner_decl == o.dg.decl) {
|
||||
const is_global = o.dg.declIsGlobal(tv);
|
||||
const fwd_decl_writer = o.dg.fwd_decl.writer();
|
||||
if (is_global) {
|
||||
try fwd_decl_writer.writeAll("ZIG_EXTERN_C ");
|
||||
}
|
||||
try o.dg.renderFunctionSignature(fwd_decl_writer, is_global);
|
||||
try fwd_decl_writer.writeAll(";\n");
|
||||
|
||||
try o.writer().writeByte(' ');
|
||||
try genBody(o, func.body);
|
||||
try o.indent_writer.insertNewline();
|
||||
try o.dg.renderFunctionSignature(o.writer(), is_global);
|
||||
|
||||
try o.indent_writer.insertNewline();
|
||||
} else if (tv.val.tag() == .extern_fn) {
|
||||
try o.writer().writeByte(' ');
|
||||
try genBody(o, func.body);
|
||||
|
||||
try o.indent_writer.insertNewline();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (tv.val.tag() == .extern_fn) {
|
||||
const writer = o.writer();
|
||||
try writer.writeAll("ZIG_EXTERN_C ");
|
||||
try o.dg.renderFunctionSignature(writer, true);
|
||||
@@ -644,9 +823,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
|
||||
const is_global = dg.declIsGlobal(tv);
|
||||
if (is_global) {
|
||||
try writer.writeAll("ZIG_EXTERN_C ");
|
||||
try dg.renderFunctionSignature(writer, is_global);
|
||||
try dg.fwd_decl.appendSlice(";\n");
|
||||
}
|
||||
try dg.renderFunctionSignature(writer, is_global);
|
||||
try dg.fwd_decl.appendSlice(";\n");
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@@ -726,8 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
|
||||
.ref => try genRef(o, inst.castTag(.ref).?),
|
||||
.struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
|
||||
|
||||
.is_err => try genIsErr(o, inst.castTag(.is_err).?),
|
||||
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
|
||||
.is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="),
|
||||
.is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="),
|
||||
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="),
|
||||
.is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="),
|
||||
|
||||
.unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
|
||||
.unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
|
||||
@@ -1213,9 +1394,25 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
|
||||
|
||||
// *(E!T) -> E NOT *E
|
||||
fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
if (inst.base.isUnused())
|
||||
return CValue.none;
|
||||
|
||||
const writer = o.writer();
|
||||
const operand = try o.resolveInst(inst.operand);
|
||||
|
||||
const payload_ty = inst.operand.ty.errorUnionChild();
|
||||
if (!payload_ty.hasCodeGenBits()) {
|
||||
if (inst.operand.ty.zigTypeTag() == .Pointer) {
|
||||
const local = try o.allocLocal(inst.base.ty, .Const);
|
||||
try writer.writeAll(" = *");
|
||||
try o.writeCValue(writer, operand);
|
||||
try writer.writeAll(";\n");
|
||||
return local;
|
||||
} else {
|
||||
return operand;
|
||||
}
|
||||
}
|
||||
|
||||
const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
|
||||
|
||||
const local = try o.allocLocal(inst.base.ty, .Const);
|
||||
@@ -1225,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
try writer.print("){s}error;\n", .{maybe_deref});
|
||||
return local;
|
||||
}
|
||||
|
||||
fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
if (inst.base.isUnused())
|
||||
return CValue.none;
|
||||
|
||||
const writer = o.writer();
|
||||
const operand = try o.resolveInst(inst.operand);
|
||||
|
||||
const payload_ty = inst.operand.ty.errorUnionChild();
|
||||
if (!payload_ty.hasCodeGenBits()) {
|
||||
return CValue.none;
|
||||
}
|
||||
|
||||
const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
|
||||
const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";
|
||||
|
||||
@@ -1277,15 +1483,26 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
return local;
|
||||
}
|
||||
|
||||
fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
fn genIsErr(
|
||||
o: *Object,
|
||||
inst: *Inst.UnOp,
|
||||
deref_prefix: [*:0]const u8,
|
||||
deref_suffix: [*:0]const u8,
|
||||
op_str: [*:0]const u8,
|
||||
) !CValue {
|
||||
const writer = o.writer();
|
||||
const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else "";
|
||||
const operand = try o.resolveInst(inst.operand);
|
||||
|
||||
const local = try o.allocLocal(Type.initTag(.bool), .Const);
|
||||
try writer.writeAll(" = (");
|
||||
try o.writeCValue(writer, operand);
|
||||
try writer.print("){s}.error != 0;\n", .{maybe_deref});
|
||||
const payload_ty = inst.operand.ty.errorUnionChild();
|
||||
if (!payload_ty.hasCodeGenBits()) {
|
||||
try writer.print(" = {s}", .{deref_prefix});
|
||||
try o.writeCValue(writer, operand);
|
||||
try writer.print(" {s} 0;\n", .{op_str});
|
||||
} else {
|
||||
try writer.writeAll(" = ");
|
||||
try o.writeCValue(writer, operand);
|
||||
try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str });
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
|
||||
@@ -814,7 +814,8 @@ pub const Context = struct {
|
||||
.constant => unreachable,
|
||||
.dbg_stmt => WValue.none,
|
||||
.div => self.genBinOp(inst.castTag(.div).?, .div),
|
||||
.is_err => self.genIsErr(inst.castTag(.is_err).?),
|
||||
.is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),
|
||||
.is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),
|
||||
.load => self.genLoad(inst.castTag(.load).?),
|
||||
.loop => self.genLoop(inst.castTag(.loop).?),
|
||||
.mul => self.genBinOp(inst.castTag(.mul).?, .mul),
|
||||
@@ -1278,7 +1279,7 @@ pub const Context = struct {
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
|
||||
fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue {
|
||||
const operand = self.resolveInst(inst.operand);
|
||||
const offset = self.code.items.len;
|
||||
const writer = self.code.writer();
|
||||
@@ -1289,9 +1290,7 @@ pub const Context = struct {
|
||||
try writer.writeByte(wasm.opcode(.i32_const));
|
||||
try leb.writeILEB128(writer, @as(i32, 0));
|
||||
|
||||
// we want to break out of the condition if they're *not* equal,
|
||||
// because that means there's an error.
|
||||
try writer.writeByte(wasm.opcode(.i32_ne));
|
||||
try writer.writeByte(@enumToInt(opcode));
|
||||
|
||||
return WValue{ .code_offset = offset };
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
|
||||
}
|
||||
|
||||
var fn_count: usize = 0;
|
||||
var typedefs = std.HashMap(Type, []const u8, Type.HashContext, std.hash_map.default_max_load_percentage).init(comp.gpa);
|
||||
var typedefs = std.HashMap(Type, void, Type.HashContext, std.hash_map.default_max_load_percentage).init(comp.gpa);
|
||||
defer typedefs.deinit();
|
||||
|
||||
// Typedefs, forward decls and non-functions first.
|
||||
@@ -217,14 +217,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
|
||||
if (!decl.has_tv) continue;
|
||||
const buf = buf: {
|
||||
if (decl.val.castTag(.function)) |_| {
|
||||
try typedefs.ensureUnusedCapacity(decl.fn_link.c.typedefs.count());
|
||||
var it = decl.fn_link.c.typedefs.iterator();
|
||||
while (it.next()) |new| {
|
||||
if (typedefs.get(new.key_ptr.*)) |previous| {
|
||||
try err_typedef_writer.print("typedef {s} {s};\n", .{ previous, new.value_ptr.name });
|
||||
} else {
|
||||
try typedefs.ensureCapacity(typedefs.capacity() + 1);
|
||||
const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*);
|
||||
if (!gop.found_existing) {
|
||||
try err_typedef_writer.writeAll(new.value_ptr.rendered);
|
||||
typedefs.putAssumeCapacityNoClobber(new.key_ptr.*, new.value_ptr.name);
|
||||
}
|
||||
}
|
||||
fn_count += 1;
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
#define zig_threadlocal zig_threadlocal_unavailable
|
||||
#endif
|
||||
|
||||
#if __GNUC__
|
||||
#define ZIG_COLD __attribute__ ((cold))
|
||||
#else
|
||||
#define ZIG_COLD
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ >= 199901L
|
||||
#define ZIG_RESTRICT restrict
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
@@ -2505,11 +2505,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
|
||||
abbrev_base_type,
|
||||
DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
|
||||
1, // DW.AT_byte_size, DW.FORM_data1
|
||||
'b',
|
||||
'o',
|
||||
'o',
|
||||
'l',
|
||||
0, // DW.AT_name, DW.FORM_string
|
||||
'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string
|
||||
});
|
||||
},
|
||||
.Int => {
|
||||
@@ -2526,8 +2522,23 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
|
||||
// DW.AT_name, DW.FORM_string
|
||||
try dbg_info_buffer.writer().print("{}\x00", .{ty});
|
||||
},
|
||||
.Optional => {
|
||||
if (ty.isPtrLikeOptional()) {
|
||||
try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
|
||||
dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
|
||||
// DW.AT_encoding, DW.FORM_data1
|
||||
dbg_info_buffer.appendAssumeCapacity(DW.ATE_address);
|
||||
// DW.AT_byte_size, DW.FORM_data1
|
||||
dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
|
||||
// DW.AT_name, DW.FORM_string
|
||||
try dbg_info_buffer.writer().print("{}\x00", .{ty});
|
||||
} else {
|
||||
log.err("TODO implement .debug_info for type '{}'", .{ty});
|
||||
try dbg_info_buffer.append(abbrev_pad1);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
|
||||
log.err("TODO implement .debug_info for type '{}'", .{ty});
|
||||
try dbg_info_buffer.append(abbrev_pad1);
|
||||
},
|
||||
}
|
||||
|
||||
85
src/type.zig
85
src/type.zig
@@ -58,7 +58,7 @@ pub const Type = extern union {
|
||||
.bool => return .Bool,
|
||||
.void => return .Void,
|
||||
.type => return .Type,
|
||||
.error_set, .error_set_single, .anyerror => return .ErrorSet,
|
||||
.error_set, .error_set_single, .anyerror, .error_set_inferred => return .ErrorSet,
|
||||
.comptime_int => return .ComptimeInt,
|
||||
.comptime_float => return .ComptimeFloat,
|
||||
.noreturn => return .NoReturn,
|
||||
@@ -689,7 +689,15 @@ pub const Type = extern union {
|
||||
.optional_single_mut_pointer,
|
||||
.optional_single_const_pointer,
|
||||
.anyframe_T,
|
||||
=> return self.copyPayloadShallow(allocator, Payload.ElemType),
|
||||
=> {
|
||||
const payload = self.cast(Payload.ElemType).?;
|
||||
const new_payload = try allocator.create(Payload.ElemType);
|
||||
new_payload.* = .{
|
||||
.base = .{ .tag = payload.base.tag },
|
||||
.data = try payload.data.copy(allocator),
|
||||
};
|
||||
return Type{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
|
||||
.int_signed,
|
||||
.int_unsigned,
|
||||
@@ -756,6 +764,7 @@ pub const Type = extern union {
|
||||
});
|
||||
},
|
||||
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
|
||||
.error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred),
|
||||
.error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
|
||||
.empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
|
||||
.@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
|
||||
@@ -1031,6 +1040,10 @@ pub const Type = extern union {
|
||||
const error_set = ty.castTag(.error_set).?.data;
|
||||
return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name));
|
||||
},
|
||||
.error_set_inferred => {
|
||||
const func = ty.castTag(.error_set_inferred).?.data.func;
|
||||
return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
|
||||
},
|
||||
.error_set_single => {
|
||||
const name = ty.castTag(.error_set_single).?.data;
|
||||
return writer.print("error{{{s}}}", .{name});
|
||||
@@ -1144,6 +1157,7 @@ pub const Type = extern union {
|
||||
.anyerror_void_error_union,
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.error_set_inferred,
|
||||
.manyptr_u8,
|
||||
.manyptr_const_u8,
|
||||
.atomic_ordering,
|
||||
@@ -1161,6 +1175,9 @@ pub const Type = extern union {
|
||||
.@"struct" => {
|
||||
// TODO introduce lazy value mechanism
|
||||
const struct_obj = self.castTag(.@"struct").?.data;
|
||||
assert(struct_obj.status == .have_field_types or
|
||||
struct_obj.status == .layout_wip or
|
||||
struct_obj.status == .have_layout);
|
||||
for (struct_obj.fields.values()) |value| {
|
||||
if (value.ty.hasCodeGenBits())
|
||||
return true;
|
||||
@@ -1348,6 +1365,7 @@ pub const Type = extern union {
|
||||
.error_set_single,
|
||||
.anyerror_void_error_union,
|
||||
.anyerror,
|
||||
.error_set_inferred,
|
||||
=> return 2, // TODO revisit this when we have the concept of the error tag type
|
||||
|
||||
.array, .array_sentinel => return self.elemType().abiAlignment(target),
|
||||
@@ -1580,6 +1598,7 @@ pub const Type = extern union {
|
||||
.error_set_single,
|
||||
.anyerror_void_error_union,
|
||||
.anyerror,
|
||||
.error_set_inferred,
|
||||
=> return 2, // TODO revisit this when we have the concept of the error tag type
|
||||
|
||||
.int_signed, .int_unsigned => {
|
||||
@@ -1744,6 +1763,7 @@ pub const Type = extern union {
|
||||
.error_set_single,
|
||||
.anyerror_void_error_union,
|
||||
.anyerror,
|
||||
.error_set_inferred,
|
||||
=> return 16, // TODO revisit this when we have the concept of the error tag type
|
||||
|
||||
.int_signed, .int_unsigned => self.cast(Payload.Bits).?.data,
|
||||
@@ -1863,6 +1883,48 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn slicePtrFieldType(self: Type, buffer: *Payload.ElemType) Type {
|
||||
switch (self.tag()) {
|
||||
.const_slice_u8 => return Type.initTag(.manyptr_const_u8),
|
||||
|
||||
.const_slice => {
|
||||
const elem_type = self.castTag(.const_slice).?.data;
|
||||
buffer.* = .{
|
||||
.base = .{ .tag = .many_const_pointer },
|
||||
.data = elem_type,
|
||||
};
|
||||
return Type.initPayload(&buffer.base);
|
||||
},
|
||||
.mut_slice => {
|
||||
const elem_type = self.castTag(.mut_slice).?.data;
|
||||
buffer.* = .{
|
||||
.base = .{ .tag = .many_mut_pointer },
|
||||
.data = elem_type,
|
||||
};
|
||||
return Type.initPayload(&buffer.base);
|
||||
},
|
||||
|
||||
.pointer => {
|
||||
const payload = self.castTag(.pointer).?.data;
|
||||
assert(payload.size == .Slice);
|
||||
if (payload.mutable) {
|
||||
buffer.* = .{
|
||||
.base = .{ .tag = .many_mut_pointer },
|
||||
.data = payload.pointee_type,
|
||||
};
|
||||
} else {
|
||||
buffer.* = .{
|
||||
.base = .{ .tag = .many_const_pointer },
|
||||
.data = payload.pointee_type,
|
||||
};
|
||||
}
|
||||
return Type.initPayload(&buffer.base);
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isConstPtr(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.single_const_pointer,
|
||||
@@ -1915,7 +1977,10 @@ pub const Type = extern union {
|
||||
/// Asserts that the type is an optional
|
||||
pub fn isPtrLikeOptional(self: Type) bool {
|
||||
switch (self.tag()) {
|
||||
.optional_single_const_pointer, .optional_single_mut_pointer => return true,
|
||||
.optional_single_const_pointer,
|
||||
.optional_single_mut_pointer,
|
||||
=> return true,
|
||||
|
||||
.optional => {
|
||||
var buf: Payload.ElemType = undefined;
|
||||
const child_type = self.optionalChild(&buf);
|
||||
@@ -2400,6 +2465,7 @@ pub const Type = extern union {
|
||||
.error_union,
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.error_set_inferred,
|
||||
.@"opaque",
|
||||
.var_args_param,
|
||||
.manyptr_u8,
|
||||
@@ -2892,6 +2958,8 @@ pub const Type = extern union {
|
||||
anyframe_T,
|
||||
error_set,
|
||||
error_set_single,
|
||||
/// The type is the inferred error set of a specific function.
|
||||
error_set_inferred,
|
||||
empty_struct,
|
||||
@"opaque",
|
||||
@"struct",
|
||||
@@ -2989,6 +3057,7 @@ pub const Type = extern union {
|
||||
=> Payload.Bits,
|
||||
|
||||
.error_set => Payload.ErrorSet,
|
||||
.error_set_inferred => Payload.ErrorSetInferred,
|
||||
|
||||
.array, .vector => Payload.Array,
|
||||
.array_sentinel => Payload.ArraySentinel,
|
||||
@@ -3081,6 +3150,16 @@ pub const Type = extern union {
|
||||
data: *Module.ErrorSet,
|
||||
};
|
||||
|
||||
pub const ErrorSetInferred = struct {
|
||||
pub const base_tag = Tag.error_set_inferred;
|
||||
|
||||
base: Payload = Payload{ .tag = base_tag },
|
||||
data: struct {
|
||||
func: *Module.Fn,
|
||||
map: std.StringHashMapUnmanaged(void),
|
||||
},
|
||||
};
|
||||
|
||||
pub const Pointer = struct {
|
||||
pub const base_tag = Tag.pointer;
|
||||
|
||||
|
||||
@@ -483,13 +483,13 @@ pub const Value = extern union {
|
||||
/// TODO this should become a debug dump() function. In order to print values in a meaningful way
|
||||
/// we also need access to the type.
|
||||
pub fn format(
|
||||
self: Value,
|
||||
start_val: Value,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
out_stream: anytype,
|
||||
) !void {
|
||||
comptime assert(fmt.len == 0);
|
||||
var val = self;
|
||||
var val = start_val;
|
||||
while (true) switch (val.tag()) {
|
||||
.u8_type => return out_stream.writeAll("u8"),
|
||||
.i8_type => return out_stream.writeAll("i8"),
|
||||
@@ -598,9 +598,9 @@ pub const Value = extern union {
|
||||
val = field_ptr.container_ptr;
|
||||
},
|
||||
.empty_array => return out_stream.writeAll(".{}"),
|
||||
.enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(self.castTag(.enum_literal).?.data)}),
|
||||
.enum_field_index => return out_stream.print("(enum field {d})", .{self.castTag(.enum_field_index).?.data}),
|
||||
.bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(self.castTag(.bytes).?.data)}),
|
||||
.enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}),
|
||||
.enum_field_index => return out_stream.print("(enum field {d})", .{val.castTag(.enum_field_index).?.data}),
|
||||
.bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),
|
||||
.repeated => {
|
||||
try out_stream.writeAll("(repeated) ");
|
||||
val = val.castTag(.repeated).?.data;
|
||||
@@ -1336,6 +1336,23 @@ pub const Value = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sliceLen(val: Value) u64 {
|
||||
return switch (val.tag()) {
|
||||
.empty_array => 0,
|
||||
.bytes => val.castTag(.bytes).?.data.len,
|
||||
.ref_val => sliceLen(val.castTag(.ref_val).?.data),
|
||||
.decl_ref => {
|
||||
const decl = val.castTag(.decl_ref).?.data;
|
||||
if (decl.ty.zigTypeTag() == .Array) {
|
||||
return decl.ty.arrayLen();
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the value is a single-item pointer to an array, or an array,
|
||||
/// or an unknown-length pointer, and returns the element value at the index.
|
||||
pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
|
||||
|
||||
@@ -804,19 +804,26 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
});
|
||||
}
|
||||
|
||||
ctx.c("empty start function", linux_x64,
|
||||
\\export fn _start() noreturn {
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
\\ZIG_EXTERN_C zig_noreturn void _start(void);
|
||||
\\
|
||||
\\zig_noreturn void _start(void) {
|
||||
\\ zig_breakpoint();
|
||||
\\ zig_unreachable();
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
{
|
||||
var case = ctx.exeFromCompiledC("inferred error sets", .{});
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn main() c_int {
|
||||
\\ if (foo()) |_| {
|
||||
\\ @panic("test fail");
|
||||
\\ } else |err| {
|
||||
\\ if (err != error.ItBroke) {
|
||||
\\ @panic("test fail");
|
||||
\\ }
|
||||
\\ }
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\fn foo() !void {
|
||||
\\ return error.ItBroke;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
|
||||
ctx.h("simple header", linux_x64,
|
||||
\\export fn start() void{}
|
||||
,
|
||||
|
||||
@@ -587,8 +587,6 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
}
|
||||
|
||||
{
|
||||
// TODO implement Type equality comparison of error unions in SEMA
|
||||
// before we can incrementally compile functions with an error union as return type
|
||||
var case = ctx.exe("wasm error union part 2", wasi);
|
||||
|
||||
case.addCompareOutput(
|
||||
|
||||
Reference in New Issue
Block a user