| ... | @@ -1055,6 +1055,40 @@ pub const Value = extern union { | ... | @@ -1055,6 +1055,40 @@ pub const Value = extern union { |
| 1055 | } | 1055 | } |
| 1056 | } | 1056 | } |
| 1057 | | 1057 | |
| | 1058 | pub fn tagName(val: Value, ty: Type, mod: *Module) []const u8 { |
| | 1059 | if (ty.zigTypeTag() == .Union) return val.unionTag().tagName(ty.unionTagTypeHypothetical(), mod); |
| | 1060 | |
| | 1061 | const field_index = switch (val.tag()) { |
| | 1062 | .enum_field_index => val.castTag(.enum_field_index).?.data, |
| | 1063 | .the_only_possible_value => blk: { |
| | 1064 | assert(ty.enumFieldCount() == 1); |
| | 1065 | break :blk 0; |
| | 1066 | }, |
| | 1067 | .enum_literal => return val.castTag(.enum_literal).?.data, |
| | 1068 | else => field_index: { |
| | 1069 | const values = switch (ty.tag()) { |
| | 1070 | .enum_full, .enum_nonexhaustive => ty.cast(Type.Payload.EnumFull).?.data.values, |
| | 1071 | .enum_numbered => ty.castTag(.enum_numbered).?.data.values, |
| | 1072 | .enum_simple => Module.EnumFull.ValueMap{}, |
| | 1073 | else => unreachable, |
| | 1074 | }; |
| | 1075 | break :field_index if (values.entries.len == 0) |
| | 1076 | // auto-numbered enum |
| | 1077 | @intCast(u32, val.toUnsignedInt(mod.getTarget())) |
| | 1078 | else |
| | 1079 | @intCast(u32, values.getIndexContext(val, .{ .ty = ty, .mod = mod }).?); |
| | 1080 | }, |
| | 1081 | }; |
| | 1082 | |
| | 1083 | const fields = switch (ty.tag()) { |
| | 1084 | .enum_full, .enum_nonexhaustive => ty.cast(Type.Payload.EnumFull).?.data.fields, |
| | 1085 | .enum_numbered => ty.castTag(.enum_numbered).?.data.fields, |
| | 1086 | .enum_simple => ty.castTag(.enum_simple).?.data.fields, |
| | 1087 | else => unreachable, |
| | 1088 | }; |
| | 1089 | return fields.keys()[field_index]; |
| | 1090 | } |
| | 1091 | |
| 1058 | /// Asserts the value is an integer. | 1092 | /// Asserts the value is an integer. |
| 1059 | pub fn toBigInt(val: Value, space: *BigIntSpace, target: Target) BigIntConst { | 1093 | pub fn toBigInt(val: Value, space: *BigIntSpace, target: Target) BigIntConst { |
| 1060 | return val.toBigIntAdvanced(space, target, null) catch unreachable; | 1094 | return val.toBigIntAdvanced(space, target, null) catch unreachable; |
| ... | @@ -2211,7 +2245,7 @@ pub const Value = extern union { | ... | @@ -2211,7 +2245,7 @@ pub const Value = extern union { |
| 2211 | return eqlAdvanced(a_union.val, active_field_ty, b_union.val, active_field_ty, mod, sema_kit); | 2245 | return eqlAdvanced(a_union.val, active_field_ty, b_union.val, active_field_ty, mod, sema_kit); |
| 2212 | }, | 2246 | }, |
| 2213 | else => {}, | 2247 | else => {}, |
| 2214 | } else if (a_tag == .null_value or b_tag == .null_value) { | 2248 | } else if (b_tag == .null_value or b_tag == .@"error") { |
| 2215 | return false; | 2249 | return false; |
| 2216 | } else if (a_tag == .undef or b_tag == .undef) { | 2250 | } else if (a_tag == .undef or b_tag == .undef) { |
| 2217 | return false; | 2251 | return false; |
| ... | @@ -2335,18 +2369,25 @@ pub const Value = extern union { | ... | @@ -2335,18 +2369,25 @@ pub const Value = extern union { |
| 2335 | if (a_nan) return true; | 2369 | if (a_nan) return true; |
| 2336 | return a_float == b_float; | 2370 | return a_float == b_float; |
| 2337 | }, | 2371 | }, |
| 2338 | .Optional => { | 2372 | .Optional => if (a_tag != .null_value and b_tag == .opt_payload) { |
| 2339 | if (a.tag() != .opt_payload and b.tag() == .opt_payload) { | 2373 | var sub_pl: Payload.SubValue = .{ |
| 2340 | var buffer: Payload.SubValue = .{ | 2374 | .base = .{ .tag = b.tag() }, |
| 2341 | .base = .{ .tag = .opt_payload }, | 2375 | .data = a, |
| 2342 | .data = a, | 2376 | }; |
| 2343 | }; | 2377 | const sub_val = Value.initPayload(&sub_pl.base); |
| 2344 | const opt_val = Value.initPayload(&buffer.base); | 2378 | return eqlAdvanced(sub_val, ty, b, ty, mod, sema_kit); |
| 2345 | return eqlAdvanced(opt_val, ty, b, ty, mod, sema_kit); | 2379 | }, |
| 2346 | } | 2380 | .ErrorUnion => if (a_tag != .@"error" and b_tag == .eu_payload) { |
| | 2381 | var sub_pl: Payload.SubValue = .{ |
| | 2382 | .base = .{ .tag = b.tag() }, |
| | 2383 | .data = a, |
| | 2384 | }; |
| | 2385 | const sub_val = Value.initPayload(&sub_pl.base); |
| | 2386 | return eqlAdvanced(sub_val, ty, b, ty, mod, sema_kit); |
| 2347 | }, | 2387 | }, |
| 2348 | else => {}, | 2388 | else => {}, |
| 2349 | } | 2389 | } |
| | 2390 | if (a_tag == .null_value or a_tag == .@"error") return false; |
| 2350 | return (try orderAdvanced(a, b, target, sema_kit)).compare(.eq); | 2391 | return (try orderAdvanced(a, b, target, sema_kit)).compare(.eq); |
| 2351 | } | 2392 | } |
| 2352 | | 2393 | |
| ... | @@ -2436,7 +2477,7 @@ pub const Value = extern union { | ... | @@ -2436,7 +2477,7 @@ pub const Value = extern union { |
| 2436 | const sub_ty = ty.optionalChild(&buffer); | 2477 | const sub_ty = ty.optionalChild(&buffer); |
| 2437 | sub_val.hash(sub_ty, hasher, mod); | 2478 | sub_val.hash(sub_ty, hasher, mod); |
| 2438 | } else { | 2479 | } else { |
| 2439 | std.hash.autoHash(hasher, false); // non-null | 2480 | std.hash.autoHash(hasher, false); // null |
| 2440 | } | 2481 | } |
| 2441 | }, | 2482 | }, |
| 2442 | .ErrorUnion => { | 2483 | .ErrorUnion => { |
| ... | @@ -2474,8 +2515,8 @@ pub const Value = extern union { | ... | @@ -2474,8 +2515,8 @@ pub const Value = extern union { |
| 2474 | union_obj.val.hash(active_field_ty, hasher, mod); | 2515 | union_obj.val.hash(active_field_ty, hasher, mod); |
| 2475 | }, | 2516 | }, |
| 2476 | .Fn => { | 2517 | .Fn => { |
| 2477 | // Note that his hashes the *Fn/*ExternFn rather than the *Decl. This is | 2518 | // Note that this hashes the *Fn/*ExternFn rather than the *Decl. |
| 2478 | // to differentiate function bodies from function pointers. | 2519 | // This is to differentiate function bodies from function pointers. |
| 2479 | // This is currently redundant since we already hash the zig type tag | 2520 | // This is currently redundant since we already hash the zig type tag |
| 2480 | // at the top of this function. | 2521 | // at the top of this function. |
| 2481 | if (val.castTag(.function)) |func| { | 2522 | if (val.castTag(.function)) |func| { |
| ... | @@ -2497,6 +2538,64 @@ pub const Value = extern union { | ... | @@ -2497,6 +2538,64 @@ pub const Value = extern union { |
| 2497 | } | 2538 | } |
| 2498 | } | 2539 | } |
| 2499 | | 2540 | |
| | 2541 | /// This is a more conservative hash function that produces equal hashes for values |
| | 2542 | /// that can coerce into each other. |
| | 2543 | /// This function is used by hash maps and so treats floating-point NaNs as equal |
| | 2544 | /// to each other, and not equal to other floating-point values. |
| | 2545 | pub fn hashUncoerced(val: Value, ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void { |
| | 2546 | if (val.isUndef()) return; |
| | 2547 | // The value is runtime-known and shouldn't affect the hash. |
| | 2548 | if (val.tag() == .runtime_value) return; |
| | 2549 | |
| | 2550 | switch (ty.zigTypeTag()) { |
| | 2551 | .BoundFn => unreachable, // TODO remove this from the language |
| | 2552 | .Opaque => unreachable, // Cannot hash opaque types |
| | 2553 | .Void, |
| | 2554 | .NoReturn, |
| | 2555 | .Undefined, |
| | 2556 | .Null, |
| | 2557 | .Struct, // It sure would be nice to do something clever with structs. |
| | 2558 | => |zig_type_tag| std.hash.autoHash(hasher, zig_type_tag), |
| | 2559 | .Type => { |
| | 2560 | var buf: ToTypeBuffer = undefined; |
| | 2561 | val.toType(&buf).hashWithHasher(hasher, mod); |
| | 2562 | }, |
| | 2563 | .Float, .ComptimeFloat => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128))), |
| | 2564 | .Bool, .Int, .ComptimeInt, .Pointer, .Fn => switch (val.tag()) { |
| | 2565 | .slice => val.castTag(.slice).?.data.ptr.hashPtr(hasher, mod.getTarget()), |
| | 2566 | else => val.hashPtr(hasher, mod.getTarget()), |
| | 2567 | }, |
| | 2568 | .Array, .Vector => { |
| | 2569 | const len = ty.arrayLen(); |
| | 2570 | const elem_ty = ty.childType(); |
| | 2571 | var index: usize = 0; |
| | 2572 | var elem_value_buf: ElemValueBuffer = undefined; |
| | 2573 | while (index < len) : (index += 1) { |
| | 2574 | const elem_val = val.elemValueBuffer(mod, index, &elem_value_buf); |
| | 2575 | elem_val.hashUncoerced(elem_ty, hasher, mod); |
| | 2576 | } |
| | 2577 | }, |
| | 2578 | .Optional => if (val.castTag(.opt_payload)) |payload| { |
| | 2579 | var buf: Type.Payload.ElemType = undefined; |
| | 2580 | const child_ty = ty.optionalChild(&buf); |
| | 2581 | payload.data.hashUncoerced(child_ty, hasher, mod); |
| | 2582 | } else std.hash.autoHash(hasher, std.builtin.TypeId.Null), |
| | 2583 | .ErrorSet, .ErrorUnion => if (val.getError()) |err| hasher.update(err) else { |
| | 2584 | const pl_ty = ty.errorUnionPayload(); |
| | 2585 | val.castTag(.eu_payload).?.data.hashUncoerced(pl_ty, hasher, mod); |
| | 2586 | }, |
| | 2587 | .Enum, .EnumLiteral, .Union => { |
| | 2588 | hasher.update(val.tagName(ty, mod)); |
| | 2589 | if (val.cast(Payload.Union)) |union_obj| { |
| | 2590 | const active_field_ty = ty.unionFieldType(union_obj.data.tag, mod); |
| | 2591 | union_obj.data.val.hashUncoerced(active_field_ty, hasher, mod); |
| | 2592 | } else std.hash.autoHash(hasher, std.builtin.TypeId.Void); |
| | 2593 | }, |
| | 2594 | .Frame => @panic("TODO implement hashing frame values"), |
| | 2595 | .AnyFrame => @panic("TODO implement hashing anyframe values"), |
| | 2596 | } |
| | 2597 | } |
| | 2598 | |
| 2500 | pub const ArrayHashContext = struct { | 2599 | pub const ArrayHashContext = struct { |
| 2501 | ty: Type, | 2600 | ty: Type, |
| 2502 | mod: *Module, | 2601 | mod: *Module, |