| ... | @@ -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 | } |
| 2006 | | 2006 | |
| | 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 equal | 2011 | /// 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 complication | 2016 | /// 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 - specifically | 2017 | /// 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 | } |
| 2055 | | 2057 | |
| 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); |
| 2058 | | 2060 | |
| 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; |
| 2065 | | 2067 | |
| 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; |
| 2072 | | 2074 | |
| 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]; |
| 2122 | | 2125 | |
| 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 compare | 2139 | // 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 could | 2205 | // .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 => {}, |