mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 13:58:27 +00:00
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
This commit is contained in:
parent
05947ea870
commit
c21f046a8b
@ -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.
|
||||
|
||||
64
src/Sema.zig
64
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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user