aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Module.zig29
-rw-r--r--src/Sema.zig64
-rw-r--r--src/arch/wasm/CodeGen.zig8
-rw-r--r--test/behavior/error.zig7
4 files changed, 58 insertions, 50 deletions
diff --git a/src/Module.zig b/src/Module.zig
index ce93e091fd..3bc763103b 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -146,8 +146,6 @@ const MonomorphedFuncsSet = std.HashMapUnmanaged(
);
const MonomorphedFuncsContext = struct {
- target: Target,
-
pub fn eql(ctx: @This(), a: *Fn, b: *Fn) bool {
_ = ctx;
return a == b;
@@ -155,25 +153,8 @@ const MonomorphedFuncsContext = struct {
/// Must match `Sema.GenericCallAdapter.hash`.
pub fn hash(ctx: @This(), key: *Fn) u64 {
- var hasher = std.hash.Wyhash.init(0);
-
- // The generic function Decl is guaranteed to be the first dependency
- // of each of its instantiations.
- const generic_owner_decl = key.owner_decl.dependencies.keys()[0];
- const generic_func: *const Fn = generic_owner_decl.val.castTag(.function).?.data;
- std.hash.autoHash(&hasher, generic_func);
-
- // This logic must be kept in sync with the logic in `analyzeCall` that
- // computes the hash.
- const comptime_args = key.comptime_args.?;
- const generic_ty_info = generic_owner_decl.ty.fnInfo();
- for (generic_ty_info.param_types) |param_ty, i| {
- if (generic_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) {
- comptime_args[i].val.hash(param_ty, &hasher, ctx.target);
- }
- }
-
- return hasher.final();
+ _ = ctx;
+ return key.hash;
}
};
@@ -1427,6 +1408,12 @@ pub const Fn = struct {
/// determine param names rather than redundantly storing them here.
param_names: []const [:0]const u8,
+ /// Precomputed hash for monomorphed_funcs.
+ /// This is important because it may be accessed when resizing monomorphed_funcs
+ /// while this Fn has already been added to the set, but does not have the
+ /// owner_decl, comptime_args, or other fields populated yet.
+ hash: u64,
+
/// Relative to owner Decl.
lbrace_line: u32,
/// Relative to owner Decl.
diff --git a/src/Sema.zig b/src/Sema.zig
index 7ad90cdf09..9fc54f805b 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -4671,22 +4671,6 @@ const GenericCallAdapter = struct {
}
};
-const GenericRemoveAdapter = struct {
- precomputed_hash: u64,
-
- pub fn eql(ctx: @This(), adapted_key: *Module.Fn, other_key: *Module.Fn) bool {
- _ = ctx;
- return adapted_key == other_key;
- }
-
- /// The implementation of the hash is in semantic analysis of function calls, so
- /// that any errors when computing the hash can be properly reported.
- pub fn hash(ctx: @This(), adapted_key: *Module.Fn) u64 {
- _ = adapted_key;
- return ctx.precomputed_hash;
- }
-};
-
fn analyzeCall(
sema: *Sema,
block: *Block,
@@ -5200,15 +5184,15 @@ fn instantiateGenericCall(
.comptime_tvs = comptime_tvs,
.target = target,
};
- const gop = try mod.monomorphed_funcs.getOrPutContextAdapted(gpa, {}, adapter, .{ .target = target });
+ const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
const callee = if (!gop.found_existing) callee: {
const new_module_func = try gpa.create(Module.Fn);
+ // This ensures that we can operate on the hash map before the Module.Fn
+ // struct is fully initialized.
+ new_module_func.hash = precomputed_hash;
gop.key_ptr.* = new_module_func;
errdefer gpa.destroy(new_module_func);
- const remove_adapter: GenericRemoveAdapter = .{
- .precomputed_hash = precomputed_hash,
- };
- errdefer assert(mod.monomorphed_funcs.removeAdapted(new_module_func, remove_adapter));
+ errdefer assert(mod.monomorphed_funcs.remove(new_module_func));
try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
@@ -6494,12 +6478,14 @@ fn funcCommon(
param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name);
}
+ const hash = new_func.hash;
const fn_payload = try sema.arena.create(Value.Payload.Function);
new_func.* = .{
.state = anal_state,
.zir_body_inst = func_inst,
.owner_decl = sema.owner_decl,
.comptime_args = comptime_args,
+ .hash = hash,
.lbrace_line = src_locs.lbrace_line,
.rbrace_line = src_locs.rbrace_line,
.lbrace_column = @truncate(u16, src_locs.columns),
@@ -19987,18 +19973,39 @@ fn analyzeIsNonErr(
if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
assert(ot == .ErrorUnion);
+ if (Air.refToIndex(operand)) |operand_inst| {
+ const air_tags = sema.air_instructions.items(.tag);
+ if (air_tags[operand_inst] == .wrap_errunion_payload) {
+ return Air.Inst.Ref.bool_true;
+ }
+ }
+
+ const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand);
+
// exception if the error union error set is known to be empty,
// we allow the comparison but always make it comptime known.
const set_ty = operand_ty.errorUnionSet();
switch (set_ty.tag()) {
- .anyerror, .error_set_inferred => {},
+ .anyerror => {},
+ .error_set_inferred => blk: {
+ // If the error set is empty, we must return a comptime true or false.
+ // However we want to avoid unnecessarily resolving an inferred error set
+ // in case it is already non-empty.
+ const ies = set_ty.castTag(.error_set_inferred).?.data;
+ if (ies.is_anyerror) break :blk;
+ if (ies.errors.count() != 0) break :blk;
+ if (maybe_operand_val == null) {
+ try sema.resolveInferredErrorSet(block, src, ies);
+ if (ies.is_anyerror) break :blk;
+ if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true;
+ }
+ },
else => if (set_ty.errorSetNames().len == 0) return Air.Inst.Ref.bool_true,
}
- const result_ty = Type.bool;
- if (try sema.resolveMaybeUndefVal(block, src, operand)) |err_union| {
+ if (maybe_operand_val) |err_union| {
if (err_union.isUndef()) {
- return sema.addConstUndef(result_ty);
+ return sema.addConstUndef(Type.bool);
}
if (err_union.getError() == null) {
return Air.Inst.Ref.bool_true;
@@ -20583,6 +20590,7 @@ fn wrapErrorUnionPayload(
return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
}
try sema.requireRuntimeBlock(block, inst_src);
+ try sema.queueFullTypeResolution(dest_payload_ty);
return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
}
@@ -21372,6 +21380,9 @@ fn resolveStructFully(
try sema.resolveTypeFully(block, src, field.ty);
}
struct_obj.status = .fully_resolved;
+
+ // And let's not forget comptime-only status.
+ _ = try sema.typeRequiresComptime(block, src, ty);
}
fn resolveUnionFully(
@@ -21395,6 +21406,9 @@ fn resolveUnionFully(
try sema.resolveTypeFully(block, src, field.ty);
}
union_obj.status = .fully_resolved;
+
+ // And let's not forget comptime-only status.
+ _ = try sema.typeRequiresComptime(block, src, ty);
}
pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type {
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index 024a94aaf4..5a191c5276 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -2627,12 +2627,12 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const op_ty = self.air.typeOf(ty_op.operand);
if (!op_ty.hasRuntimeBitsIgnoreComptime()) return operand;
- const err_ty = self.air.getRefType(ty_op.ty);
- const err_align = err_ty.abiAlignment(self.target);
- const set_size = err_ty.errorUnionSet().abiSize(self.target);
+ const err_union_ty = self.air.getRefType(ty_op.ty);
+ const err_align = err_union_ty.abiAlignment(self.target);
+ const set_size = err_union_ty.errorUnionSet().abiSize(self.target);
const offset = mem.alignForwardGeneric(u64, set_size, err_align);
- const err_union = try self.allocStack(err_ty);
+ const err_union = try self.allocStack(err_union_ty);
const payload_ptr = try self.buildPointerOffset(err_union, offset, .new);
try self.store(payload_ptr, operand, op_ty, 0);
diff --git a/test/behavior/error.zig b/test/behavior/error.zig
index 11146ac9ca..3030ea67ed 100644
--- a/test/behavior/error.zig
+++ b/test/behavior/error.zig
@@ -251,6 +251,13 @@ fn testErrToIntWithOnePossibleValue(
}
}
+test "inferred empty error set comptime catch" {
+ const S = struct {
+ fn foo() !void {}
+ };
+ S.foo() catch @compileError("fail");
+}
+
test "error union peer type resolution" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO