authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-17 12:55:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-17 13:02:57-07:00
logc764640e92c9e4d32b89650ac774bebf1498be92
tree5b17ee88da0d6499007a36a3a4b701c5d8f44a81
parenta12abc6d6c8b89a09befdcbd9019247ccc3bd641

Sema: fix generics with struct literal coerced to tagged union

The `Value.eql` function has to test for value equality *as-if* the lhs value parameter is coerced into the type of the rhs. For tagged unions, there was a problematic case when the lhs was an anonymous struct, because in such case the value is empty_struct_value and the type contains all the value information. But the only type available in the function was the rhs type. So the fix involved making `Value.eqlAdvanced` also accept the lhs type, and then enhancing the logic to handle the case of the `.anon_struct` tag. closes #12418 Tests run locally: * test-behavior * test-cases

3 files changed, 110 insertions(+), 42 deletions(-)

src/Sema.zig+37-19
...@@ -1495,7 +1495,8 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {...@@ -1495,7 +1495,8 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
14951495
1496 // Finally, the last section of indexes refers to the map of ZIR=>AIR.1496 // Finally, the last section of indexes refers to the map of ZIR=>AIR.
1497 const inst = sema.inst_map.get(@intCast(u32, i)).?;1497 const inst = sema.inst_map.get(@intCast(u32, i)).?;
1498 if (sema.typeOf(inst).tag() == .generic_poison) return error.GenericPoison;1498 const ty = sema.typeOf(inst);
1499 if (ty.tag() == .generic_poison) return error.GenericPoison;
1499 return inst;1500 return inst;
1500}1501}
15011502
...@@ -5570,11 +5571,15 @@ const GenericCallAdapter = struct {...@@ -5570,11 +5571,15 @@ const GenericCallAdapter = struct {
5570 generic_fn: *Module.Fn,5571 generic_fn: *Module.Fn,
5571 precomputed_hash: u64,5572 precomputed_hash: u64,
5572 func_ty_info: Type.Payload.Function.Data,5573 func_ty_info: Type.Payload.Function.Data,
5573 /// Unlike comptime_args, the Type here is not always present.5574 args: []const Arg,
5574 /// .generic_poison is used to communicate non-anytype parameters.
5575 comptime_tvs: []const TypedValue,
5576 module: *Module,5575 module: *Module,
55775576
5577 const Arg = struct {
5578 ty: Type,
5579 val: Value,
5580 is_anytype: bool,
5581 };
5582
5578 pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool {5583 pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool {
5579 _ = adapted_key;5584 _ = adapted_key;
5580 // The generic function Decl is guaranteed to be the first dependency5585 // The generic function Decl is guaranteed to be the first dependency
...@@ -5585,10 +5590,10 @@ const GenericCallAdapter = struct {...@@ -5585,10 +5590,10 @@ const GenericCallAdapter = struct {
55855590
5586 const other_comptime_args = other_key.comptime_args.?;5591 const other_comptime_args = other_key.comptime_args.?;
5587 for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| {5592 for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| {
5588 const this_arg = ctx.comptime_tvs[i];5593 const this_arg = ctx.args[i];
5589 const this_is_comptime = this_arg.val.tag() != .generic_poison;5594 const this_is_comptime = this_arg.val.tag() != .generic_poison;
5590 const other_is_comptime = other_arg.val.tag() != .generic_poison;5595 const other_is_comptime = other_arg.val.tag() != .generic_poison;
5591 const this_is_anytype = this_arg.ty.tag() != .generic_poison;5596 const this_is_anytype = this_arg.is_anytype;
5592 const other_is_anytype = other_key.isAnytypeParam(ctx.module, @intCast(u32, i));5597 const other_is_anytype = other_key.isAnytypeParam(ctx.module, @intCast(u32, i));
55935598
5594 if (other_is_anytype != this_is_anytype) return false;5599 if (other_is_anytype != this_is_anytype) return false;
...@@ -5607,7 +5612,17 @@ const GenericCallAdapter = struct {...@@ -5607,7 +5612,17 @@ const GenericCallAdapter = struct {
5607 }5612 }
5608 } else if (this_is_comptime) {5613 } else if (this_is_comptime) {
5609 // Both are comptime parameters but not anytype parameters.5614 // Both are comptime parameters but not anytype parameters.
5610 if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) {5615 // We assert no error is possible here because any lazy values must be resolved
5616 // before inserting into the generic function hash map.
5617 const is_eql = Value.eqlAdvanced(
5618 this_arg.val,
5619 this_arg.ty,
5620 other_arg.val,
5621 other_arg.ty,
5622 ctx.module,
5623 null,
5624 ) catch unreachable;
5625 if (!is_eql) {
5611 return false;5626 return false;
5612 }5627 }
5613 }5628 }
...@@ -6258,8 +6273,7 @@ fn instantiateGenericCall(...@@ -6258,8 +6273,7 @@ fn instantiateGenericCall(
6258 var hasher = std.hash.Wyhash.init(0);6273 var hasher = std.hash.Wyhash.init(0);
6259 std.hash.autoHash(&hasher, @ptrToInt(module_fn));6274 std.hash.autoHash(&hasher, @ptrToInt(module_fn));
62606275
6261 const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len);6276 const generic_args = try sema.arena.alloc(GenericCallAdapter.Arg, func_ty_info.param_types.len);
6262
6263 {6277 {
6264 var i: usize = 0;6278 var i: usize = 0;
6265 for (fn_info.param_body) |inst| {6279 for (fn_info.param_body) |inst| {
...@@ -6283,8 +6297,9 @@ fn instantiateGenericCall(...@@ -6283,8 +6297,9 @@ fn instantiateGenericCall(
6283 else => continue,6297 else => continue,
6284 }6298 }
62856299
6300 const arg_ty = sema.typeOf(uncasted_args[i]);
6301
6286 if (is_comptime) {6302 if (is_comptime) {
6287 const arg_ty = sema.typeOf(uncasted_args[i]);
6288 const arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, uncasted_args[i]) catch |err| switch (err) {6303 const arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, uncasted_args[i]) catch |err| switch (err) {
6289 error.NeededSourceLocation => {6304 error.NeededSourceLocation => {
6290 const decl = sema.mod.declPtr(block.src_decl);6305 const decl = sema.mod.declPtr(block.src_decl);
...@@ -6297,27 +6312,30 @@ fn instantiateGenericCall(...@@ -6297,27 +6312,30 @@ fn instantiateGenericCall(
6297 arg_val.hash(arg_ty, &hasher, mod);6312 arg_val.hash(arg_ty, &hasher, mod);
6298 if (is_anytype) {6313 if (is_anytype) {
6299 arg_ty.hashWithHasher(&hasher, mod);6314 arg_ty.hashWithHasher(&hasher, mod);
6300 comptime_tvs[i] = .{6315 generic_args[i] = .{
6301 .ty = arg_ty,6316 .ty = arg_ty,
6302 .val = arg_val,6317 .val = arg_val,
6318 .is_anytype = true,
6303 };6319 };
6304 } else {6320 } else {
6305 comptime_tvs[i] = .{6321 generic_args[i] = .{
6306 .ty = Type.initTag(.generic_poison),6322 .ty = arg_ty,
6307 .val = arg_val,6323 .val = arg_val,
6324 .is_anytype = false,
6308 };6325 };
6309 }6326 }
6310 } else if (is_anytype) {6327 } else if (is_anytype) {
6311 const arg_ty = sema.typeOf(uncasted_args[i]);
6312 arg_ty.hashWithHasher(&hasher, mod);6328 arg_ty.hashWithHasher(&hasher, mod);
6313 comptime_tvs[i] = .{6329 generic_args[i] = .{
6314 .ty = arg_ty,6330 .ty = arg_ty,
6315 .val = Value.initTag(.generic_poison),6331 .val = Value.initTag(.generic_poison),
6332 .is_anytype = true,
6316 };6333 };
6317 } else {6334 } else {
6318 comptime_tvs[i] = .{6335 generic_args[i] = .{
6319 .ty = Type.initTag(.generic_poison),6336 .ty = arg_ty,
6320 .val = Value.initTag(.generic_poison),6337 .val = Value.initTag(.generic_poison),
6338 .is_anytype = false,
6321 };6339 };
6322 }6340 }
63236341
...@@ -6331,7 +6349,7 @@ fn instantiateGenericCall(...@@ -6331,7 +6349,7 @@ fn instantiateGenericCall(
6331 .generic_fn = module_fn,6349 .generic_fn = module_fn,
6332 .precomputed_hash = precomputed_hash,6350 .precomputed_hash = precomputed_hash,
6333 .func_ty_info = func_ty_info,6351 .func_ty_info = func_ty_info,
6334 .comptime_tvs = comptime_tvs,6352 .args = generic_args,
6335 .module = mod,6353 .module = mod,
6336 };6354 };
6337 const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);6355 const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
...@@ -30124,7 +30142,7 @@ fn valuesEqual(...@@ -30124,7 +30142,7 @@ fn valuesEqual(
30124 rhs: Value,30142 rhs: Value,
30125 ty: Type,30143 ty: Type,
30126) CompileError!bool {30144) CompileError!bool {
30127 return Value.eqlAdvanced(lhs, rhs, ty, sema.mod, sema.kit(block, src));30145 return Value.eqlAdvanced(lhs, ty, rhs, ty, sema.mod, sema.kit(block, src));
30128}30146}
3012930147
30130/// Asserts the values are comparable vectors of type `ty`.30148/// Asserts the values are comparable vectors of type `ty`.
src/value.zig+54-23
...@@ -2004,6 +2004,10 @@ pub const Value = extern union {...@@ -2004,6 +2004,10 @@ pub const Value = extern union {
2004 return (try orderAgainstZeroAdvanced(lhs, sema_kit)).compare(op);2004 return (try orderAgainstZeroAdvanced(lhs, sema_kit)).compare(op);
2005 }2005 }
20062006
2007 pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool {
2008 return eqlAdvanced(a, ty, b, ty, mod, null) catch unreachable;
2009 }
2010
2007 /// This function is used by hash maps and so treats floating-point NaNs as equal2011 /// This function is used by hash maps and so treats floating-point NaNs as equal
2008 /// to each other, and not equal to other floating-point values.2012 /// to each other, and not equal to other floating-point values.
2009 /// Similarly, it treats `undef` as a distinct value from all other values.2013 /// Similarly, it treats `undef` as a distinct value from all other values.
...@@ -2012,13 +2016,10 @@ pub const Value = extern union {...@@ -2012,13 +2016,10 @@ pub const Value = extern union {
2012 /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication2016 /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication
2013 /// is required in order to make generic function instantiation efficient - specifically2017 /// is required in order to make generic function instantiation efficient - specifically
2014 /// the insertion into the monomorphized function table.2018 /// the insertion into the monomorphized function table.
2015 pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool {
2016 return eqlAdvanced(a, b, ty, mod, null) catch unreachable;
2017 }
2018
2019 /// If `null` is provided for `sema_kit` then it is guaranteed no error will be returned.2019 /// If `null` is provided for `sema_kit` then it is guaranteed no error will be returned.
2020 pub fn eqlAdvanced(2020 pub fn eqlAdvanced(
2021 a: Value,2021 a: Value,
2022 a_ty: Type,
2022 b: Value,2023 b: Value,
2023 ty: Type,2024 ty: Type,
2024 mod: *Module,2025 mod: *Module,
...@@ -2044,33 +2045,34 @@ pub const Value = extern union {...@@ -2044,33 +2045,34 @@ pub const Value = extern union {
2044 const a_payload = a.castTag(.opt_payload).?.data;2045 const a_payload = a.castTag(.opt_payload).?.data;
2045 const b_payload = b.castTag(.opt_payload).?.data;2046 const b_payload = b.castTag(.opt_payload).?.data;
2046 var buffer: Type.Payload.ElemType = undefined;2047 var buffer: Type.Payload.ElemType = undefined;
2047 return eqlAdvanced(a_payload, b_payload, ty.optionalChild(&buffer), mod, sema_kit);2048 const payload_ty = ty.optionalChild(&buffer);
2049 return eqlAdvanced(a_payload, payload_ty, b_payload, payload_ty, mod, sema_kit);
2048 },2050 },
2049 .slice => {2051 .slice => {
2050 const a_payload = a.castTag(.slice).?.data;2052 const a_payload = a.castTag(.slice).?.data;
2051 const b_payload = b.castTag(.slice).?.data;2053 const b_payload = b.castTag(.slice).?.data;
2052 if (!(try eqlAdvanced(a_payload.len, b_payload.len, Type.usize, mod, sema_kit))) {2054 if (!(try eqlAdvanced(a_payload.len, Type.usize, b_payload.len, Type.usize, mod, sema_kit))) {
2053 return false;2055 return false;
2054 }2056 }
20552057
2056 var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;2058 var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
2057 const ptr_ty = ty.slicePtrFieldType(&ptr_buf);2059 const ptr_ty = ty.slicePtrFieldType(&ptr_buf);
20582060
2059 return eqlAdvanced(a_payload.ptr, b_payload.ptr, ptr_ty, mod, sema_kit);2061 return eqlAdvanced(a_payload.ptr, ptr_ty, b_payload.ptr, ptr_ty, mod, sema_kit);
2060 },2062 },
2061 .elem_ptr => {2063 .elem_ptr => {
2062 const a_payload = a.castTag(.elem_ptr).?.data;2064 const a_payload = a.castTag(.elem_ptr).?.data;
2063 const b_payload = b.castTag(.elem_ptr).?.data;2065 const b_payload = b.castTag(.elem_ptr).?.data;
2064 if (a_payload.index != b_payload.index) return false;2066 if (a_payload.index != b_payload.index) return false;
20652067
2066 return eqlAdvanced(a_payload.array_ptr, b_payload.array_ptr, ty, mod, sema_kit);2068 return eqlAdvanced(a_payload.array_ptr, ty, b_payload.array_ptr, ty, mod, sema_kit);
2067 },2069 },
2068 .field_ptr => {2070 .field_ptr => {
2069 const a_payload = a.castTag(.field_ptr).?.data;2071 const a_payload = a.castTag(.field_ptr).?.data;
2070 const b_payload = b.castTag(.field_ptr).?.data;2072 const b_payload = b.castTag(.field_ptr).?.data;
2071 if (a_payload.field_index != b_payload.field_index) return false;2073 if (a_payload.field_index != b_payload.field_index) return false;
20722074
2073 return eqlAdvanced(a_payload.container_ptr, b_payload.container_ptr, ty, mod, sema_kit);2075 return eqlAdvanced(a_payload.container_ptr, ty, b_payload.container_ptr, ty, mod, sema_kit);
2074 },2076 },
2075 .@"error" => {2077 .@"error" => {
2076 const a_name = a.castTag(.@"error").?.data.name;2078 const a_name = a.castTag(.@"error").?.data.name;
...@@ -2080,7 +2082,8 @@ pub const Value = extern union {...@@ -2080,7 +2082,8 @@ pub const Value = extern union {
2080 .eu_payload => {2082 .eu_payload => {
2081 const a_payload = a.castTag(.eu_payload).?.data;2083 const a_payload = a.castTag(.eu_payload).?.data;
2082 const b_payload = b.castTag(.eu_payload).?.data;2084 const b_payload = b.castTag(.eu_payload).?.data;
2083 return eqlAdvanced(a_payload, b_payload, ty.errorUnionPayload(), mod, sema_kit);2085 const payload_ty = ty.errorUnionPayload();
2086 return eqlAdvanced(a_payload, payload_ty, b_payload, payload_ty, mod, sema_kit);
2084 },2087 },
2085 .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),2088 .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
2086 .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),2089 .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
...@@ -2098,7 +2101,7 @@ pub const Value = extern union {...@@ -2098,7 +2101,7 @@ pub const Value = extern union {
2098 const types = ty.tupleFields().types;2101 const types = ty.tupleFields().types;
2099 assert(types.len == a_field_vals.len);2102 assert(types.len == a_field_vals.len);
2100 for (types) |field_ty, i| {2103 for (types) |field_ty, i| {
2101 if (!(try eqlAdvanced(a_field_vals[i], b_field_vals[i], field_ty, mod, sema_kit))) {2104 if (!(try eqlAdvanced(a_field_vals[i], field_ty, b_field_vals[i], field_ty, mod, sema_kit))) {
2102 return false;2105 return false;
2103 }2106 }
2104 }2107 }
...@@ -2109,7 +2112,7 @@ pub const Value = extern union {...@@ -2109,7 +2112,7 @@ pub const Value = extern union {
2109 const fields = ty.structFields().values();2112 const fields = ty.structFields().values();
2110 assert(fields.len == a_field_vals.len);2113 assert(fields.len == a_field_vals.len);
2111 for (fields) |field, i| {2114 for (fields) |field, i| {
2112 if (!(try eqlAdvanced(a_field_vals[i], b_field_vals[i], field.ty, mod, sema_kit))) {2115 if (!(try eqlAdvanced(a_field_vals[i], field.ty, b_field_vals[i], field.ty, mod, sema_kit))) {
2113 return false;2116 return false;
2114 }2117 }
2115 }2118 }
...@@ -2120,7 +2123,7 @@ pub const Value = extern union {...@@ -2120,7 +2123,7 @@ pub const Value = extern union {
2120 for (a_field_vals) |a_elem, i| {2123 for (a_field_vals) |a_elem, i| {
2121 const b_elem = b_field_vals[i];2124 const b_elem = b_field_vals[i];
21222125
2123 if (!(try eqlAdvanced(a_elem, b_elem, elem_ty, mod, sema_kit))) {2126 if (!(try eqlAdvanced(a_elem, elem_ty, b_elem, elem_ty, mod, sema_kit))) {
2124 return false;2127 return false;
2125 }2128 }
2126 }2129 }
...@@ -2132,7 +2135,7 @@ pub const Value = extern union {...@@ -2132,7 +2135,7 @@ pub const Value = extern union {
2132 switch (ty.containerLayout()) {2135 switch (ty.containerLayout()) {
2133 .Packed, .Extern => {2136 .Packed, .Extern => {
2134 const tag_ty = ty.unionTagTypeHypothetical();2137 const tag_ty = ty.unionTagTypeHypothetical();
2135 if (!(try a_union.tag.eqlAdvanced(b_union.tag, tag_ty, mod, sema_kit))) {2138 if (!(try eqlAdvanced(a_union.tag, tag_ty, b_union.tag, tag_ty, mod, sema_kit))) {
2136 // In this case, we must disregard mismatching tags and compare2139 // In this case, we must disregard mismatching tags and compare
2137 // based on the in-memory bytes of the payloads.2140 // based on the in-memory bytes of the payloads.
2138 @panic("TODO comptime comparison of extern union values with mismatching tags");2141 @panic("TODO comptime comparison of extern union values with mismatching tags");
...@@ -2140,13 +2143,13 @@ pub const Value = extern union {...@@ -2140,13 +2143,13 @@ pub const Value = extern union {
2140 },2143 },
2141 .Auto => {2144 .Auto => {
2142 const tag_ty = ty.unionTagTypeHypothetical();2145 const tag_ty = ty.unionTagTypeHypothetical();
2143 if (!(try a_union.tag.eqlAdvanced(b_union.tag, tag_ty, mod, sema_kit))) {2146 if (!(try eqlAdvanced(a_union.tag, tag_ty, b_union.tag, tag_ty, mod, sema_kit))) {
2144 return false;2147 return false;
2145 }2148 }
2146 },2149 },
2147 }2150 }
2148 const active_field_ty = ty.unionFieldType(a_union.tag, mod);2151 const active_field_ty = ty.unionFieldType(a_union.tag, mod);
2149 return a_union.val.eqlAdvanced(b_union.val, active_field_ty, mod, sema_kit);2152 return eqlAdvanced(a_union.val, active_field_ty, b_union.val, active_field_ty, mod, sema_kit);
2150 },2153 },
2151 else => {},2154 else => {},
2152 } else if (a_tag == .null_value or b_tag == .null_value) {2155 } else if (a_tag == .null_value or b_tag == .null_value) {
...@@ -2180,7 +2183,7 @@ pub const Value = extern union {...@@ -2180,7 +2183,7 @@ pub const Value = extern union {
2180 const b_val = b.enumToInt(ty, &buf_b);2183 const b_val = b.enumToInt(ty, &buf_b);
2181 var buf_ty: Type.Payload.Bits = undefined;2184 var buf_ty: Type.Payload.Bits = undefined;
2182 const int_ty = ty.intTagType(&buf_ty);2185 const int_ty = ty.intTagType(&buf_ty);
2183 return eqlAdvanced(a_val, b_val, int_ty, mod, sema_kit);2186 return eqlAdvanced(a_val, int_ty, b_val, int_ty, mod, sema_kit);
2184 },2187 },
2185 .Array, .Vector => {2188 .Array, .Vector => {
2186 const len = ty.arrayLen();2189 const len = ty.arrayLen();
...@@ -2191,17 +2194,44 @@ pub const Value = extern union {...@@ -2191,17 +2194,44 @@ pub const Value = extern union {
2191 while (i < len) : (i += 1) {2194 while (i < len) : (i += 1) {
2192 const a_elem = elemValueBuffer(a, mod, i, &a_buf);2195 const a_elem = elemValueBuffer(a, mod, i, &a_buf);
2193 const b_elem = elemValueBuffer(b, mod, i, &b_buf);2196 const b_elem = elemValueBuffer(b, mod, i, &b_buf);
2194 if (!(try eqlAdvanced(a_elem, b_elem, elem_ty, mod, sema_kit))) {2197 if (!(try eqlAdvanced(a_elem, elem_ty, b_elem, elem_ty, mod, sema_kit))) {
2195 return false;2198 return false;
2196 }2199 }
2197 }2200 }
2198 return true;2201 return true;
2199 },2202 },
2200 .Struct => {2203 .Struct => {
2201 // A tuple can be represented with .empty_struct_value,2204 // A struct can be represented with one of:
2202 // the_one_possible_value, .aggregate in which case we could2205 // .empty_struct_value,
2203 // end up here and the values are equal if the type has zero fields.2206 // .the_one_possible_value,
2204 return ty.isTupleOrAnonStruct() and ty.structFieldCount() != 0;2207 // .aggregate,
2208 // Note that we already checked above for matching tags, e.g. both .aggregate.
2209 return ty.onePossibleValue() != null;
2210 },
2211 .Union => {
2212 // Here we have to check for value equality, as-if `a` has been coerced to `ty`.
2213 if (ty.onePossibleValue() != null) {
2214 return true;
2215 }
2216 if (a_ty.castTag(.anon_struct)) |payload| {
2217 const tuple = payload.data;
2218 if (tuple.values.len != 1) {
2219 return false;
2220 }
2221 const field_name = tuple.names[0];
2222 const union_obj = ty.cast(Type.Payload.Union).?.data;
2223 const field_index = union_obj.fields.getIndex(field_name) orelse return false;
2224 const tag_and_val = b.castTag(.@"union").?.data;
2225 var field_tag_buf: Value.Payload.U32 = .{
2226 .base = .{ .tag = .enum_field_index },
2227 .data = @intCast(u32, field_index),
2228 };
2229 const field_tag = Value.initPayload(&field_tag_buf.base);
2230 const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod);
2231 if (!tag_matches) return false;
2232 return eqlAdvanced(tag_and_val.val, union_obj.tag_ty, tuple.values[0], tuple.types[0], mod, sema_kit);
2233 }
2234 return false;
2205 },2235 },
2206 .Float => {2236 .Float => {
2207 switch (ty.floatBits(target)) {2237 switch (ty.floatBits(target)) {
...@@ -2230,7 +2260,8 @@ pub const Value = extern union {...@@ -2230,7 +2260,8 @@ pub const Value = extern union {
2230 .base = .{ .tag = .opt_payload },2260 .base = .{ .tag = .opt_payload },
2231 .data = a,2261 .data = a,
2232 };2262 };
2233 return eqlAdvanced(Value.initPayload(&buffer.base), b, ty, mod, sema_kit);2263 const opt_val = Value.initPayload(&buffer.base);
2264 return eqlAdvanced(opt_val, ty, b, ty, mod, sema_kit);
2234 }2265 }
2235 },2266 },
2236 else => {},2267 else => {},
test/behavior/generics.zig+19
...@@ -323,3 +323,22 @@ test "generic function instantiation non-duplicates" {...@@ -323,3 +323,22 @@ test "generic function instantiation non-duplicates" {
323 S.copy(u8, &buffer, "hello");323 S.copy(u8, &buffer, "hello");
324 S.copy(u8, &buffer, "hello2");324 S.copy(u8, &buffer, "hello2");
325}325}
326
327test "generic instantiation of tagged union with only one field" {
328 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
329 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
330 if (builtin.os.tag == .wasi) return error.SkipZigTest;
331
332 const S = struct {
333 const U = union(enum) {
334 s: []const u8,
335 };
336
337 fn foo(comptime u: U) usize {
338 return u.s.len;
339 }
340 };
341
342 try expect(S.foo(.{ .s = "a" }) == 1);
343 try expect(S.foo(.{ .s = "ab" }) == 2);
344}