diff options
| -rw-r--r-- | src/Module.zig | 29 | ||||
| -rw-r--r-- | src/Sema.zig | 64 | ||||
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 8 | ||||
| -rw-r--r-- | test/behavior/error.zig | 7 |
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 |
