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(...@@ -146,8 +146,6 @@ const MonomorphedFuncsSet = std.HashMapUnmanaged(
146);146);
147147
148const MonomorphedFuncsContext = struct {148const MonomorphedFuncsContext = struct {
149 target: Target,
150
151 pub fn eql(ctx: @This(), a: *Fn, b: *Fn) bool {149 pub fn eql(ctx: @This(), a: *Fn, b: *Fn) bool {
152 _ = ctx;150 _ = ctx;
153 return a == b;151 return a == b;
...@@ -155,25 +153,8 @@ const MonomorphedFuncsContext = struct {...@@ -155,25 +153,8 @@ const MonomorphedFuncsContext = struct {
155153
156 /// Must match `Sema.GenericCallAdapter.hash`.154 /// Must match `Sema.GenericCallAdapter.hash`.
157 pub fn hash(ctx: @This(), key: *Fn) u64 {155 pub fn hash(ctx: @This(), key: *Fn) u64 {
158 var hasher = std.hash.Wyhash.init(0);156 _ = ctx;
159157 return key.hash;
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();
177 }158 }
178};159};
179160
...@@ -1427,6 +1408,12 @@ pub const Fn = struct {...@@ -1427,6 +1408,12 @@ pub const Fn = struct {
1427 /// determine param names rather than redundantly storing them here.1408 /// determine param names rather than redundantly storing them here.
1428 param_names: []const [:0]const u8,1409 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
1430 /// Relative to owner Decl.1417 /// Relative to owner Decl.
1431 lbrace_line: u32,1418 lbrace_line: u32,
1432 /// Relative to owner Decl.1419 /// Relative to owner Decl.
src/Sema.zig+39-25
...@@ -4671,22 +4671,6 @@ const GenericCallAdapter = struct {...@@ -4671,22 +4671,6 @@ const GenericCallAdapter = struct {
4671 }4671 }
4672};4672};
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
4690fn analyzeCall(4674fn analyzeCall(
4691 sema: *Sema,4675 sema: *Sema,
4692 block: *Block,4676 block: *Block,
...@@ -5200,15 +5184,15 @@ fn instantiateGenericCall(...@@ -5200,15 +5184,15 @@ fn instantiateGenericCall(
5200 .comptime_tvs = comptime_tvs,5184 .comptime_tvs = comptime_tvs,
5201 .target = target,5185 .target = target,
5202 };5186 };
5203 const gop = try mod.monomorphed_funcs.getOrPutContextAdapted(gpa, {}, adapter, .{ .target = target });5187 const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
5204 const callee = if (!gop.found_existing) callee: {5188 const callee = if (!gop.found_existing) callee: {
5205 const new_module_func = try gpa.create(Module.Fn);5189 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;
5206 gop.key_ptr.* = new_module_func;5193 gop.key_ptr.* = new_module_func;
5207 errdefer gpa.destroy(new_module_func);5194 errdefer gpa.destroy(new_module_func);
5208 const remove_adapter: GenericRemoveAdapter = .{5195 errdefer assert(mod.monomorphed_funcs.remove(new_module_func));
5209 .precomputed_hash = precomputed_hash,
5210 };
5211 errdefer assert(mod.monomorphed_funcs.removeAdapted(new_module_func, remove_adapter));
52125196
5213 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);5197 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
52145198
...@@ -6494,12 +6478,14 @@ fn funcCommon(...@@ -6494,12 +6478,14 @@ fn funcCommon(
6494 param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name);6478 param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name);
6495 }6479 }
64966480
6481 const hash = new_func.hash;
6497 const fn_payload = try sema.arena.create(Value.Payload.Function);6482 const fn_payload = try sema.arena.create(Value.Payload.Function);
6498 new_func.* = .{6483 new_func.* = .{
6499 .state = anal_state,6484 .state = anal_state,
6500 .zir_body_inst = func_inst,6485 .zir_body_inst = func_inst,
6501 .owner_decl = sema.owner_decl,6486 .owner_decl = sema.owner_decl,
6502 .comptime_args = comptime_args,6487 .comptime_args = comptime_args,
6488 .hash = hash,
6503 .lbrace_line = src_locs.lbrace_line,6489 .lbrace_line = src_locs.lbrace_line,
6504 .rbrace_line = src_locs.rbrace_line,6490 .rbrace_line = src_locs.rbrace_line,
6505 .lbrace_column = @truncate(u16, src_locs.columns),6491 .lbrace_column = @truncate(u16, src_locs.columns),
...@@ -19987,18 +19973,39 @@ fn analyzeIsNonErr(...@@ -19987,18 +19973,39 @@ fn analyzeIsNonErr(
19987 if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;19973 if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
19988 assert(ot == .ErrorUnion);19974 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
19990 // exception if the error union error set is known to be empty,19985 // exception if the error union error set is known to be empty,
19991 // we allow the comparison but always make it comptime known.19986 // we allow the comparison but always make it comptime known.
19992 const set_ty = operand_ty.errorUnionSet();19987 const set_ty = operand_ty.errorUnionSet();
19993 switch (set_ty.tag()) {19988 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 },
19995 else => if (set_ty.errorSetNames().len == 0) return Air.Inst.Ref.bool_true,20003 else => if (set_ty.errorSetNames().len == 0) return Air.Inst.Ref.bool_true,
19996 }20004 }
1999720005
19998 const result_ty = Type.bool;20006 if (maybe_operand_val) |err_union| {
19999 if (try sema.resolveMaybeUndefVal(block, src, operand)) |err_union| {
20000 if (err_union.isUndef()) {20007 if (err_union.isUndef()) {
20001 return sema.addConstUndef(result_ty);20008 return sema.addConstUndef(Type.bool);
20002 }20009 }
20003 if (err_union.getError() == null) {20010 if (err_union.getError() == null) {
20004 return Air.Inst.Ref.bool_true;20011 return Air.Inst.Ref.bool_true;
...@@ -20583,6 +20590,7 @@ fn wrapErrorUnionPayload(...@@ -20583,6 +20590,7 @@ fn wrapErrorUnionPayload(
20583 return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));20590 return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
20584 }20591 }
20585 try sema.requireRuntimeBlock(block, inst_src);20592 try sema.requireRuntimeBlock(block, inst_src);
20593 try sema.queueFullTypeResolution(dest_payload_ty);
20586 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);20594 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
20587}20595}
2058820596
...@@ -21372,6 +21380,9 @@ fn resolveStructFully(...@@ -21372,6 +21380,9 @@ fn resolveStructFully(
21372 try sema.resolveTypeFully(block, src, field.ty);21380 try sema.resolveTypeFully(block, src, field.ty);
21373 }21381 }
21374 struct_obj.status = .fully_resolved;21382 struct_obj.status = .fully_resolved;
21383
21384 // And let's not forget comptime-only status.
21385 _ = try sema.typeRequiresComptime(block, src, ty);
21375}21386}
2137621387
21377fn resolveUnionFully(21388fn resolveUnionFully(
...@@ -21395,6 +21406,9 @@ fn resolveUnionFully(...@@ -21395,6 +21406,9 @@ fn resolveUnionFully(
21395 try sema.resolveTypeFully(block, src, field.ty);21406 try sema.resolveTypeFully(block, src, field.ty);
21396 }21407 }
21397 union_obj.status = .fully_resolved;21408 union_obj.status = .fully_resolved;
21409
21410 // And let's not forget comptime-only status.
21411 _ = try sema.typeRequiresComptime(block, src, ty);
21398}21412}
2139921413
21400pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type {21414pub 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 {...@@ -2627,12 +2627,12 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26272627
2628 const op_ty = self.air.typeOf(ty_op.operand);2628 const op_ty = self.air.typeOf(ty_op.operand);
2629 if (!op_ty.hasRuntimeBitsIgnoreComptime()) return operand;2629 if (!op_ty.hasRuntimeBitsIgnoreComptime()) return operand;
2630 const err_ty = self.air.getRefType(ty_op.ty);2630 const err_union_ty = self.air.getRefType(ty_op.ty);
2631 const err_align = err_ty.abiAlignment(self.target);2631 const err_align = err_union_ty.abiAlignment(self.target);
2632 const set_size = err_ty.errorUnionSet().abiSize(self.target);2632 const set_size = err_union_ty.errorUnionSet().abiSize(self.target);
2633 const offset = mem.alignForwardGeneric(u64, set_size, err_align);2633 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);
2636 const payload_ptr = try self.buildPointerOffset(err_union, offset, .new);2636 const payload_ptr = try self.buildPointerOffset(err_union, offset, .new);
2637 try self.store(payload_ptr, operand, op_ty, 0);2637 try self.store(payload_ptr, operand, op_ty, 0);
26382638
test/behavior/error.zig+7
...@@ -251,6 +251,13 @@ fn testErrToIntWithOnePossibleValue(...@@ -251,6 +251,13 @@ fn testErrToIntWithOnePossibleValue(
251 }251 }
252}252}
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
254test "error union peer type resolution" {261test "error union peer type resolution" {
255 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO262 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
256 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO263 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO