From 684aee32209932166713462cc341fa469aad0728 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 13 Jul 2023 13:24:44 -0700 Subject: [PATCH] frontend: fixes for function regressions in this branch * Introduce InternPool.Tag.func_coerced to handle the case of a function body coerced to a new type. `InternPool.getCoerced` is now implemented for function bodies in this branch. * implement resolution of ad-hoc inferred error sets in `Sema.analyzeCall`. * fix generic_owner being set wrong for child Sema bodies of param expressions. * fix `Sema.resolveInferredErrorSetTy` when passed `anyerror`. --- src/InternPool.zig | 477 +++++++++++++++++++++++++++------------------ src/Sema.zig | 42 +++- 2 files changed, 319 insertions(+), 200 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 0aef044f11..36e690b381 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1670,6 +1670,9 @@ pub const Index = enum(u32) { @"trailing.comptime_args.len": *@"data.generic_owner.data.ty.data.params_len", trailing: struct { resolved_error_set: []Index, comptime_args: []Index }, }, + func_coerced: struct { + data: *Tag.FuncCoerced, + }, only_possible_value: DataIsIndex, union_value: struct { data: *Key.Union }, bytes: struct { data: *Bytes }, @@ -2192,6 +2195,9 @@ pub const Tag = enum(u8) { /// A generic function instantiation. /// data is extra index to `FuncInstance`. func_instance, + /// A `func_decl` or a `func_instance` that has been coerced to a different type. + /// data is extra index to `FuncCoerced`. + func_coerced, /// This represents the only possible value for *some* types which have /// only one possible value. Not all only-possible-values are encoded this way; /// for example structs which have all comptime fields are not encoded this way. @@ -2298,6 +2304,7 @@ pub const Tag = enum(u8) { .extern_func => ExternFunc, .func_decl => FuncDecl, .func_instance => FuncInstance, + .func_coerced => FuncCoerced, .only_possible_value => unreachable, .union_value => Union, .bytes => Bytes, @@ -2364,6 +2371,11 @@ pub const Tag = enum(u8) { generic_owner: Index, }; + pub const FuncCoerced = struct { + ty: Index, + func: Index, + }; + /// Trailing: /// 0. name: NullTerminatedString for each names_len pub const ErrorSet = struct { @@ -3205,6 +3217,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .extern_func => .{ .extern_func = ip.extraData(Tag.ExternFunc, data) }, .func_instance => .{ .func = ip.extraFuncInstance(data) }, .func_decl => .{ .func = ip.extraFuncDecl(data) }, + .func_coerced => .{ .func = ip.extraFuncCoerced(data) }, .only_possible_value => { const ty = @as(Index, @enumFromInt(data)); const ty_item = ip.items.get(@intFromEnum(ty)); @@ -3397,6 +3410,18 @@ fn extraFuncInstance(ip: *const InternPool, extra_index: u32) Key.Func { }; } +fn extraFuncCoerced(ip: *const InternPool, extra_index: u32) Key.Func { + const func_coerced = ip.extraData(Tag.FuncCoerced, extra_index); + const sub_item = ip.items.get(@intFromEnum(func_coerced.func)); + var func: Key.Func = switch (sub_item.tag) { + .func_instance => ip.extraFuncInstance(sub_item.data), + .func_decl => ip.extraFuncDecl(sub_item.data), + else => unreachable, + }; + func.ty = func_coerced.ty; + return func; +} + fn indexToKeyEnum(ip: *const InternPool, data: u32, tag_mode: Key.EnumType.TagMode) Key { const enum_explicit = ip.extraDataTrail(EnumExplicit, data); const names = @as( @@ -5480,210 +5505,226 @@ pub fn sliceLen(ip: *const InternPool, i: Index) Index { pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index { const old_ty = ip.typeOf(val); if (old_ty == new_ty) return val; + + const tags = ip.items.items(.tag); + switch (val) { .undef => return ip.get(gpa, .{ .undef = new_ty }), - .null_value => if (ip.isOptionalType(new_ty)) - return ip.get(gpa, .{ .opt = .{ + .null_value => { + if (ip.isOptionalType(new_ty)) return ip.get(gpa, .{ .opt = .{ .ty = new_ty, .val = .none, - } }) - else if (ip.isPointerType(new_ty)) - return ip.get(gpa, .{ .ptr = .{ + } }); + + if (ip.isPointerType(new_ty)) return ip.get(gpa, .{ .ptr = .{ .ty = new_ty, .addr = .{ .int = .zero_usize }, .len = switch (ip.indexToKey(new_ty).ptr_type.flags.size) { .One, .Many, .C => .none, .Slice => try ip.get(gpa, .{ .undef = .usize_type }), }, - } }), - else => switch (ip.indexToKey(val)) { - .undef => return ip.get(gpa, .{ .undef = new_ty }), - .extern_func => |extern_func| if (ip.isFunctionType(new_ty)) - return ip.get(gpa, .{ .extern_func = .{ - .ty = new_ty, - .decl = extern_func.decl, - .lib_name = extern_func.lib_name, - } }), - - .func => |func| { - if (func.generic_owner == .none) { - @panic("TODO"); - } else { - @panic("TODO"); + } }); + }, + else => switch (tags[@intFromEnum(val)]) { + .func_decl => return getCoercedFuncDecl(ip, gpa, val, new_ty), + .func_instance => return getCoercedFuncInstance(ip, gpa, val, new_ty), + .func_coerced => { + const extra_index = ip.items.items(.data)[@intFromEnum(val)]; + const func: Index = @enumFromInt( + ip.extra.items[extra_index + std.meta.fieldIndex(Tag.FuncCoerced, "func").?], + ); + switch (tags[@intFromEnum(func)]) { + .func_decl => return getCoercedFuncDecl(ip, gpa, val, new_ty), + .func_instance => return getCoercedFuncInstance(ip, gpa, val, new_ty), + else => unreachable, } }, - - .int => |int| switch (ip.indexToKey(new_ty)) { - .enum_type => |enum_type| return ip.get(gpa, .{ .enum_tag = .{ - .ty = new_ty, - .int = try ip.getCoerced(gpa, val, enum_type.tag_ty), - } }), - .ptr_type => return ip.get(gpa, .{ .ptr = .{ - .ty = new_ty, - .addr = .{ .int = try ip.getCoerced(gpa, val, .usize_type) }, - } }), - else => if (ip.isIntegerType(new_ty)) - return getCoercedInts(ip, gpa, int, new_ty), - }, - .float => |float| switch (ip.indexToKey(new_ty)) { - .simple_type => |simple| switch (simple) { - .f16, - .f32, - .f64, - .f80, - .f128, - .c_longdouble, - .comptime_float, - => return ip.get(gpa, .{ .float = .{ - .ty = new_ty, - .storage = float.storage, - } }), - else => {}, - }, - else => {}, - }, - .enum_tag => |enum_tag| if (ip.isIntegerType(new_ty)) - return getCoercedInts(ip, gpa, ip.indexToKey(enum_tag.int).int, new_ty), - .enum_literal => |enum_literal| switch (ip.indexToKey(new_ty)) { - .enum_type => |enum_type| { - const index = enum_type.nameIndex(ip, enum_literal).?; - return ip.get(gpa, .{ .enum_tag = .{ - .ty = new_ty, - .int = if (enum_type.values.len != 0) - enum_type.values[index] - else - try ip.get(gpa, .{ .int = .{ - .ty = enum_type.tag_ty, - .storage = .{ .u64 = index }, - } }), - } }); - }, - else => {}, - }, - .ptr => |ptr| if (ip.isPointerType(new_ty)) - return ip.get(gpa, .{ .ptr = .{ - .ty = new_ty, - .addr = ptr.addr, - .len = ptr.len, - } }) - else if (ip.isIntegerType(new_ty)) - switch (ptr.addr) { - .int => |int| return ip.getCoerced(gpa, int, new_ty), - else => {}, - }, - .opt => |opt| switch (ip.indexToKey(new_ty)) { - .ptr_type => |ptr_type| return switch (opt.val) { - .none => try ip.get(gpa, .{ .ptr = .{ - .ty = new_ty, - .addr = .{ .int = .zero_usize }, - .len = switch (ptr_type.flags.size) { - .One, .Many, .C => .none, - .Slice => try ip.get(gpa, .{ .undef = .usize_type }), - }, - } }), - else => |payload| try ip.getCoerced(gpa, payload, new_ty), - }, - .opt_type => |child_type| return try ip.get(gpa, .{ .opt = .{ - .ty = new_ty, - .val = switch (opt.val) { - .none => .none, - else => try ip.getCoerced(gpa, opt.val, child_type), - }, - } }), - else => {}, - }, - .err => |err| if (ip.isErrorSetType(new_ty)) - return ip.get(gpa, .{ .err = .{ - .ty = new_ty, - .name = err.name, - } }) - else if (ip.isErrorUnionType(new_ty)) - return ip.get(gpa, .{ .error_union = .{ - .ty = new_ty, - .val = .{ .err_name = err.name }, - } }), - .error_union => |error_union| if (ip.isErrorUnionType(new_ty)) - return ip.get(gpa, .{ .error_union = .{ - .ty = new_ty, - .val = error_union.val, - } }), - .aggregate => |aggregate| { - const new_len = @as(usize, @intCast(ip.aggregateTypeLen(new_ty))); - direct: { - const old_ty_child = switch (ip.indexToKey(old_ty)) { - inline .array_type, .vector_type => |seq_type| seq_type.child, - .anon_struct_type, .struct_type => break :direct, - else => unreachable, - }; - const new_ty_child = switch (ip.indexToKey(new_ty)) { - inline .array_type, .vector_type => |seq_type| seq_type.child, - .anon_struct_type, .struct_type => break :direct, - else => unreachable, - }; - if (old_ty_child != new_ty_child) break :direct; - // TODO: write something like getCoercedInts to avoid needing to dupe here - switch (aggregate.storage) { - .bytes => |bytes| { - const bytes_copy = try gpa.dupe(u8, bytes[0..new_len]); - defer gpa.free(bytes_copy); - return ip.get(gpa, .{ .aggregate = .{ - .ty = new_ty, - .storage = .{ .bytes = bytes_copy }, - } }); - }, - .elems => |elems| { - const elems_copy = try gpa.dupe(InternPool.Index, elems[0..new_len]); - defer gpa.free(elems_copy); - return ip.get(gpa, .{ .aggregate = .{ - .ty = new_ty, - .storage = .{ .elems = elems_copy }, - } }); - }, - .repeated_elem => |elem| { - return ip.get(gpa, .{ .aggregate = .{ - .ty = new_ty, - .storage = .{ .repeated_elem = elem }, - } }); - }, - } - } - // Direct approach failed - we must recursively coerce elems - const agg_elems = try gpa.alloc(InternPool.Index, new_len); - defer gpa.free(agg_elems); - // First, fill the vector with the uncoerced elements. We do this to avoid key - // lifetime issues, since it'll allow us to avoid referencing `aggregate` after we - // begin interning elems. - switch (aggregate.storage) { - .bytes => { - // We have to intern each value here, so unfortunately we can't easily avoid - // the repeated indexToKey calls. - for (agg_elems, 0..) |*elem, i| { - const x = ip.indexToKey(val).aggregate.storage.bytes[i]; - elem.* = try ip.get(gpa, .{ .int = .{ - .ty = .u8_type, - .storage = .{ .u64 = x }, - } }); - } - }, - .elems => |elems| @memcpy(agg_elems, elems[0..new_len]), - .repeated_elem => |elem| @memset(agg_elems, elem), - } - // Now, coerce each element to its new type. - for (agg_elems, 0..) |*elem, i| { - const new_elem_ty = switch (ip.indexToKey(new_ty)) { - inline .array_type, .vector_type => |seq_type| seq_type.child, - .anon_struct_type => |anon_struct_type| anon_struct_type.types[i], - .struct_type => |struct_type| ip.structPtr(struct_type.index.unwrap().?) - .fields.values()[i].ty.toIntern(), - else => unreachable, - }; - elem.* = try ip.getCoerced(gpa, elem.*, new_elem_ty); - } - return ip.get(gpa, .{ .aggregate = .{ .ty = new_ty, .storage = .{ .elems = agg_elems } } }); - }, else => {}, }, } + + switch (ip.indexToKey(val)) { + .undef => return ip.get(gpa, .{ .undef = new_ty }), + .extern_func => |extern_func| if (ip.isFunctionType(new_ty)) + return ip.get(gpa, .{ .extern_func = .{ + .ty = new_ty, + .decl = extern_func.decl, + .lib_name = extern_func.lib_name, + } }), + + .func => unreachable, + + .int => |int| switch (ip.indexToKey(new_ty)) { + .enum_type => |enum_type| return ip.get(gpa, .{ .enum_tag = .{ + .ty = new_ty, + .int = try ip.getCoerced(gpa, val, enum_type.tag_ty), + } }), + .ptr_type => return ip.get(gpa, .{ .ptr = .{ + .ty = new_ty, + .addr = .{ .int = try ip.getCoerced(gpa, val, .usize_type) }, + } }), + else => if (ip.isIntegerType(new_ty)) + return getCoercedInts(ip, gpa, int, new_ty), + }, + .float => |float| switch (ip.indexToKey(new_ty)) { + .simple_type => |simple| switch (simple) { + .f16, + .f32, + .f64, + .f80, + .f128, + .c_longdouble, + .comptime_float, + => return ip.get(gpa, .{ .float = .{ + .ty = new_ty, + .storage = float.storage, + } }), + else => {}, + }, + else => {}, + }, + .enum_tag => |enum_tag| if (ip.isIntegerType(new_ty)) + return getCoercedInts(ip, gpa, ip.indexToKey(enum_tag.int).int, new_ty), + .enum_literal => |enum_literal| switch (ip.indexToKey(new_ty)) { + .enum_type => |enum_type| { + const index = enum_type.nameIndex(ip, enum_literal).?; + return ip.get(gpa, .{ .enum_tag = .{ + .ty = new_ty, + .int = if (enum_type.values.len != 0) + enum_type.values[index] + else + try ip.get(gpa, .{ .int = .{ + .ty = enum_type.tag_ty, + .storage = .{ .u64 = index }, + } }), + } }); + }, + else => {}, + }, + .ptr => |ptr| if (ip.isPointerType(new_ty)) + return ip.get(gpa, .{ .ptr = .{ + .ty = new_ty, + .addr = ptr.addr, + .len = ptr.len, + } }) + else if (ip.isIntegerType(new_ty)) + switch (ptr.addr) { + .int => |int| return ip.getCoerced(gpa, int, new_ty), + else => {}, + }, + .opt => |opt| switch (ip.indexToKey(new_ty)) { + .ptr_type => |ptr_type| return switch (opt.val) { + .none => try ip.get(gpa, .{ .ptr = .{ + .ty = new_ty, + .addr = .{ .int = .zero_usize }, + .len = switch (ptr_type.flags.size) { + .One, .Many, .C => .none, + .Slice => try ip.get(gpa, .{ .undef = .usize_type }), + }, + } }), + else => |payload| try ip.getCoerced(gpa, payload, new_ty), + }, + .opt_type => |child_type| return try ip.get(gpa, .{ .opt = .{ + .ty = new_ty, + .val = switch (opt.val) { + .none => .none, + else => try ip.getCoerced(gpa, opt.val, child_type), + }, + } }), + else => {}, + }, + .err => |err| if (ip.isErrorSetType(new_ty)) + return ip.get(gpa, .{ .err = .{ + .ty = new_ty, + .name = err.name, + } }) + else if (ip.isErrorUnionType(new_ty)) + return ip.get(gpa, .{ .error_union = .{ + .ty = new_ty, + .val = .{ .err_name = err.name }, + } }), + .error_union => |error_union| if (ip.isErrorUnionType(new_ty)) + return ip.get(gpa, .{ .error_union = .{ + .ty = new_ty, + .val = error_union.val, + } }), + .aggregate => |aggregate| { + const new_len = @as(usize, @intCast(ip.aggregateTypeLen(new_ty))); + direct: { + const old_ty_child = switch (ip.indexToKey(old_ty)) { + inline .array_type, .vector_type => |seq_type| seq_type.child, + .anon_struct_type, .struct_type => break :direct, + else => unreachable, + }; + const new_ty_child = switch (ip.indexToKey(new_ty)) { + inline .array_type, .vector_type => |seq_type| seq_type.child, + .anon_struct_type, .struct_type => break :direct, + else => unreachable, + }; + if (old_ty_child != new_ty_child) break :direct; + // TODO: write something like getCoercedInts to avoid needing to dupe here + switch (aggregate.storage) { + .bytes => |bytes| { + const bytes_copy = try gpa.dupe(u8, bytes[0..new_len]); + defer gpa.free(bytes_copy); + return ip.get(gpa, .{ .aggregate = .{ + .ty = new_ty, + .storage = .{ .bytes = bytes_copy }, + } }); + }, + .elems => |elems| { + const elems_copy = try gpa.dupe(InternPool.Index, elems[0..new_len]); + defer gpa.free(elems_copy); + return ip.get(gpa, .{ .aggregate = .{ + .ty = new_ty, + .storage = .{ .elems = elems_copy }, + } }); + }, + .repeated_elem => |elem| { + return ip.get(gpa, .{ .aggregate = .{ + .ty = new_ty, + .storage = .{ .repeated_elem = elem }, + } }); + }, + } + } + // Direct approach failed - we must recursively coerce elems + const agg_elems = try gpa.alloc(InternPool.Index, new_len); + defer gpa.free(agg_elems); + // First, fill the vector with the uncoerced elements. We do this to avoid key + // lifetime issues, since it'll allow us to avoid referencing `aggregate` after we + // begin interning elems. + switch (aggregate.storage) { + .bytes => { + // We have to intern each value here, so unfortunately we can't easily avoid + // the repeated indexToKey calls. + for (agg_elems, 0..) |*elem, i| { + const x = ip.indexToKey(val).aggregate.storage.bytes[i]; + elem.* = try ip.get(gpa, .{ .int = .{ + .ty = .u8_type, + .storage = .{ .u64 = x }, + } }); + } + }, + .elems => |elems| @memcpy(agg_elems, elems[0..new_len]), + .repeated_elem => |elem| @memset(agg_elems, elem), + } + // Now, coerce each element to its new type. + for (agg_elems, 0..) |*elem, i| { + const new_elem_ty = switch (ip.indexToKey(new_ty)) { + inline .array_type, .vector_type => |seq_type| seq_type.child, + .anon_struct_type => |anon_struct_type| anon_struct_type.types[i], + .struct_type => |struct_type| ip.structPtr(struct_type.index.unwrap().?) + .fields.values()[i].ty.toIntern(), + else => unreachable, + }; + elem.* = try ip.getCoerced(gpa, elem.*, new_elem_ty); + } + return ip.get(gpa, .{ .aggregate = .{ .ty = new_ty, .storage = .{ .elems = agg_elems } } }); + }, + else => {}, + } + switch (ip.indexToKey(new_ty)) { .opt_type => |child_type| switch (val) { .null_value => return ip.get(gpa, .{ .opt = .{ @@ -5711,6 +5752,54 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al unreachable; } +fn getCoercedFuncDecl(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index { + const datas = ip.items.items(.data); + const extra_index = datas[@intFromEnum(val)]; + const prev_ty: Index = @enumFromInt( + ip.extra.items[extra_index + std.meta.fieldIndex(Tag.FuncDecl, "ty").?], + ); + if (new_ty == prev_ty) return val; + return getCoercedFunc(ip, gpa, val, new_ty); +} + +fn getCoercedFuncInstance(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index { + const datas = ip.items.items(.data); + const extra_index = datas[@intFromEnum(val)]; + const prev_ty: Index = @enumFromInt( + ip.extra.items[extra_index + std.meta.fieldIndex(Tag.FuncInstance, "ty").?], + ); + if (new_ty == prev_ty) return val; + return getCoercedFunc(ip, gpa, val, new_ty); +} + +fn getCoercedFunc(ip: *InternPool, gpa: Allocator, func: Index, ty: Index) Allocator.Error!Index { + const prev_extra_len = ip.extra.items.len; + try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.FuncCoerced).Struct.fields.len); + try ip.items.ensureUnusedCapacity(gpa, 1); + try ip.map.ensureUnusedCapacity(gpa, 1); + + const extra_index = ip.addExtraAssumeCapacity(Tag.FuncCoerced{ + .ty = ty, + .func = func, + }); + + const adapter: KeyAdapter = .{ .intern_pool = ip }; + const gop = ip.map.getOrPutAssumeCapacityAdapted(Key{ + .func = extraFuncCoerced(ip, extra_index), + }, adapter); + + if (gop.found_existing) { + ip.extra.items.len = prev_extra_len; + return @enumFromInt(gop.index); + } + + ip.items.appendAssumeCapacity(.{ + .tag = .func_coerced, + .data = extra_index, + }); + return @enumFromInt(ip.items.len - 1); +} + /// Asserts `val` has an integer type. /// Assumes `new_ty` is an integer type. pub fn getCoercedInts(ip: *InternPool, gpa: Allocator, int: Key.Int, new_ty: Index) Allocator.Error!Index { @@ -6025,6 +6114,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { break :b @sizeOf(Tag.FuncInstance) + @sizeOf(Index) * params_len + @sizeOf(Module.Decl); }, + .func_coerced => @sizeOf(Tag.FuncCoerced), .only_possible_value => 0, .union_value => @sizeOf(Key.Union), @@ -6131,6 +6221,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void { .extern_func, .func_decl, .func_instance, + .func_coerced, .union_value, .memoized_call, => try w.print("{d}", .{data}), @@ -6471,7 +6562,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .undef, .opt_null, .only_possible_value, - => @as(Index, @enumFromInt(ip.items.items(.data)[@intFromEnum(index)])), + => @enumFromInt(ip.items.items(.data)[@intFromEnum(index)]), .simple_value => unreachable, // handled via Index above @@ -6497,6 +6588,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .extern_func, .func_decl, .func_instance, + .func_coerced, .union_value, .bytes, .aggregate, @@ -6504,7 +6596,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { => |t| { const extra_index = ip.items.items(.data)[@intFromEnum(index)]; const field_index = std.meta.fieldIndex(t.Payload(), "ty").?; - return @as(Index, @enumFromInt(ip.extra.items[extra_index + field_index])); + return @enumFromInt(ip.extra.items[extra_index + field_index]); }, .int_u8 => .u8_type, @@ -6850,6 +6942,7 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois .extern_func, .func_decl, .func_instance, + .func_coerced, .only_possible_value, .union_value, .bytes, @@ -6866,7 +6959,7 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois pub fn isFuncBody(ip: *const InternPool, i: Index) bool { assert(i != .none); return switch (ip.items.items(.tag)[@intFromEnum(i)]) { - .func_decl, .func_instance => true, + .func_decl, .func_instance, .func_coerced => true, else => false, }; } diff --git a/src/Sema.zig b/src/Sema.zig index a3efa2d083..cb3fc239ab 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7155,9 +7155,17 @@ fn analyzeCall( break :res2 Air.internedToRef(result_transformed); } - if (sema.fn_ret_ty_ies) |ies| { - _ = ies; - @panic("TODO: resolve ad-hoc inferred error set"); + if (try sema.resolveMaybeUndefVal(result)) |result_val| { + const result_interned = try result_val.intern(sema.fn_ret_ty, mod); + const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned); + break :res2 Air.internedToRef(result_transformed); + } + + const new_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result).toIntern()); + if (new_ty != .none) { + // TODO: mutate in place the previous instruction if possible + // rather than adding a bitcast instruction. + break :res2 try block.addBitCast(new_ty.toType(), result); } break :res2 result; @@ -9141,11 +9149,14 @@ fn zirParam( // Make sure any nested param instructions don't clobber our work. const prev_params = block.params; const prev_no_partial_func_type = sema.no_partial_func_ty; + const prev_generic_owner = sema.generic_owner; block.params = .{}; sema.no_partial_func_ty = true; + sema.generic_owner = .none; defer { block.params = prev_params; sema.no_partial_func_ty = prev_no_partial_func_type; + sema.generic_owner = prev_generic_owner; } if (sema.resolveBody(block, body, inst)) |param_ty_inst| { @@ -34114,24 +34125,37 @@ fn resolveAdHocInferredErrorSet( src: LazySrcLoc, value: InternPool.Index, ) CompileError!InternPool.Index { - const ies = sema.fn_ret_ty_ies orelse return value; const mod = sema.mod; const gpa = sema.gpa; const ip = &mod.intern_pool; - const ty = ip.typeOf(value); + const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value)); + if (new_ty == .none) return value; + return ip.getCoerced(gpa, value, new_ty); +} + +fn resolveAdHocInferredErrorSetTy( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ty: InternPool.Index, +) CompileError!InternPool.Index { + const ies = sema.fn_ret_ty_ies orelse return .none; + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; const error_union_info = switch (ip.indexToKey(ty)) { .error_union_type => |x| x, - else => return value, + else => return .none, }; if (error_union_info.error_set_type != .adhoc_inferred_error_set_type) - return value; + return .none; try sema.resolveInferredErrorSetPtr(block, src, ies); const new_ty = try ip.get(gpa, .{ .error_union_type = .{ .error_set_type = ies.resolved, .payload_type = error_union_info.payload_type, } }); - return ip.getCoerced(gpa, value, new_ty); + return new_ty; } fn resolveInferredErrorSetTy( @@ -34142,6 +34166,7 @@ fn resolveInferredErrorSetTy( ) CompileError!InternPool.Index { const mod = sema.mod; const ip = &mod.intern_pool; + if (ty == .anyerror_type) return ty; switch (ip.indexToKey(ty)) { .error_set_type => return ty, .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty), @@ -35229,6 +35254,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .extern_func, .func_decl, .func_instance, + .func_coerced, .only_possible_value, .union_value, .bytes,