| ... | ... | @@ -2004,6 +2004,10 @@ pub const Value = extern union { |
| 2004 | 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 | 2011 | /// This function is used by hash maps and so treats floating-point NaNs as equal |
| 2008 | 2012 | /// to each other, and not equal to other floating-point values. |
| 2009 | 2013 | /// Similarly, it treats `undef` as a distinct value from all other values. |
| ... | ... | @@ -2012,13 +2016,10 @@ pub const Value = extern union { |
| 2012 | 2016 | /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication |
| 2013 | 2017 | /// is required in order to make generic function instantiation efficient - specifically |
| 2014 | 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 | 2019 | /// If `null` is provided for `sema_kit` then it is guaranteed no error will be returned. |
| 2020 | 2020 | pub fn eqlAdvanced( |
| 2021 | 2021 | a: Value, |
| 2022 | a_ty: Type, |
| 2022 | 2023 | b: Value, |
| 2023 | 2024 | ty: Type, |
| 2024 | 2025 | mod: *Module, |
| ... | ... | @@ -2044,33 +2045,34 @@ pub const Value = extern union { |
| 2044 | 2045 | const a_payload = a.castTag(.opt_payload).?.data; |
| 2045 | 2046 | const b_payload = b.castTag(.opt_payload).?.data; |
| 2046 | 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 | 2051 | .slice => { |
| 2050 | 2052 | const a_payload = a.castTag(.slice).?.data; |
| 2051 | 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 | 2055 | return false; |
| 2054 | 2056 | } |
| 2055 | 2057 | |
| 2056 | 2058 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 2057 | 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 | 2063 | .elem_ptr => { |
| 2062 | 2064 | const a_payload = a.castTag(.elem_ptr).?.data; |
| 2063 | 2065 | const b_payload = b.castTag(.elem_ptr).?.data; |
| 2064 | 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 | 2070 | .field_ptr => { |
| 2069 | 2071 | const a_payload = a.castTag(.field_ptr).?.data; |
| 2070 | 2072 | const b_payload = b.castTag(.field_ptr).?.data; |
| 2071 | 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 | 2077 | .@"error" => { |
| 2076 | 2078 | const a_name = a.castTag(.@"error").?.data.name; |
| ... | ... | @@ -2080,7 +2082,8 @@ pub const Value = extern union { |
| 2080 | 2082 | .eu_payload => { |
| 2081 | 2083 | const a_payload = a.castTag(.eu_payload).?.data; |
| 2082 | 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 | 2088 | .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 2086 | 2089 | .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), |
| ... | ... | @@ -2098,7 +2101,7 @@ pub const Value = extern union { |
| 2098 | 2101 | const types = ty.tupleFields().types; |
| 2099 | 2102 | assert(types.len == a_field_vals.len); |
| 2100 | 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 | 2105 | return false; |
| 2103 | 2106 | } |
| 2104 | 2107 | } |
| ... | ... | @@ -2109,7 +2112,7 @@ pub const Value = extern union { |
| 2109 | 2112 | const fields = ty.structFields().values(); |
| 2110 | 2113 | assert(fields.len == a_field_vals.len); |
| 2111 | 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 | 2116 | return false; |
| 2114 | 2117 | } |
| 2115 | 2118 | } |
| ... | ... | @@ -2120,7 +2123,7 @@ pub const Value = extern union { |
| 2120 | 2123 | for (a_field_vals) |a_elem, i| { |
| 2121 | 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 | 2127 | return false; |
| 2125 | 2128 | } |
| 2126 | 2129 | } |
| ... | ... | @@ -2132,7 +2135,7 @@ pub const Value = extern union { |
| 2132 | 2135 | switch (ty.containerLayout()) { |
| 2133 | 2136 | .Packed, .Extern => { |
| 2134 | 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 | 2139 | // In this case, we must disregard mismatching tags and compare |
| 2137 | 2140 | // based on the in-memory bytes of the payloads. |
| 2138 | 2141 | @panic("TODO comptime comparison of extern union values with mismatching tags"); |
| ... | ... | @@ -2140,13 +2143,13 @@ pub const Value = extern union { |
| 2140 | 2143 | }, |
| 2141 | 2144 | .Auto => { |
| 2142 | 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 | 2147 | return false; |
| 2145 | 2148 | } |
| 2146 | 2149 | }, |
| 2147 | 2150 | } |
| 2148 | 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 | 2154 | else => {}, |
| 2152 | 2155 | } else if (a_tag == .null_value or b_tag == .null_value) { |
| ... | ... | @@ -2180,7 +2183,7 @@ pub const Value = extern union { |
| 2180 | 2183 | const b_val = b.enumToInt(ty, &buf_b); |
| 2181 | 2184 | var buf_ty: Type.Payload.Bits = undefined; |
| 2182 | 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 | 2188 | .Array, .Vector => { |
| 2186 | 2189 | const len = ty.arrayLen(); |
| ... | ... | @@ -2191,17 +2194,44 @@ pub const Value = extern union { |
| 2191 | 2194 | while (i < len) : (i += 1) { |
| 2192 | 2195 | const a_elem = elemValueBuffer(a, mod, i, &a_buf); |
| 2193 | 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 | 2198 | return false; |
| 2196 | 2199 | } |
| 2197 | 2200 | } |
| 2198 | 2201 | return true; |
| 2199 | 2202 | }, |
| 2200 | 2203 | .Struct => { |
| 2201 | | // A tuple can be represented with .empty_struct_value, |
| 2202 | | // the_one_possible_value, .aggregate in which case we could |
| 2203 | | // end up here and the values are equal if the type has zero fields. |
| 2204 | | return ty.isTupleOrAnonStruct() and ty.structFieldCount() != 0; |
| 2204 | // A struct can be represented with one of: |
| 2205 | // .empty_struct_value, |
| 2206 | // .the_one_possible_value, |
| 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 | 2236 | .Float => { |
| 2207 | 2237 | switch (ty.floatBits(target)) { |
| ... | ... | @@ -2230,7 +2260,8 @@ pub const Value = extern union { |
| 2230 | 2260 | .base = .{ .tag = .opt_payload }, |
| 2231 | 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 | 2267 | else => {}, |