authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-30 00:47:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-30 00:47:55-07:00
logc21f046a8b019c42aa0dbb0fb9c592b590edf977
tree96c7f602f08b00c9cabdd699f8f678de215e50c5
parent05947ea870f95ab90a75e174079492f546baaf72

Sema: enhance is_non_err to be comptime more often

* Sema: store the precomputed monomorphed_funcs hash inside Module.Fn. 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. * Sema: in `analyzeIsNonErr`, take advantage of the AIR tag being `wrap_errunion_payload` to infer that `is_non_err` is comptime true without performing any error set resolution. - Also add some code to check for empty inferred error sets in this function. If necessary we do resolve the inferred error set. * Sema: queue full type resolution of payload type when `wrap_errunion_payload` AIR instruction is emitted. This ensures the backend may check the alignment of it. * Sema: resolveTypeFully now additionally resolves comptime-only status. closes #11306

4 files changed, 58 insertions(+), 50 deletions(-)

src/Module.zig+8-21
......@@ -146,8 +146,6 @@ const MonomorphedFuncsSet = std.HashMapUnmanaged(
146146);
147147
148148const MonomorphedFuncsContext = struct {
149 target: Target,
150
151149 pub fn eql(ctx: @This(), a: *Fn, b: *Fn) bool {
152150 _ = ctx;
153151 return a == b;
......@@ -155,25 +153,8 @@ const MonomorphedFuncsContext = struct {
155153
156154 /// Must match `Sema.GenericCallAdapter.hash`.
157155 pub fn hash(ctx: @This(), key: *Fn) u64 {
158 var hasher = std.hash.Wyhash.init(0);
159
160 // The generic function Decl is guaranteed to be the first dependency
161 // of each of its instantiations.
162 const generic_owner_decl = key.owner_decl.dependencies.keys()[0];
163 const generic_func: *const Fn = generic_owner_decl.val.castTag(.function).?.data;
164 std.hash.autoHash(&hasher, generic_func);
165
166 // This logic must be kept in sync with the logic in `analyzeCall` that
167 // computes the hash.
168 const comptime_args = key.comptime_args.?;
169 const generic_ty_info = generic_owner_decl.ty.fnInfo();
170 for (generic_ty_info.param_types) |param_ty, i| {
171 if (generic_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) {
172 comptime_args[i].val.hash(param_ty, &hasher, ctx.target);
173 }
174 }
175
176 return hasher.final();
156 _ = ctx;
157 return key.hash;
177158 }
178159};
179160
......@@ -1427,6 +1408,12 @@ pub const Fn = struct {
14271408 /// determine param names rather than redundantly storing them here.
14281409 param_names: []const [:0]const u8,
14291410
1411 /// Precomputed hash for monomorphed_funcs.
1412 /// This is important because it may be accessed when resizing monomorphed_funcs
1413 /// while this Fn has already been added to the set, but does not have the
1414 /// owner_decl, comptime_args, or other fields populated yet.
1415 hash: u64,
1416
14301417 /// Relative to owner Decl.
14311418 lbrace_line: u32,
14321419 /// Relative to owner Decl.
src/Sema.zig+39-25
......@@ -4671,22 +4671,6 @@ const GenericCallAdapter = struct {
46714671 }
46724672};
46734673
4674const GenericRemoveAdapter = struct {
4675 precomputed_hash: u64,
4676
4677 pub fn eql(ctx: @This(), adapted_key: *Module.Fn, other_key: *Module.Fn) bool {
4678 _ = ctx;
4679 return adapted_key == other_key;
4680 }
4681
4682 /// The implementation of the hash is in semantic analysis of function calls, so
4683 /// that any errors when computing the hash can be properly reported.
4684 pub fn hash(ctx: @This(), adapted_key: *Module.Fn) u64 {
4685 _ = adapted_key;
4686 return ctx.precomputed_hash;
4687 }
4688};
4689
46904674fn analyzeCall(
46914675 sema: *Sema,
46924676 block: *Block,
......@@ -5200,15 +5184,15 @@ fn instantiateGenericCall(
52005184 .comptime_tvs = comptime_tvs,
52015185 .target = target,
52025186 };
5203 const gop = try mod.monomorphed_funcs.getOrPutContextAdapted(gpa, {}, adapter, .{ .target = target });
5187 const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
52045188 const callee = if (!gop.found_existing) callee: {
52055189 const new_module_func = try gpa.create(Module.Fn);
5190 // This ensures that we can operate on the hash map before the Module.Fn
5191 // struct is fully initialized.
5192 new_module_func.hash = precomputed_hash;
52065193 gop.key_ptr.* = new_module_func;
52075194 errdefer gpa.destroy(new_module_func);
5208 const remove_adapter: GenericRemoveAdapter = .{
5209 .precomputed_hash = precomputed_hash,
5210 };
5211 errdefer assert(mod.monomorphed_funcs.removeAdapted(new_module_func, remove_adapter));
5195 errdefer assert(mod.monomorphed_funcs.remove(new_module_func));
52125196
52135197 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
52145198
......@@ -6494,12 +6478,14 @@ fn funcCommon(
64946478 param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name);
64956479 }
64966480
6481 const hash = new_func.hash;
64976482 const fn_payload = try sema.arena.create(Value.Payload.Function);
64986483 new_func.* = .{
64996484 .state = anal_state,
65006485 .zir_body_inst = func_inst,
65016486 .owner_decl = sema.owner_decl,
65026487 .comptime_args = comptime_args,
6488 .hash = hash,
65036489 .lbrace_line = src_locs.lbrace_line,
65046490 .rbrace_line = src_locs.rbrace_line,
65056491 .lbrace_column = @truncate(u16, src_locs.columns),
......@@ -19987,18 +19973,39 @@ fn analyzeIsNonErr(
1998719973 if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
1998819974 assert(ot == .ErrorUnion);
1998919975
19976 if (Air.refToIndex(operand)) |operand_inst| {
19977 const air_tags = sema.air_instructions.items(.tag);
19978 if (air_tags[operand_inst] == .wrap_errunion_payload) {
19979 return Air.Inst.Ref.bool_true;
19980 }
19981 }
19982
19983 const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand);
19984
1999019985 // exception if the error union error set is known to be empty,
1999119986 // we allow the comparison but always make it comptime known.
1999219987 const set_ty = operand_ty.errorUnionSet();
1999319988 switch (set_ty.tag()) {
19994 .anyerror, .error_set_inferred => {},
19989 .anyerror => {},
19990 .error_set_inferred => blk: {
19991 // If the error set is empty, we must return a comptime true or false.
19992 // However we want to avoid unnecessarily resolving an inferred error set
19993 // in case it is already non-empty.
19994 const ies = set_ty.castTag(.error_set_inferred).?.data;
19995 if (ies.is_anyerror) break :blk;
19996 if (ies.errors.count() != 0) break :blk;
19997 if (maybe_operand_val == null) {
19998 try sema.resolveInferredErrorSet(block, src, ies);
19999 if (ies.is_anyerror) break :blk;
20000 if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true;
20001 }
20002 },
1999520003 else => if (set_ty.errorSetNames().len == 0) return Air.Inst.Ref.bool_true,
1999620004 }
1999720005
19998 const result_ty = Type.bool;
19999 if (try sema.resolveMaybeUndefVal(block, src, operand)) |err_union| {
20006 if (maybe_operand_val) |err_union| {
2000020007 if (err_union.isUndef()) {
20001 return sema.addConstUndef(result_ty);
20008 return sema.addConstUndef(Type.bool);
2000220009 }
2000320010 if (err_union.getError() == null) {
2000420011 return Air.Inst.Ref.bool_true;
......@@ -20583,6 +20590,7 @@ fn wrapErrorUnionPayload(
2058320590 return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
2058420591 }
2058520592 try sema.requireRuntimeBlock(block, inst_src);
20593 try sema.queueFullTypeResolution(dest_payload_ty);
2058620594 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
2058720595}
2058820596
......@@ -21372,6 +21380,9 @@ fn resolveStructFully(
2137221380 try sema.resolveTypeFully(block, src, field.ty);
2137321381 }
2137421382 struct_obj.status = .fully_resolved;
21383
21384 // And let's not forget comptime-only status.
21385 _ = try sema.typeRequiresComptime(block, src, ty);
2137521386}
2137621387
2137721388fn resolveUnionFully(
......@@ -21395,6 +21406,9 @@ fn resolveUnionFully(
2139521406 try sema.resolveTypeFully(block, src, field.ty);
2139621407 }
2139721408 union_obj.status = .fully_resolved;
21409
21410 // And let's not forget comptime-only status.
21411 _ = try sema.typeRequiresComptime(block, src, ty);
2139821412}
2139921413
2140021414pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type {
src/arch/wasm/CodeGen.zig+4-4
......@@ -2627,12 +2627,12 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26272627
26282628 const op_ty = self.air.typeOf(ty_op.operand);
26292629 if (!op_ty.hasRuntimeBitsIgnoreComptime()) return operand;
2630 const err_ty = self.air.getRefType(ty_op.ty);
2631 const err_align = err_ty.abiAlignment(self.target);
2632 const set_size = err_ty.errorUnionSet().abiSize(self.target);
2630 const err_union_ty = self.air.getRefType(ty_op.ty);
2631 const err_align = err_union_ty.abiAlignment(self.target);
2632 const set_size = err_union_ty.errorUnionSet().abiSize(self.target);
26332633 const offset = mem.alignForwardGeneric(u64, set_size, err_align);
26342634
2635 const err_union = try self.allocStack(err_ty);
2635 const err_union = try self.allocStack(err_union_ty);
26362636 const payload_ptr = try self.buildPointerOffset(err_union, offset, .new);
26372637 try self.store(payload_ptr, operand, op_ty, 0);
26382638
test/behavior/error.zig+7
......@@ -251,6 +251,13 @@ fn testErrToIntWithOnePossibleValue(
251251 }
252252}
253253
254test "inferred empty error set comptime catch" {
255 const S = struct {
256 fn foo() !void {}
257 };
258 S.foo() catch @compileError("fail");
259}
260
254261test "error union peer type resolution" {
255262 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
256263 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO