| author | |
| committer | |
| log | 507dc1f2e7fac212e79f152e557cbec98a3c30e9 |
| tree | 34b057caf8ebadef2d199af39b1b44fca48c1a23 |
| parent | 84039a57e4684e8df10e657bb76c6acb3fb89238 |
* `Value.toType` accepts a buffer parameter instead of an allocator
parameter and can no longer fail.
* Module: remove the unused `mod: *Module` parameter from various
functions.
* `Value.compare` now accepts a `Type` parameter which indicates the
type of both operands. There is also a `Value.compareHetero` which
accepts only Value parameters and supports comparing mixed types.
Likewise, `Value.eql` requires a `Type` parameter.
* `Value.hash` is removed; instead the hash map context structs now
have a `ty: Type` field, and the hash function lives there, where it
has access to a Value's Type when it computes a hash.
- This allowed the hash function to be greatly simplified and sound
in the sense that the same Values, even with different
representations, always hash to the same thing.
* Sema: Fix source location of zirCmp when an operand is runtime known
but needs to be comptime known.
* Remove unused target parameter from `Value.floatCast`.6 files changed, 229 insertions(+), 442 deletions(-)
src/Air.zig+2-1| ... | @@ -503,7 +503,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -503,7 +503,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 503 | pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type { | 503 | pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type { |
| 504 | const ref_int = @enumToInt(ref); | 504 | const ref_int = @enumToInt(ref); |
| 505 | if (ref_int < Air.Inst.Ref.typed_value_map.len) { | 505 | if (ref_int < Air.Inst.Ref.typed_value_map.len) { |
| 506 | return Air.Inst.Ref.typed_value_map[ref_int].val.toType(undefined) catch unreachable; | 506 | var buffer: Value.ToTypeBuffer = undefined; |
| 507 | return Air.Inst.Ref.typed_value_map[ref_int].val.toType(&buffer); | ||
| 507 | } | 508 | } |
| 508 | const inst_index = ref_int - Air.Inst.Ref.typed_value_map.len; | 509 | const inst_index = ref_int - Air.Inst.Ref.typed_value_map.len; |
| 509 | const air_tags = air.instructions.items(.tag); | 510 | const air_tags = air.instructions.items(.tag); |
src/Module.zig+2-9| ... | @@ -4299,7 +4299,6 @@ pub fn simplePtrType( | ... | @@ -4299,7 +4299,6 @@ pub fn simplePtrType( |
| 4299 | } | 4299 | } |
| 4300 | 4300 | ||
| 4301 | pub fn ptrType( | 4301 | pub fn ptrType( |
| 4302 | mod: *Module, | ||
| 4303 | arena: *Allocator, | 4302 | arena: *Allocator, |
| 4304 | elem_ty: Type, | 4303 | elem_ty: Type, |
| 4305 | sentinel: ?Value, | 4304 | sentinel: ?Value, |
| ... | @@ -4311,7 +4310,6 @@ pub fn ptrType( | ... | @@ -4311,7 +4310,6 @@ pub fn ptrType( |
| 4311 | @"volatile": bool, | 4310 | @"volatile": bool, |
| 4312 | size: std.builtin.TypeInfo.Pointer.Size, | 4311 | size: std.builtin.TypeInfo.Pointer.Size, |
| 4313 | ) Allocator.Error!Type { | 4312 | ) Allocator.Error!Type { |
| 4314 | _ = mod; | ||
| 4315 | assert(host_size == 0 or bit_offset < host_size * 8); | 4313 | assert(host_size == 0 or bit_offset < host_size * 8); |
| 4316 | 4314 | ||
| 4317 | // TODO check if type can be represented by simplePtrType | 4315 | // TODO check if type can be represented by simplePtrType |
| ... | @@ -4328,8 +4326,7 @@ pub fn ptrType( | ... | @@ -4328,8 +4326,7 @@ pub fn ptrType( |
| 4328 | }); | 4326 | }); |
| 4329 | } | 4327 | } |
| 4330 | 4328 | ||
| 4331 | pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator.Error!Type { | 4329 | pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type { |
| 4332 | _ = mod; | ||
| 4333 | switch (child_type.tag()) { | 4330 | switch (child_type.tag()) { |
| 4334 | .single_const_pointer => return Type.Tag.optional_single_const_pointer.create( | 4331 | .single_const_pointer => return Type.Tag.optional_single_const_pointer.create( |
| 4335 | arena, | 4332 | arena, |
| ... | @@ -4344,16 +4341,14 @@ pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator | ... | @@ -4344,16 +4341,14 @@ pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator |
| 4344 | } | 4341 | } |
| 4345 | 4342 | ||
| 4346 | pub fn arrayType( | 4343 | pub fn arrayType( |
| 4347 | mod: *Module, | ||
| 4348 | arena: *Allocator, | 4344 | arena: *Allocator, |
| 4349 | len: u64, | 4345 | len: u64, |
| 4350 | sentinel: ?Value, | 4346 | sentinel: ?Value, |
| 4351 | elem_type: Type, | 4347 | elem_type: Type, |
| 4352 | ) Allocator.Error!Type { | 4348 | ) Allocator.Error!Type { |
| 4353 | _ = mod; | ||
| 4354 | if (elem_type.eql(Type.initTag(.u8))) { | 4349 | if (elem_type.eql(Type.initTag(.u8))) { |
| 4355 | if (sentinel) |some| { | 4350 | if (sentinel) |some| { |
| 4356 | if (some.eql(Value.initTag(.zero))) { | 4351 | if (some.eql(Value.initTag(.zero), elem_type)) { |
| 4357 | return Type.Tag.array_u8_sentinel_0.create(arena, len); | 4352 | return Type.Tag.array_u8_sentinel_0.create(arena, len); |
| 4358 | } | 4353 | } |
| 4359 | } else { | 4354 | } else { |
| ... | @@ -4376,12 +4371,10 @@ pub fn arrayType( | ... | @@ -4376,12 +4371,10 @@ pub fn arrayType( |
| 4376 | } | 4371 | } |
| 4377 | 4372 | ||
| 4378 | pub fn errorUnionType( | 4373 | pub fn errorUnionType( |
| 4379 | mod: *Module, | ||
| 4380 | arena: *Allocator, | 4374 | arena: *Allocator, |
| 4381 | error_set: Type, | 4375 | error_set: Type, |
| 4382 | payload: Type, | 4376 | payload: Type, |
| 4383 | ) Allocator.Error!Type { | 4377 | ) Allocator.Error!Type { |
| 4384 | _ = mod; | ||
| 4385 | assert(error_set.zigTypeTag() == .ErrorSet); | 4378 | assert(error_set.zigTypeTag() == .ErrorSet); |
| 4386 | if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) { | 4379 | if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) { |
| 4387 | return Type.initTag(.anyerror_void_error_union); | 4380 | return Type.initTag(.anyerror_void_error_union); |
src/RangeSet.zig+15-8| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Order = std.math.Order; | 2 | const Order = std.math.Order; |
| 3 | const Type = @import("type.zig").Type; | ||
| 3 | const Value = @import("value.zig").Value; | 4 | const Value = @import("value.zig").Value; |
| 4 | const RangeSet = @This(); | 5 | const RangeSet = @This(); |
| 5 | const SwitchProngSrc = @import("Module.zig").SwitchProngSrc; | 6 | const SwitchProngSrc = @import("Module.zig").SwitchProngSrc; |
| ... | @@ -22,9 +23,15 @@ pub fn deinit(self: *RangeSet) void { | ... | @@ -22,9 +23,15 @@ pub fn deinit(self: *RangeSet) void { |
| 22 | self.ranges.deinit(); | 23 | self.ranges.deinit(); |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | pub fn add(self: *RangeSet, first: Value, last: Value, src: SwitchProngSrc) !?SwitchProngSrc { | 26 | pub fn add( |
| 27 | self: *RangeSet, | ||
| 28 | first: Value, | ||
| 29 | last: Value, | ||
| 30 | ty: Type, | ||
| 31 | src: SwitchProngSrc, | ||
| 32 | ) !?SwitchProngSrc { | ||
| 26 | for (self.ranges.items) |range| { | 33 | for (self.ranges.items) |range| { |
| 27 | if (last.compare(.gte, range.first) and first.compare(.lte, range.last)) { | 34 | if (last.compare(.gte, range.first, ty) and first.compare(.lte, range.last, ty)) { |
| 28 | return range.src; // They overlap. | 35 | return range.src; // They overlap. |
| 29 | } | 36 | } |
| 30 | } | 37 | } |
| ... | @@ -37,18 +44,18 @@ pub fn add(self: *RangeSet, first: Value, last: Value, src: SwitchProngSrc) !?Sw | ... | @@ -37,18 +44,18 @@ pub fn add(self: *RangeSet, first: Value, last: Value, src: SwitchProngSrc) !?Sw |
| 37 | } | 44 | } |
| 38 | 45 | ||
| 39 | /// Assumes a and b do not overlap | 46 | /// Assumes a and b do not overlap |
| 40 | fn lessThan(_: void, a: Range, b: Range) bool { | 47 | fn lessThan(ty: Type, a: Range, b: Range) bool { |
| 41 | return a.first.compare(.lt, b.first); | 48 | return a.first.compare(.lt, b.first, ty); |
| 42 | } | 49 | } |
| 43 | 50 | ||
| 44 | pub fn spans(self: *RangeSet, first: Value, last: Value) !bool { | 51 | pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool { |
| 45 | if (self.ranges.items.len == 0) | 52 | if (self.ranges.items.len == 0) |
| 46 | return false; | 53 | return false; |
| 47 | 54 | ||
| 48 | std.sort.sort(Range, self.ranges.items, {}, lessThan); | 55 | std.sort.sort(Range, self.ranges.items, ty, lessThan); |
| 49 | 56 | ||
| 50 | if (!self.ranges.items[0].first.eql(first) or | 57 | if (!self.ranges.items[0].first.eql(first, ty) or |
| 51 | !self.ranges.items[self.ranges.items.len - 1].last.eql(last)) | 58 | !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty)) |
| 52 | { | 59 | { |
| 53 | return false; | 60 | return false; |
| 54 | } | 61 | } |
src/Sema.zig+75-59| ... | @@ -634,7 +634,9 @@ fn analyzeAsType( | ... | @@ -634,7 +634,9 @@ fn analyzeAsType( |
| 634 | const wanted_type = Type.initTag(.@"type"); | 634 | const wanted_type = Type.initTag(.@"type"); |
| 635 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); | 635 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); |
| 636 | const val = try sema.resolveConstValue(block, src, coerced_inst); | 636 | const val = try sema.resolveConstValue(block, src, coerced_inst); |
| 637 | return val.toType(sema.arena); | 637 | var buffer: Value.ToTypeBuffer = undefined; |
| 638 | const ty = val.toType(&buffer); | ||
| 639 | return ty.copy(sema.arena); | ||
| 638 | } | 640 | } |
| 639 | 641 | ||
| 640 | /// May return Value Tags: `variable`, `undef`. | 642 | /// May return Value Tags: `variable`, `undef`. |
| ... | @@ -1022,7 +1024,9 @@ fn zirEnumDecl( | ... | @@ -1022,7 +1024,9 @@ fn zirEnumDecl( |
| 1022 | if (bag != 0) break true; | 1024 | if (bag != 0) break true; |
| 1023 | } else false; | 1025 | } else false; |
| 1024 | if (any_values) { | 1026 | if (any_values) { |
| 1025 | try enum_obj.values.ensureCapacity(&new_decl_arena.allocator, fields_len); | 1027 | try enum_obj.values.ensureTotalCapacityContext(&new_decl_arena.allocator, fields_len, .{ |
| 1028 | .ty = tag_ty, | ||
| 1029 | }); | ||
| 1026 | } | 1030 | } |
| 1027 | 1031 | ||
| 1028 | { | 1032 | { |
| ... | @@ -1100,10 +1104,10 @@ fn zirEnumDecl( | ... | @@ -1100,10 +1104,10 @@ fn zirEnumDecl( |
| 1100 | // that points to this default value expression rather than the struct. | 1104 | // that points to this default value expression rather than the struct. |
| 1101 | // But only resolve the source location if we need to emit a compile error. | 1105 | // But only resolve the source location if we need to emit a compile error. |
| 1102 | const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val; | 1106 | const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val; |
| 1103 | enum_obj.values.putAssumeCapacityNoClobber(tag_val, {}); | 1107 | enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = tag_ty }); |
| 1104 | } else if (any_values) { | 1108 | } else if (any_values) { |
| 1105 | const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i); | 1109 | const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i); |
| 1106 | enum_obj.values.putAssumeCapacityNoClobber(tag_val, {}); | 1110 | enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = tag_ty }); |
| 1107 | } | 1111 | } |
| 1108 | } | 1112 | } |
| 1109 | 1113 | ||
| ... | @@ -2516,7 +2520,7 @@ fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compi | ... | @@ -2516,7 +2520,7 @@ fn zirOptionalType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compi |
| 2516 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 2520 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2517 | const src = inst_data.src(); | 2521 | const src = inst_data.src(); |
| 2518 | const child_type = try sema.resolveType(block, src, inst_data.operand); | 2522 | const child_type = try sema.resolveType(block, src, inst_data.operand); |
| 2519 | const opt_type = try sema.mod.optionalType(sema.arena, child_type); | 2523 | const opt_type = try Module.optionalType(sema.arena, child_type); |
| 2520 | 2524 | ||
| 2521 | return sema.addType(opt_type); | 2525 | return sema.addType(opt_type); |
| 2522 | } | 2526 | } |
| ... | @@ -2547,11 +2551,10 @@ fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE | ... | @@ -2547,11 +2551,10 @@ fn zirArrayType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE |
| 2547 | const tracy = trace(@src()); | 2551 | const tracy = trace(@src()); |
| 2548 | defer tracy.end(); | 2552 | defer tracy.end(); |
| 2549 | 2553 | ||
| 2550 | // TODO these should be lazily evaluated | ||
| 2551 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | 2554 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; |
| 2552 | const len = try sema.resolveInstConst(block, .unneeded, bin_inst.lhs); | 2555 | const len = try sema.resolveInstConst(block, .unneeded, bin_inst.lhs); |
| 2553 | const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs); | 2556 | const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs); |
| 2554 | const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), null, elem_type); | 2557 | const array_ty = try Module.arrayType(sema.arena, len.val.toUnsignedInt(), null, elem_type); |
| 2555 | 2558 | ||
| 2556 | return sema.addType(array_ty); | 2559 | return sema.addType(array_ty); |
| 2557 | } | 2560 | } |
| ... | @@ -2560,13 +2563,12 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) | ... | @@ -2560,13 +2563,12 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) |
| 2560 | const tracy = trace(@src()); | 2563 | const tracy = trace(@src()); |
| 2561 | defer tracy.end(); | 2564 | defer tracy.end(); |
| 2562 | 2565 | ||
| 2563 | // TODO these should be lazily evaluated | ||
| 2564 | const inst_data = sema.code.instructions.items(.data)[inst].array_type_sentinel; | 2566 | const inst_data = sema.code.instructions.items(.data)[inst].array_type_sentinel; |
| 2565 | const len = try sema.resolveInstConst(block, .unneeded, inst_data.len); | 2567 | const len = try sema.resolveInstConst(block, .unneeded, inst_data.len); |
| 2566 | const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; | 2568 | const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; |
| 2567 | const sentinel = try sema.resolveInstConst(block, .unneeded, extra.sentinel); | 2569 | const sentinel = try sema.resolveInstConst(block, .unneeded, extra.sentinel); |
| 2568 | const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type); | 2570 | const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type); |
| 2569 | const array_ty = try sema.mod.arrayType(sema.arena, len.val.toUnsignedInt(), sentinel.val, elem_type); | 2571 | const array_ty = try Module.arrayType(sema.arena, len.val.toUnsignedInt(), sentinel.val, elem_type); |
| 2570 | 2572 | ||
| 2571 | return sema.addType(array_ty); | 2573 | return sema.addType(array_ty); |
| 2572 | } | 2574 | } |
| ... | @@ -2599,7 +2601,7 @@ fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com | ... | @@ -2599,7 +2601,7 @@ fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com |
| 2599 | error_union.elemType(), | 2601 | error_union.elemType(), |
| 2600 | }); | 2602 | }); |
| 2601 | } | 2603 | } |
| 2602 | const err_union_ty = try sema.mod.errorUnionType(sema.arena, error_union, payload); | 2604 | const err_union_ty = try Module.errorUnionType(sema.arena, error_union, payload); |
| 2603 | return sema.addType(err_union_ty); | 2605 | return sema.addType(err_union_ty); |
| 2604 | } | 2606 | } |
| 2605 | 2607 | ||
| ... | @@ -3890,6 +3892,7 @@ fn analyzeSwitch( | ... | @@ -3890,6 +3892,7 @@ fn analyzeSwitch( |
| 3890 | block, | 3892 | block, |
| 3891 | &range_set, | 3893 | &range_set, |
| 3892 | item_ref, | 3894 | item_ref, |
| 3895 | operand_ty, | ||
| 3893 | src_node_offset, | 3896 | src_node_offset, |
| 3894 | .{ .scalar = scalar_i }, | 3897 | .{ .scalar = scalar_i }, |
| 3895 | ); | 3898 | ); |
| ... | @@ -3912,6 +3915,7 @@ fn analyzeSwitch( | ... | @@ -3912,6 +3915,7 @@ fn analyzeSwitch( |
| 3912 | block, | 3915 | block, |
| 3913 | &range_set, | 3916 | &range_set, |
| 3914 | item_ref, | 3917 | item_ref, |
| 3918 | operand_ty, | ||
| 3915 | src_node_offset, | 3919 | src_node_offset, |
| 3916 | .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, | 3920 | .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, |
| 3917 | ); | 3921 | ); |
| ... | @@ -3929,6 +3933,7 @@ fn analyzeSwitch( | ... | @@ -3929,6 +3933,7 @@ fn analyzeSwitch( |
| 3929 | &range_set, | 3933 | &range_set, |
| 3930 | item_first, | 3934 | item_first, |
| 3931 | item_last, | 3935 | item_last, |
| 3936 | operand_ty, | ||
| 3932 | src_node_offset, | 3937 | src_node_offset, |
| 3933 | .{ .range = .{ .prong = multi_i, .item = range_i } }, | 3938 | .{ .range = .{ .prong = multi_i, .item = range_i } }, |
| 3934 | ); | 3939 | ); |
| ... | @@ -3945,7 +3950,7 @@ fn analyzeSwitch( | ... | @@ -3945,7 +3950,7 @@ fn analyzeSwitch( |
| 3945 | 3950 | ||
| 3946 | const min_int = try operand_ty.minInt(&arena, mod.getTarget()); | 3951 | const min_int = try operand_ty.minInt(&arena, mod.getTarget()); |
| 3947 | const max_int = try operand_ty.maxInt(&arena, mod.getTarget()); | 3952 | const max_int = try operand_ty.maxInt(&arena, mod.getTarget()); |
| 3948 | if (try range_set.spans(min_int, max_int)) { | 3953 | if (try range_set.spans(min_int, max_int, operand_ty)) { |
| 3949 | if (special_prong == .@"else") { | 3954 | if (special_prong == .@"else") { |
| 3950 | return mod.fail( | 3955 | return mod.fail( |
| 3951 | &block.base, | 3956 | &block.base, |
| ... | @@ -4050,7 +4055,7 @@ fn analyzeSwitch( | ... | @@ -4050,7 +4055,7 @@ fn analyzeSwitch( |
| 4050 | ); | 4055 | ); |
| 4051 | } | 4056 | } |
| 4052 | 4057 | ||
| 4053 | var seen_values = ValueSrcMap.init(gpa); | 4058 | var seen_values = ValueSrcMap.initContext(gpa, .{ .ty = operand_ty }); |
| 4054 | defer seen_values.deinit(); | 4059 | defer seen_values.deinit(); |
| 4055 | 4060 | ||
| 4056 | var extra_index: usize = special.end; | 4061 | var extra_index: usize = special.end; |
| ... | @@ -4161,7 +4166,7 @@ fn analyzeSwitch( | ... | @@ -4161,7 +4166,7 @@ fn analyzeSwitch( |
| 4161 | const item = sema.resolveInst(item_ref); | 4166 | const item = sema.resolveInst(item_ref); |
| 4162 | // Validation above ensured these will succeed. | 4167 | // Validation above ensured these will succeed. |
| 4163 | const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; | 4168 | const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; |
| 4164 | if (operand_val.eql(item_val)) { | 4169 | if (operand_val.eql(item_val, operand_ty)) { |
| 4165 | return sema.resolveBlockBody(block, src, &child_block, body, merges); | 4170 | return sema.resolveBlockBody(block, src, &child_block, body, merges); |
| 4166 | } | 4171 | } |
| 4167 | } | 4172 | } |
| ... | @@ -4183,7 +4188,7 @@ fn analyzeSwitch( | ... | @@ -4183,7 +4188,7 @@ fn analyzeSwitch( |
| 4183 | const item = sema.resolveInst(item_ref); | 4188 | const item = sema.resolveInst(item_ref); |
| 4184 | // Validation above ensured these will succeed. | 4189 | // Validation above ensured these will succeed. |
| 4185 | const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; | 4190 | const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; |
| 4186 | if (operand_val.eql(item_val)) { | 4191 | if (operand_val.eql(item_val, operand_ty)) { |
| 4187 | return sema.resolveBlockBody(block, src, &child_block, body, merges); | 4192 | return sema.resolveBlockBody(block, src, &child_block, body, merges); |
| 4188 | } | 4193 | } |
| 4189 | } | 4194 | } |
| ... | @@ -4198,8 +4203,8 @@ fn analyzeSwitch( | ... | @@ -4198,8 +4203,8 @@ fn analyzeSwitch( |
| 4198 | // Validation above ensured these will succeed. | 4203 | // Validation above ensured these will succeed. |
| 4199 | const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable; | 4204 | const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable; |
| 4200 | const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable; | 4205 | const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable; |
| 4201 | if (Value.compare(operand_val, .gte, first_tv.val) and | 4206 | if (Value.compare(operand_val, .gte, first_tv.val, operand_ty) and |
| 4202 | Value.compare(operand_val, .lte, last_tv.val)) | 4207 | Value.compare(operand_val, .lte, last_tv.val, operand_ty)) |
| 4203 | { | 4208 | { |
| 4204 | return sema.resolveBlockBody(block, src, &child_block, body, merges); | 4209 | return sema.resolveBlockBody(block, src, &child_block, body, merges); |
| 4205 | } | 4210 | } |
| ... | @@ -4450,12 +4455,13 @@ fn validateSwitchRange( | ... | @@ -4450,12 +4455,13 @@ fn validateSwitchRange( |
| 4450 | range_set: *RangeSet, | 4455 | range_set: *RangeSet, |
| 4451 | first_ref: Zir.Inst.Ref, | 4456 | first_ref: Zir.Inst.Ref, |
| 4452 | last_ref: Zir.Inst.Ref, | 4457 | last_ref: Zir.Inst.Ref, |
| 4458 | operand_ty: Type, | ||
| 4453 | src_node_offset: i32, | 4459 | src_node_offset: i32, |
| 4454 | switch_prong_src: Module.SwitchProngSrc, | 4460 | switch_prong_src: Module.SwitchProngSrc, |
| 4455 | ) CompileError!void { | 4461 | ) CompileError!void { |
| 4456 | const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val; | 4462 | const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val; |
| 4457 | const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val; | 4463 | const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val; |
| 4458 | const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src); | 4464 | const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src); |
| 4459 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); | 4465 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); |
| 4460 | } | 4466 | } |
| 4461 | 4467 | ||
| ... | @@ -4464,11 +4470,12 @@ fn validateSwitchItem( | ... | @@ -4464,11 +4470,12 @@ fn validateSwitchItem( |
| 4464 | block: *Scope.Block, | 4470 | block: *Scope.Block, |
| 4465 | range_set: *RangeSet, | 4471 | range_set: *RangeSet, |
| 4466 | item_ref: Zir.Inst.Ref, | 4472 | item_ref: Zir.Inst.Ref, |
| 4473 | operand_ty: Type, | ||
| 4467 | src_node_offset: i32, | 4474 | src_node_offset: i32, |
| 4468 | switch_prong_src: Module.SwitchProngSrc, | 4475 | switch_prong_src: Module.SwitchProngSrc, |
| 4469 | ) CompileError!void { | 4476 | ) CompileError!void { |
| 4470 | const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val; | 4477 | const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val; |
| 4471 | const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src); | 4478 | const maybe_prev_src = try range_set.add(item_val, item_val, operand_ty, switch_prong_src); |
| 4472 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); | 4479 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); |
| 4473 | } | 4480 | } |
| 4474 | 4481 | ||
| ... | @@ -5137,20 +5144,26 @@ fn zirCmp( | ... | @@ -5137,20 +5144,26 @@ fn zirCmp( |
| 5137 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); | 5144 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 5138 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); | 5145 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 5139 | 5146 | ||
| 5140 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { | 5147 | const runtime_src: LazySrcLoc = src: { |
| 5141 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { | 5148 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { |
| 5142 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 5149 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { |
| 5143 | return sema.addConstUndef(resolved_type); | 5150 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 5144 | } | 5151 | return sema.addConstUndef(resolved_type); |
| 5145 | if (lhs_val.compare(op, rhs_val)) { | 5152 | } |
| 5146 | return Air.Inst.Ref.bool_true; | 5153 | if (lhs_val.compare(op, rhs_val, resolved_type)) { |
| 5154 | return Air.Inst.Ref.bool_true; | ||
| 5155 | } else { | ||
| 5156 | return Air.Inst.Ref.bool_false; | ||
| 5157 | } | ||
| 5147 | } else { | 5158 | } else { |
| 5148 | return Air.Inst.Ref.bool_false; | 5159 | break :src rhs_src; |
| 5149 | } | 5160 | } |
| 5161 | } else { | ||
| 5162 | break :src lhs_src; | ||
| 5150 | } | 5163 | } |
| 5151 | } | 5164 | }; |
| 5165 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 5152 | 5166 | ||
| 5153 | try sema.requireRuntimeBlock(block, src); | ||
| 5154 | const tag: Air.Inst.Tag = switch (op) { | 5167 | const tag: Air.Inst.Tag = switch (op) { |
| 5155 | .lt => .cmp_lt, | 5168 | .lt => .cmp_lt, |
| 5156 | .lte => .cmp_lte, | 5169 | .lte => .cmp_lte, |
| ... | @@ -5626,7 +5639,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp | ... | @@ -5626,7 +5639,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp |
| 5626 | 5639 | ||
| 5627 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; | 5640 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; |
| 5628 | const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type); | 5641 | const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type); |
| 5629 | const ty = try sema.mod.ptrType( | 5642 | const ty = try Module.ptrType( |
| 5630 | sema.arena, | 5643 | sema.arena, |
| 5631 | elem_type, | 5644 | elem_type, |
| 5632 | null, | 5645 | null, |
| ... | @@ -5680,7 +5693,7 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr | ... | @@ -5680,7 +5693,7 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 5680 | 5693 | ||
| 5681 | const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type); | 5694 | const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type); |
| 5682 | 5695 | ||
| 5683 | const ty = try sema.mod.ptrType( | 5696 | const ty = try Module.ptrType( |
| 5684 | sema.arena, | 5697 | sema.arena, |
| 5685 | elem_type, | 5698 | elem_type, |
| 5686 | sentinel, | 5699 | sentinel, |
| ... | @@ -6569,7 +6582,7 @@ fn panicWithMsg( | ... | @@ -6569,7 +6582,7 @@ fn panicWithMsg( |
| 6569 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); | 6582 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); |
| 6570 | const ptr_stack_trace_ty = try Module.simplePtrType(arena, stack_trace_ty, true, .One); | 6583 | const ptr_stack_trace_ty = try Module.simplePtrType(arena, stack_trace_ty, true, .One); |
| 6571 | const null_stack_trace = try sema.addConstant( | 6584 | const null_stack_trace = try sema.addConstant( |
| 6572 | try mod.optionalType(arena, ptr_stack_trace_ty), | 6585 | try Module.optionalType(arena, ptr_stack_trace_ty), |
| 6573 | Value.initTag(.null_value), | 6586 | Value.initTag(.null_value), |
| 6574 | ); | 6587 | ); |
| 6575 | const args = try arena.create([2]Air.Inst.Ref); | 6588 | const args = try arena.create([2]Air.Inst.Ref); |
| ... | @@ -6713,7 +6726,8 @@ fn fieldVal( | ... | @@ -6713,7 +6726,8 @@ fn fieldVal( |
| 6713 | }, | 6726 | }, |
| 6714 | .Type => { | 6727 | .Type => { |
| 6715 | const val = (try sema.resolveDefinedValue(block, object_src, object)).?; | 6728 | const val = (try sema.resolveDefinedValue(block, object_src, object)).?; |
| 6716 | const child_type = try val.toType(arena); | 6729 | var to_type_buffer: Value.ToTypeBuffer = undefined; |
| 6730 | const child_type = val.toType(&to_type_buffer); | ||
| 6717 | switch (child_type.zigTypeTag()) { | 6731 | switch (child_type.zigTypeTag()) { |
| 6718 | .ErrorSet => { | 6732 | .ErrorSet => { |
| 6719 | // TODO resolve inferred error sets | 6733 | // TODO resolve inferred error sets |
| ... | @@ -6733,7 +6747,7 @@ fn fieldVal( | ... | @@ -6733,7 +6747,7 @@ fn fieldVal( |
| 6733 | } else (try mod.getErrorValue(field_name)).key; | 6747 | } else (try mod.getErrorValue(field_name)).key; |
| 6734 | 6748 | ||
| 6735 | return sema.addConstant( | 6749 | return sema.addConstant( |
| 6736 | child_type, | 6750 | try child_type.copy(arena), |
| 6737 | try Value.Tag.@"error".create(arena, .{ .name = name }), | 6751 | try Value.Tag.@"error".create(arena, .{ .name = name }), |
| 6738 | ); | 6752 | ); |
| 6739 | }, | 6753 | }, |
| ... | @@ -6781,7 +6795,7 @@ fn fieldVal( | ... | @@ -6781,7 +6795,7 @@ fn fieldVal( |
| 6781 | }; | 6795 | }; |
| 6782 | const field_index_u32 = @intCast(u32, field_index); | 6796 | const field_index_u32 = @intCast(u32, field_index); |
| 6783 | const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32); | 6797 | const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32); |
| 6784 | return sema.addConstant(child_type, enum_val); | 6798 | return sema.addConstant(try child_type.copy(arena), enum_val); |
| 6785 | }, | 6799 | }, |
| 6786 | else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}), | 6800 | else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}), |
| 6787 | } | 6801 | } |
| ... | @@ -6805,7 +6819,6 @@ fn fieldPtr( | ... | @@ -6805,7 +6819,6 @@ fn fieldPtr( |
| 6805 | // in `fieldVal`. This function takes a pointer and returns a pointer. | 6819 | // in `fieldVal`. This function takes a pointer and returns a pointer. |
| 6806 | 6820 | ||
| 6807 | const mod = sema.mod; | 6821 | const mod = sema.mod; |
| 6808 | const arena = sema.arena; | ||
| 6809 | const object_ptr_src = src; // TODO better source location | 6822 | const object_ptr_src = src; // TODO better source location |
| 6810 | const object_ptr_ty = sema.typeOf(object_ptr); | 6823 | const object_ptr_ty = sema.typeOf(object_ptr); |
| 6811 | const object_ty = switch (object_ptr_ty.zigTypeTag()) { | 6824 | const object_ty = switch (object_ptr_ty.zigTypeTag()) { |
| ... | @@ -6887,7 +6900,8 @@ fn fieldPtr( | ... | @@ -6887,7 +6900,8 @@ fn fieldPtr( |
| 6887 | _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr); | 6900 | _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr); |
| 6888 | const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); | 6901 | const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); |
| 6889 | const val = (sema.resolveDefinedValue(block, src, result) catch unreachable).?; | 6902 | const val = (sema.resolveDefinedValue(block, src, result) catch unreachable).?; |
| 6890 | const child_type = try val.toType(arena); | 6903 | var to_type_buffer: Value.ToTypeBuffer = undefined; |
| 6904 | const child_type = val.toType(&to_type_buffer); | ||
| 6891 | switch (child_type.zigTypeTag()) { | 6905 | switch (child_type.zigTypeTag()) { |
| 6892 | .ErrorSet => { | 6906 | .ErrorSet => { |
| 6893 | // TODO resolve inferred error sets | 6907 | // TODO resolve inferred error sets |
| ... | @@ -6902,15 +6916,14 @@ fn fieldPtr( | ... | @@ -6902,15 +6916,14 @@ fn fieldPtr( |
| 6902 | } | 6916 | } |
| 6903 | } | 6917 | } |
| 6904 | return mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ | 6918 | return mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ |
| 6905 | field_name, | 6919 | field_name, child_type, |
| 6906 | child_type, | ||
| 6907 | }); | 6920 | }); |
| 6908 | } else (try mod.getErrorValue(field_name)).key; | 6921 | } else (try mod.getErrorValue(field_name)).key; |
| 6909 | 6922 | ||
| 6910 | var anon_decl = try block.startAnonDecl(); | 6923 | var anon_decl = try block.startAnonDecl(); |
| 6911 | defer anon_decl.deinit(); | 6924 | defer anon_decl.deinit(); |
| 6912 | return sema.analyzeDeclRef(try anon_decl.finish( | 6925 | return sema.analyzeDeclRef(try anon_decl.finish( |
| 6913 | child_type, | 6926 | try child_type.copy(anon_decl.arena()), |
| 6914 | try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }), | 6927 | try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }), |
| 6915 | )); | 6928 | )); |
| 6916 | }, | 6929 | }, |
| ... | @@ -6960,7 +6973,7 @@ fn fieldPtr( | ... | @@ -6960,7 +6973,7 @@ fn fieldPtr( |
| 6960 | var anon_decl = try block.startAnonDecl(); | 6973 | var anon_decl = try block.startAnonDecl(); |
| 6961 | defer anon_decl.deinit(); | 6974 | defer anon_decl.deinit(); |
| 6962 | return sema.analyzeDeclRef(try anon_decl.finish( | 6975 | return sema.analyzeDeclRef(try anon_decl.finish( |
| 6963 | child_type, | 6976 | try child_type.copy(anon_decl.arena()), |
| 6964 | try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32), | 6977 | try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32), |
| 6965 | )); | 6978 | )); |
| 6966 | }, | 6979 | }, |
| ... | @@ -7352,7 +7365,7 @@ fn coerce( | ... | @@ -7352,7 +7365,7 @@ fn coerce( |
| 7352 | 7365 | ||
| 7353 | if (src_sentinel) |src_s| { | 7366 | if (src_sentinel) |src_s| { |
| 7354 | if (dst_sentinel) |dst_s| { | 7367 | if (dst_sentinel) |dst_s| { |
| 7355 | if (src_s.eql(dst_s)) { | 7368 | if (src_s.eql(dst_s, dst_elem_type)) { |
| 7356 | return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src); | 7369 | return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src); |
| 7357 | } | 7370 | } |
| 7358 | } | 7371 | } |
| ... | @@ -7474,7 +7487,7 @@ fn coerceNum( | ... | @@ -7474,7 +7487,7 @@ fn coerceNum( |
| 7474 | } | 7487 | } |
| 7475 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { | 7488 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { |
| 7476 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | 7489 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { |
| 7477 | const res = val.floatCast(sema.arena, dest_type, target) catch |err| switch (err) { | 7490 | const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) { |
| 7478 | error.Overflow => return sema.mod.fail( | 7491 | error.Overflow => return sema.mod.fail( |
| 7479 | &block.base, | 7492 | &block.base, |
| 7480 | inst_src, | 7493 | inst_src, |
| ... | @@ -7813,12 +7826,12 @@ fn analyzeSlice( | ... | @@ -7813,12 +7826,12 @@ fn analyzeSlice( |
| 7813 | array_type.sentinel() | 7826 | array_type.sentinel() |
| 7814 | else | 7827 | else |
| 7815 | slice_sentinel; | 7828 | slice_sentinel; |
| 7816 | return_elem_type = try sema.mod.arrayType(sema.arena, len, array_sentinel, elem_type); | 7829 | return_elem_type = try Module.arrayType(sema.arena, len, array_sentinel, elem_type); |
| 7817 | return_ptr_size = .One; | 7830 | return_ptr_size = .One; |
| 7818 | } | 7831 | } |
| 7819 | } | 7832 | } |
| 7820 | } | 7833 | } |
| 7821 | const return_type = try sema.mod.ptrType( | 7834 | const return_type = try Module.ptrType( |
| 7822 | sema.arena, | 7835 | sema.arena, |
| 7823 | return_elem_type, | 7836 | return_elem_type, |
| 7824 | if (end_opt == .none) slice_sentinel else null, | 7837 | if (end_opt == .none) slice_sentinel else null, |
| ... | @@ -7858,39 +7871,42 @@ fn cmpNumeric( | ... | @@ -7858,39 +7871,42 @@ fn cmpNumeric( |
| 7858 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | 7871 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { |
| 7859 | if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { | 7872 | if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { |
| 7860 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ | 7873 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ |
| 7861 | lhs_ty.arrayLen(), | 7874 | lhs_ty.arrayLen(), rhs_ty.arrayLen(), |
| 7862 | rhs_ty.arrayLen(), | ||
| 7863 | }); | 7875 | }); |
| 7864 | } | 7876 | } |
| 7865 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{}); | 7877 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{}); |
| 7866 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | 7878 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { |
| 7867 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | 7879 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ |
| 7868 | lhs_ty, | 7880 | lhs_ty, rhs_ty, |
| 7869 | rhs_ty, | ||
| 7870 | }); | 7881 | }); |
| 7871 | } | 7882 | } |
| 7872 | 7883 | ||
| 7873 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { | 7884 | const runtime_src: LazySrcLoc = src: { |
| 7874 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { | 7885 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { |
| 7875 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 7886 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { |
| 7876 | return sema.addConstUndef(Type.initTag(.bool)); | 7887 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 7877 | } | 7888 | return sema.addConstUndef(Type.initTag(.bool)); |
| 7878 | if (Value.compare(lhs_val, op, rhs_val)) { | 7889 | } |
| 7879 | return Air.Inst.Ref.bool_true; | 7890 | if (Value.compareHetero(lhs_val, op, rhs_val)) { |
| 7891 | return Air.Inst.Ref.bool_true; | ||
| 7892 | } else { | ||
| 7893 | return Air.Inst.Ref.bool_false; | ||
| 7894 | } | ||
| 7880 | } else { | 7895 | } else { |
| 7881 | return Air.Inst.Ref.bool_false; | 7896 | break :src rhs_src; |
| 7882 | } | 7897 | } |
| 7898 | } else { | ||
| 7899 | break :src lhs_src; | ||
| 7883 | } | 7900 | } |
| 7884 | } | 7901 | }; |
| 7885 | 7902 | ||
| 7886 | // TODO handle comparisons against lazy zero values | 7903 | // TODO handle comparisons against lazy zero values |
| 7887 | // Some values can be compared against zero without being runtime known or without forcing | 7904 | // Some values can be compared against zero without being runtime known or without forcing |
| 7888 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to | 7905 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to |
| 7889 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout | 7906 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout |
| 7890 | // of this function if we don't need to. | 7907 | // of this function if we don't need to. |
| 7908 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 7891 | 7909 | ||
| 7892 | // It must be a runtime comparison. | ||
| 7893 | try sema.requireRuntimeBlock(block, src); | ||
| 7894 | // For floats, emit a float comparison instruction. | 7910 | // For floats, emit a float comparison instruction. |
| 7895 | const lhs_is_float = switch (lhs_ty_tag) { | 7911 | const lhs_is_float = switch (lhs_ty_tag) { |
| 7896 | .Float, .ComptimeFloat => true, | 7912 | .Float, .ComptimeFloat => true, |
src/type.zig+30-13| ... | @@ -426,7 +426,7 @@ pub const Type = extern union { | ... | @@ -426,7 +426,7 @@ pub const Type = extern union { |
| 426 | const sentinel_b = info_b.sentinel; | 426 | const sentinel_b = info_b.sentinel; |
| 427 | if (sentinel_a) |sa| { | 427 | if (sentinel_a) |sa| { |
| 428 | if (sentinel_b) |sb| { | 428 | if (sentinel_b) |sb| { |
| 429 | if (!sa.eql(sb)) | 429 | if (!sa.eql(sb, info_a.pointee_type)) |
| 430 | return false; | 430 | return false; |
| 431 | } else { | 431 | } else { |
| 432 | return false; | 432 | return false; |
| ... | @@ -455,13 +455,14 @@ pub const Type = extern union { | ... | @@ -455,13 +455,14 @@ pub const Type = extern union { |
| 455 | .Array, .Vector => { | 455 | .Array, .Vector => { |
| 456 | if (a.arrayLen() != b.arrayLen()) | 456 | if (a.arrayLen() != b.arrayLen()) |
| 457 | return false; | 457 | return false; |
| 458 | if (!a.elemType().eql(b.elemType())) | 458 | const elem_ty = a.elemType(); |
| 459 | if (!elem_ty.eql(b.elemType())) | ||
| 459 | return false; | 460 | return false; |
| 460 | const sentinel_a = a.sentinel(); | 461 | const sentinel_a = a.sentinel(); |
| 461 | const sentinel_b = b.sentinel(); | 462 | const sentinel_b = b.sentinel(); |
| 462 | if (sentinel_a) |sa| { | 463 | if (sentinel_a) |sa| { |
| 463 | if (sentinel_b) |sb| { | 464 | if (sentinel_b) |sb| { |
| 464 | return sa.eql(sb); | 465 | return sa.eql(sb, elem_ty); |
| 465 | } else { | 466 | } else { |
| 466 | return false; | 467 | return false; |
| 467 | } | 468 | } |
| ... | @@ -2744,29 +2745,37 @@ pub const Type = extern union { | ... | @@ -2744,29 +2745,37 @@ pub const Type = extern union { |
| 2744 | return @as(usize, payload.data); | 2745 | return @as(usize, payload.data); |
| 2745 | } | 2746 | } |
| 2746 | const S = struct { | 2747 | const S = struct { |
| 2747 | fn fieldWithRange(int_val: Value, end: usize) ?usize { | 2748 | fn fieldWithRange(int_ty: Type, int_val: Value, end: usize) ?usize { |
| 2748 | if (int_val.compareWithZero(.lt)) return null; | 2749 | if (int_val.compareWithZero(.lt)) return null; |
| 2749 | var end_payload: Value.Payload.U64 = .{ | 2750 | var end_payload: Value.Payload.U64 = .{ |
| 2750 | .base = .{ .tag = .int_u64 }, | 2751 | .base = .{ .tag = .int_u64 }, |
| 2751 | .data = end, | 2752 | .data = end, |
| 2752 | }; | 2753 | }; |
| 2753 | const end_val = Value.initPayload(&end_payload.base); | 2754 | const end_val = Value.initPayload(&end_payload.base); |
| 2754 | if (int_val.compare(.gte, end_val)) return null; | 2755 | if (int_val.compare(.gte, end_val, int_ty)) return null; |
| 2755 | return @intCast(usize, int_val.toUnsignedInt()); | 2756 | return @intCast(usize, int_val.toUnsignedInt()); |
| 2756 | } | 2757 | } |
| 2757 | }; | 2758 | }; |
| 2758 | switch (ty.tag()) { | 2759 | switch (ty.tag()) { |
| 2759 | .enum_full, .enum_nonexhaustive => { | 2760 | .enum_full, .enum_nonexhaustive => { |
| 2760 | const enum_full = ty.cast(Payload.EnumFull).?.data; | 2761 | const enum_full = ty.cast(Payload.EnumFull).?.data; |
| 2762 | const tag_ty = enum_full.tag_ty; | ||
| 2761 | if (enum_full.values.count() == 0) { | 2763 | if (enum_full.values.count() == 0) { |
| 2762 | return S.fieldWithRange(enum_tag, enum_full.fields.count()); | 2764 | return S.fieldWithRange(tag_ty, enum_tag, enum_full.fields.count()); |
| 2763 | } else { | 2765 | } else { |
| 2764 | return enum_full.values.getIndex(enum_tag); | 2766 | return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty }); |
| 2765 | } | 2767 | } |
| 2766 | }, | 2768 | }, |
| 2767 | .enum_simple => { | 2769 | .enum_simple => { |
| 2768 | const enum_simple = ty.castTag(.enum_simple).?.data; | 2770 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 2769 | return S.fieldWithRange(enum_tag, enum_simple.fields.count()); | 2771 | const fields_len = enum_simple.fields.count(); |
| 2772 | const bits = std.math.log2_int_ceil(usize, fields_len); | ||
| 2773 | var buffer: Payload.Bits = .{ | ||
| 2774 | .base = .{ .tag = .int_unsigned }, | ||
| 2775 | .data = bits, | ||
| 2776 | }; | ||
| 2777 | const tag_ty = Type.initPayload(&buffer.base); | ||
| 2778 | return S.fieldWithRange(tag_ty, enum_tag, fields_len); | ||
| 2770 | }, | 2779 | }, |
| 2771 | .atomic_ordering, | 2780 | .atomic_ordering, |
| 2772 | .atomic_rmw_op, | 2781 | .atomic_rmw_op, |
| ... | @@ -2875,14 +2884,14 @@ pub const Type = extern union { | ... | @@ -2875,14 +2884,14 @@ pub const Type = extern union { |
| 2875 | /// Asserts the type is an enum. | 2884 | /// Asserts the type is an enum. |
| 2876 | pub fn enumHasInt(ty: Type, int: Value, target: Target) bool { | 2885 | pub fn enumHasInt(ty: Type, int: Value, target: Target) bool { |
| 2877 | const S = struct { | 2886 | const S = struct { |
| 2878 | fn intInRange(int_val: Value, end: usize) bool { | 2887 | fn intInRange(tag_ty: Type, int_val: Value, end: usize) bool { |
| 2879 | if (int_val.compareWithZero(.lt)) return false; | 2888 | if (int_val.compareWithZero(.lt)) return false; |
| 2880 | var end_payload: Value.Payload.U64 = .{ | 2889 | var end_payload: Value.Payload.U64 = .{ |
| 2881 | .base = .{ .tag = .int_u64 }, | 2890 | .base = .{ .tag = .int_u64 }, |
| 2882 | .data = end, | 2891 | .data = end, |
| 2883 | }; | 2892 | }; |
| 2884 | const end_val = Value.initPayload(&end_payload.base); | 2893 | const end_val = Value.initPayload(&end_payload.base); |
| 2885 | if (int_val.compare(.gte, end_val)) return false; | 2894 | if (int_val.compare(.gte, end_val, tag_ty)) return false; |
| 2886 | return true; | 2895 | return true; |
| 2887 | } | 2896 | } |
| 2888 | }; | 2897 | }; |
| ... | @@ -2890,15 +2899,23 @@ pub const Type = extern union { | ... | @@ -2890,15 +2899,23 @@ pub const Type = extern union { |
| 2890 | .enum_nonexhaustive => return int.intFitsInType(ty, target), | 2899 | .enum_nonexhaustive => return int.intFitsInType(ty, target), |
| 2891 | .enum_full => { | 2900 | .enum_full => { |
| 2892 | const enum_full = ty.castTag(.enum_full).?.data; | 2901 | const enum_full = ty.castTag(.enum_full).?.data; |
| 2902 | const tag_ty = enum_full.tag_ty; | ||
| 2893 | if (enum_full.values.count() == 0) { | 2903 | if (enum_full.values.count() == 0) { |
| 2894 | return S.intInRange(int, enum_full.fields.count()); | 2904 | return S.intInRange(tag_ty, int, enum_full.fields.count()); |
| 2895 | } else { | 2905 | } else { |
| 2896 | return enum_full.values.contains(int); | 2906 | return enum_full.values.containsContext(int, .{ .ty = tag_ty }); |
| 2897 | } | 2907 | } |
| 2898 | }, | 2908 | }, |
| 2899 | .enum_simple => { | 2909 | .enum_simple => { |
| 2900 | const enum_simple = ty.castTag(.enum_simple).?.data; | 2910 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 2901 | return S.intInRange(int, enum_simple.fields.count()); | 2911 | const fields_len = enum_simple.fields.count(); |
| 2912 | const bits = std.math.log2_int_ceil(usize, fields_len); | ||
| 2913 | var buffer: Payload.Bits = .{ | ||
| 2914 | .base = .{ .tag = .int_unsigned }, | ||
| 2915 | .data = bits, | ||
| 2916 | }; | ||
| 2917 | const tag_ty = Type.initPayload(&buffer.base); | ||
| 2918 | return S.intInRange(tag_ty, int, fields_len); | ||
| 2902 | }, | 2919 | }, |
| 2903 | .atomic_ordering, | 2920 | .atomic_ordering, |
| 2904 | .atomic_rmw_op, | 2921 | .atomic_rmw_op, |
src/value.zig+105-352| ... | @@ -653,8 +653,10 @@ pub const Value = extern union { | ... | @@ -653,8 +653,10 @@ pub const Value = extern union { |
| 653 | unreachable; | 653 | unreachable; |
| 654 | } | 654 | } |
| 655 | 655 | ||
| 656 | pub const ToTypeBuffer = Type.Payload.Bits; | ||
| 657 | |||
| 656 | /// Asserts that the value is representable as a type. | 658 | /// Asserts that the value is representable as a type. |
| 657 | pub fn toType(self: Value, allocator: *Allocator) !Type { | 659 | pub fn toType(self: Value, buffer: *ToTypeBuffer) Type { |
| 658 | return switch (self.tag()) { | 660 | return switch (self.tag()) { |
| 659 | .ty => self.castTag(.ty).?.data, | 661 | .ty => self.castTag(.ty).?.data, |
| 660 | .u1_type => Type.initTag(.u1), | 662 | .u1_type => Type.initTag(.u1), |
| ... | @@ -714,14 +716,13 @@ pub const Value = extern union { | ... | @@ -714,14 +716,13 @@ pub const Value = extern union { |
| 714 | 716 | ||
| 715 | .int_type => { | 717 | .int_type => { |
| 716 | const payload = self.castTag(.int_type).?.data; | 718 | const payload = self.castTag(.int_type).?.data; |
| 717 | const new = try allocator.create(Type.Payload.Bits); | 719 | buffer.* = .{ |
| 718 | new.* = .{ | ||
| 719 | .base = .{ | 720 | .base = .{ |
| 720 | .tag = if (payload.signed) .int_signed else .int_unsigned, | 721 | .tag = if (payload.signed) .int_signed else .int_unsigned, |
| 721 | }, | 722 | }, |
| 722 | .data = payload.bits, | 723 | .data = payload.bits, |
| 723 | }; | 724 | }; |
| 724 | return Type.initPayload(&new.base); | 725 | return Type.initPayload(&buffer.base); |
| 725 | }, | 726 | }, |
| 726 | 727 | ||
| 727 | .undef, | 728 | .undef, |
| ... | @@ -958,9 +959,8 @@ pub const Value = extern union { | ... | @@ -958,9 +959,8 @@ pub const Value = extern union { |
| 958 | 959 | ||
| 959 | /// Converts an integer or a float to a float. | 960 | /// Converts an integer or a float to a float. |
| 960 | /// Returns `error.Overflow` if the value does not fit in the new type. | 961 | /// Returns `error.Overflow` if the value does not fit in the new type. |
| 961 | pub fn floatCast(self: Value, allocator: *Allocator, ty: Type, target: Target) !Value { | 962 | pub fn floatCast(self: Value, allocator: *Allocator, dest_ty: Type) !Value { |
| 962 | _ = target; | 963 | switch (dest_ty.tag()) { |
| 963 | switch (ty.tag()) { | ||
| 964 | .f16 => { | 964 | .f16 => { |
| 965 | @panic("TODO add __trunctfhf2 to compiler-rt"); | 965 | @panic("TODO add __trunctfhf2 to compiler-rt"); |
| 966 | //const res = try Value.Tag.float_16.create(allocator, self.toFloat(f16)); | 966 | //const res = try Value.Tag.float_16.create(allocator, self.toFloat(f16)); |
| ... | @@ -970,13 +970,13 @@ pub const Value = extern union { | ... | @@ -970,13 +970,13 @@ pub const Value = extern union { |
| 970 | }, | 970 | }, |
| 971 | .f32 => { | 971 | .f32 => { |
| 972 | const res = try Value.Tag.float_32.create(allocator, self.toFloat(f32)); | 972 | const res = try Value.Tag.float_32.create(allocator, self.toFloat(f32)); |
| 973 | if (!self.eql(res)) | 973 | if (!self.eql(res, dest_ty)) |
| 974 | return error.Overflow; | 974 | return error.Overflow; |
| 975 | return res; | 975 | return res; |
| 976 | }, | 976 | }, |
| 977 | .f64 => { | 977 | .f64 => { |
| 978 | const res = try Value.Tag.float_64.create(allocator, self.toFloat(f64)); | 978 | const res = try Value.Tag.float_64.create(allocator, self.toFloat(f64)); |
| 979 | if (!self.eql(res)) | 979 | if (!self.eql(res, dest_ty)) |
| 980 | return error.Overflow; | 980 | return error.Overflow; |
| 981 | return res; | 981 | return res; |
| 982 | }, | 982 | }, |
| ... | @@ -1083,12 +1083,18 @@ pub const Value = extern union { | ... | @@ -1083,12 +1083,18 @@ pub const Value = extern union { |
| 1083 | return lhs_bigint.order(rhs_bigint); | 1083 | return lhs_bigint.order(rhs_bigint); |
| 1084 | } | 1084 | } |
| 1085 | 1085 | ||
| 1086 | /// Asserts the value is comparable. | 1086 | /// Asserts the value is comparable. Does not take a type parameter because it supports |
| 1087 | pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool { | 1087 | /// comparisons between heterogeneous types. |
| 1088 | pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool { | ||
| 1089 | return order(lhs, rhs).compare(op); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | /// Asserts the value is comparable. Both operands have type `ty`. | ||
| 1093 | pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type) bool { | ||
| 1088 | return switch (op) { | 1094 | return switch (op) { |
| 1089 | .eq => lhs.eql(rhs), | 1095 | .eq => lhs.eql(rhs, ty), |
| 1090 | .neq => !lhs.eql(rhs), | 1096 | .neq => !lhs.eql(rhs, ty), |
| 1091 | else => order(lhs, rhs).compare(op), | 1097 | else => compareHetero(lhs, op, rhs), |
| 1092 | }; | 1098 | }; |
| 1093 | } | 1099 | } |
| 1094 | 1100 | ||
| ... | @@ -1097,11 +1103,11 @@ pub const Value = extern union { | ... | @@ -1097,11 +1103,11 @@ pub const Value = extern union { |
| 1097 | return orderAgainstZero(lhs).compare(op); | 1103 | return orderAgainstZero(lhs).compare(op); |
| 1098 | } | 1104 | } |
| 1099 | 1105 | ||
| 1100 | /// TODO we can't compare value equality without also knowing the type to treat | 1106 | pub fn eql(a: Value, b: Value, ty: Type) bool { |
| 1101 | /// the values as | ||
| 1102 | pub fn eql(a: Value, b: Value) bool { | ||
| 1103 | const a_tag = a.tag(); | 1107 | const a_tag = a.tag(); |
| 1104 | const b_tag = b.tag(); | 1108 | const b_tag = b.tag(); |
| 1109 | assert(a_tag != .undef); | ||
| 1110 | assert(b_tag != .undef); | ||
| 1105 | if (a_tag == b_tag) { | 1111 | if (a_tag == b_tag) { |
| 1106 | switch (a_tag) { | 1112 | switch (a_tag) { |
| 1107 | .void_value, .null_value => return true, | 1113 | .void_value, .null_value => return true, |
| ... | @@ -1118,230 +1124,106 @@ pub const Value = extern union { | ... | @@ -1118,230 +1124,106 @@ pub const Value = extern union { |
| 1118 | else => {}, | 1124 | else => {}, |
| 1119 | } | 1125 | } |
| 1120 | } | 1126 | } |
| 1121 | if (a.isType() and b.isType()) { | 1127 | if (ty.zigTypeTag() == .Type) { |
| 1122 | // 128 bytes should be enough to hold both types | 1128 | var buf_a: ToTypeBuffer = undefined; |
| 1123 | var buf: [128]u8 = undefined; | 1129 | var buf_b: ToTypeBuffer = undefined; |
| 1124 | var fib = std.heap.FixedBufferAllocator.init(&buf); | 1130 | const a_type = a.toType(&buf_a); |
| 1125 | const a_type = a.toType(&fib.allocator) catch unreachable; | 1131 | const b_type = b.toType(&buf_b); |
| 1126 | const b_type = b.toType(&fib.allocator) catch unreachable; | ||
| 1127 | return a_type.eql(b_type); | 1132 | return a_type.eql(b_type); |
| 1128 | } | 1133 | } |
| 1129 | return order(a, b).compare(.eq); | 1134 | return order(a, b).compare(.eq); |
| 1130 | } | 1135 | } |
| 1131 | 1136 | ||
| 1132 | pub fn hash_u32(self: Value) u32 { | 1137 | pub const ArrayHashContext = struct { |
| 1133 | return @truncate(u32, self.hash()); | 1138 | ty: Type, |
| 1134 | } | ||
| 1135 | |||
| 1136 | /// TODO we can't hash without also knowing the type of the value. | ||
| 1137 | /// we have to hash as if there were a canonical value memory layout. | ||
| 1138 | pub fn hash(self: Value) u64 { | ||
| 1139 | var hasher = std.hash.Wyhash.init(0); | ||
| 1140 | 1139 | ||
| 1141 | switch (self.tag()) { | 1140 | pub fn hash(self: @This(), v: Value) u32 { |
| 1142 | .u1_type, | 1141 | const other_context: HashContext = .{ .ty = self.ty }; |
| 1143 | .u8_type, | 1142 | return @truncate(u32, other_context.hash(v)); |
| 1144 | .i8_type, | 1143 | } |
| 1145 | .u16_type, | 1144 | pub fn eql(self: @This(), a: Value, b: Value) bool { |
| 1146 | .i16_type, | 1145 | return a.eql(b, self.ty); |
| 1147 | .u32_type, | 1146 | } |
| 1148 | .i32_type, | 1147 | }; |
| 1149 | .u64_type, | ||
| 1150 | .i64_type, | ||
| 1151 | .u128_type, | ||
| 1152 | .i128_type, | ||
| 1153 | .usize_type, | ||
| 1154 | .isize_type, | ||
| 1155 | .c_short_type, | ||
| 1156 | .c_ushort_type, | ||
| 1157 | .c_int_type, | ||
| 1158 | .c_uint_type, | ||
| 1159 | .c_long_type, | ||
| 1160 | .c_ulong_type, | ||
| 1161 | .c_longlong_type, | ||
| 1162 | .c_ulonglong_type, | ||
| 1163 | .c_longdouble_type, | ||
| 1164 | .f16_type, | ||
| 1165 | .f32_type, | ||
| 1166 | .f64_type, | ||
| 1167 | .f128_type, | ||
| 1168 | .c_void_type, | ||
| 1169 | .bool_type, | ||
| 1170 | .void_type, | ||
| 1171 | .type_type, | ||
| 1172 | .anyerror_type, | ||
| 1173 | .comptime_int_type, | ||
| 1174 | .comptime_float_type, | ||
| 1175 | .noreturn_type, | ||
| 1176 | .null_type, | ||
| 1177 | .undefined_type, | ||
| 1178 | .fn_noreturn_no_args_type, | ||
| 1179 | .fn_void_no_args_type, | ||
| 1180 | .fn_naked_noreturn_no_args_type, | ||
| 1181 | .fn_ccc_void_no_args_type, | ||
| 1182 | .single_const_pointer_to_comptime_int_type, | ||
| 1183 | .anyframe_type, | ||
| 1184 | .const_slice_u8_type, | ||
| 1185 | .enum_literal_type, | ||
| 1186 | .ty, | ||
| 1187 | .abi_align_default, | ||
| 1188 | => { | ||
| 1189 | // Directly return Type.hash, toType can only fail for .int_type. | ||
| 1190 | var allocator = std.heap.FixedBufferAllocator.init(&[_]u8{}); | ||
| 1191 | return (self.toType(&allocator.allocator) catch unreachable).hash(); | ||
| 1192 | }, | ||
| 1193 | .int_type => { | ||
| 1194 | const payload = self.castTag(.int_type).?.data; | ||
| 1195 | var int_payload = Type.Payload.Bits{ | ||
| 1196 | .base = .{ | ||
| 1197 | .tag = if (payload.signed) .int_signed else .int_unsigned, | ||
| 1198 | }, | ||
| 1199 | .data = payload.bits, | ||
| 1200 | }; | ||
| 1201 | return Type.initPayload(&int_payload.base).hash(); | ||
| 1202 | }, | ||
| 1203 | 1148 | ||
| 1204 | .empty_struct_value, | 1149 | pub const HashContext = struct { |
| 1205 | .empty_array, | 1150 | ty: Type, |
| 1206 | => {}, | ||
| 1207 | 1151 | ||
| 1208 | .undef, | 1152 | pub fn hash(self: @This(), v: Value) u64 { |
| 1209 | .null_value, | 1153 | var hasher = std.hash.Wyhash.init(0); |
| 1210 | .void_value, | ||
| 1211 | .unreachable_value, | ||
| 1212 | => std.hash.autoHash(&hasher, self.tag()), | ||
| 1213 | 1154 | ||
| 1214 | .zero, .bool_false => std.hash.autoHash(&hasher, @as(u64, 0)), | 1155 | switch (self.ty.zigTypeTag()) { |
| 1215 | .one, .bool_true => std.hash.autoHash(&hasher, @as(u64, 1)), | 1156 | .BoundFn => unreachable, // TODO remove this from the language |
| 1216 | 1157 | ||
| 1217 | .float_16, .float_32, .float_64, .float_128 => { | 1158 | .Void, |
| 1218 | @panic("TODO implement Value.hash for floats"); | 1159 | .NoReturn, |
| 1219 | }, | 1160 | .Undefined, |
| 1161 | .Null, | ||
| 1162 | => {}, | ||
| 1220 | 1163 | ||
| 1221 | .enum_literal => { | 1164 | .Type => { |
| 1222 | const payload = self.castTag(.enum_literal).?; | 1165 | var buf: ToTypeBuffer = undefined; |
| 1223 | hasher.update(payload.data); | 1166 | return v.toType(&buf).hash(); |
| 1224 | }, | 1167 | }, |
| 1225 | .enum_field_index => { | 1168 | .Bool => { |
| 1226 | const payload = self.castTag(.enum_field_index).?; | 1169 | std.hash.autoHash(&hasher, v.toBool()); |
| 1227 | std.hash.autoHash(&hasher, payload.data); | 1170 | }, |
| 1228 | }, | 1171 | .Int, .ComptimeInt => { |
| 1229 | .bytes => { | 1172 | var space: BigIntSpace = undefined; |
| 1230 | const payload = self.castTag(.bytes).?; | 1173 | const big = v.toBigInt(&space); |
| 1231 | hasher.update(payload.data); | ||
| 1232 | }, | ||
| 1233 | .repeated => { | ||
| 1234 | @panic("TODO Value.hash for repeated"); | ||
| 1235 | }, | ||
| 1236 | .array => { | ||
| 1237 | @panic("TODO Value.hash for array"); | ||
| 1238 | }, | ||
| 1239 | .slice => { | ||
| 1240 | @panic("TODO Value.hash for slice"); | ||
| 1241 | }, | ||
| 1242 | .eu_payload_ptr => { | ||
| 1243 | @panic("TODO Value.hash for eu_payload_ptr"); | ||
| 1244 | }, | ||
| 1245 | .int_u64 => { | ||
| 1246 | const payload = self.castTag(.int_u64).?; | ||
| 1247 | std.hash.autoHash(&hasher, payload.data); | ||
| 1248 | }, | ||
| 1249 | .int_i64 => { | ||
| 1250 | const payload = self.castTag(.int_i64).?; | ||
| 1251 | std.hash.autoHash(&hasher, payload.data); | ||
| 1252 | }, | ||
| 1253 | .comptime_alloc => { | ||
| 1254 | const payload = self.castTag(.comptime_alloc).?; | ||
| 1255 | std.hash.autoHash(&hasher, payload.data.val.hash()); | ||
| 1256 | }, | ||
| 1257 | .int_big_positive, .int_big_negative => { | ||
| 1258 | var space: BigIntSpace = undefined; | ||
| 1259 | const big = self.toBigInt(&space); | ||
| 1260 | if (big.limbs.len == 1) { | ||
| 1261 | // handle like {u,i}64 to ensure same hash as with Int{i,u}64 | ||
| 1262 | if (big.positive) { | ||
| 1263 | std.hash.autoHash(&hasher, @as(u64, big.limbs[0])); | ||
| 1264 | } else { | ||
| 1265 | std.hash.autoHash(&hasher, @as(u64, @bitCast(usize, -@bitCast(isize, big.limbs[0])))); | ||
| 1266 | } | ||
| 1267 | } else { | ||
| 1268 | std.hash.autoHash(&hasher, big.positive); | 1174 | std.hash.autoHash(&hasher, big.positive); |
| 1269 | for (big.limbs) |limb| { | 1175 | for (big.limbs) |limb| { |
| 1270 | std.hash.autoHash(&hasher, limb); | 1176 | std.hash.autoHash(&hasher, limb); |
| 1271 | } | 1177 | } |
| 1272 | } | 1178 | }, |
| 1273 | }, | 1179 | .Float, .ComptimeFloat => { |
| 1274 | .elem_ptr => { | 1180 | @panic("TODO implement hashing float values"); |
| 1275 | const payload = self.castTag(.elem_ptr).?.data; | 1181 | }, |
| 1276 | std.hash.autoHash(&hasher, payload.array_ptr.hash()); | 1182 | .Pointer => { |
| 1277 | std.hash.autoHash(&hasher, payload.index); | 1183 | @panic("TODO implement hashing pointer values"); |
| 1278 | }, | 1184 | }, |
| 1279 | .field_ptr => { | 1185 | .Array, .Vector => { |
| 1280 | const payload = self.castTag(.field_ptr).?.data; | 1186 | @panic("TODO implement hashing array/vector values"); |
| 1281 | std.hash.autoHash(&hasher, payload.container_ptr.hash()); | 1187 | }, |
| 1282 | std.hash.autoHash(&hasher, payload.field_index); | 1188 | .Struct => { |
| 1283 | }, | 1189 | @panic("TODO implement hashing struct values"); |
| 1284 | .decl_ref => { | 1190 | }, |
| 1285 | const decl = self.castTag(.decl_ref).?.data; | 1191 | .Optional => { |
| 1286 | std.hash.autoHash(&hasher, decl); | 1192 | @panic("TODO implement hashing optional values"); |
| 1287 | }, | 1193 | }, |
| 1288 | .function => { | 1194 | .ErrorUnion => { |
| 1289 | const func = self.castTag(.function).?.data; | 1195 | @panic("TODO implement hashing error union values"); |
| 1290 | std.hash.autoHash(&hasher, func); | 1196 | }, |
| 1291 | }, | 1197 | .ErrorSet => { |
| 1292 | .extern_fn => { | 1198 | @panic("TODO implement hashing error set values"); |
| 1293 | const decl = self.castTag(.extern_fn).?.data; | 1199 | }, |
| 1294 | std.hash.autoHash(&hasher, decl); | 1200 | .Enum => { |
| 1295 | }, | 1201 | @panic("TODO implement hashing enum values"); |
| 1296 | .variable => { | 1202 | }, |
| 1297 | const variable = self.castTag(.variable).?.data; | 1203 | .Union => { |
| 1298 | std.hash.autoHash(&hasher, variable); | 1204 | @panic("TODO implement hashing union values"); |
| 1299 | }, | 1205 | }, |
| 1300 | .@"error" => { | 1206 | .Fn => { |
| 1301 | const payload = self.castTag(.@"error").?.data; | 1207 | @panic("TODO implement hashing function values"); |
| 1302 | hasher.update(payload.name); | 1208 | }, |
| 1303 | }, | 1209 | .Opaque => { |
| 1304 | .error_union => { | 1210 | @panic("TODO implement hashing opaque values"); |
| 1305 | const payload = self.castTag(.error_union).?.data; | 1211 | }, |
| 1306 | std.hash.autoHash(&hasher, payload.hash()); | 1212 | .Frame => { |
| 1307 | }, | 1213 | @panic("TODO implement hashing frame values"); |
| 1308 | .inferred_alloc => unreachable, | 1214 | }, |
| 1309 | 1215 | .AnyFrame => { | |
| 1310 | .manyptr_u8_type, | 1216 | @panic("TODO implement hashing anyframe values"); |
| 1311 | .manyptr_const_u8_type, | 1217 | }, |
| 1312 | .atomic_ordering_type, | 1218 | .EnumLiteral => { |
| 1313 | .atomic_rmw_op_type, | 1219 | @panic("TODO implement hashing enum literal values"); |
| 1314 | .calling_convention_type, | 1220 | }, |
| 1315 | .float_mode_type, | 1221 | } |
| 1316 | .reduce_op_type, | 1222 | return hasher.final(); |
| 1317 | .call_options_type, | ||
| 1318 | .export_options_type, | ||
| 1319 | .extern_options_type, | ||
| 1320 | .@"struct", | ||
| 1321 | .@"union", | ||
| 1322 | => @panic("TODO this hash function looks pretty broken. audit it"), | ||
| 1323 | } | 1223 | } |
| 1324 | return hasher.final(); | ||
| 1325 | } | ||
| 1326 | 1224 | ||
| 1327 | pub const ArrayHashContext = struct { | ||
| 1328 | pub fn hash(self: @This(), v: Value) u32 { | ||
| 1329 | _ = self; | ||
| 1330 | return v.hash_u32(); | ||
| 1331 | } | ||
| 1332 | pub fn eql(self: @This(), a: Value, b: Value) bool { | ||
| 1333 | _ = self; | ||
| 1334 | return a.eql(b); | ||
| 1335 | } | ||
| 1336 | }; | ||
| 1337 | pub const HashContext = struct { | ||
| 1338 | pub fn hash(self: @This(), v: Value) u64 { | ||
| 1339 | _ = self; | ||
| 1340 | return v.hash(); | ||
| 1341 | } | ||
| 1342 | pub fn eql(self: @This(), a: Value, b: Value) bool { | 1225 | pub fn eql(self: @This(), a: Value, b: Value) bool { |
| 1343 | _ = self; | 1226 | return a.eql(b, self.ty); |
| 1344 | return a.eql(b); | ||
| 1345 | } | 1227 | } |
| 1346 | }; | 1228 | }; |
| 1347 | 1229 | ||
| ... | @@ -1508,111 +1390,6 @@ pub const Value = extern union { | ... | @@ -1508,111 +1390,6 @@ pub const Value = extern union { |
| 1508 | }; | 1390 | }; |
| 1509 | } | 1391 | } |
| 1510 | 1392 | ||
| 1511 | /// Valid for all types. Asserts the value is not undefined. | ||
| 1512 | /// TODO this function is a code smell and should be deleted | ||
| 1513 | fn isType(self: Value) bool { | ||
| 1514 | return switch (self.tag()) { | ||
| 1515 | .ty, | ||
| 1516 | .int_type, | ||
| 1517 | .u1_type, | ||
| 1518 | .u8_type, | ||
| 1519 | .i8_type, | ||
| 1520 | .u16_type, | ||
| 1521 | .i16_type, | ||
| 1522 | .u32_type, | ||
| 1523 | .i32_type, | ||
| 1524 | .u64_type, | ||
| 1525 | .i64_type, | ||
| 1526 | .u128_type, | ||
| 1527 | .i128_type, | ||
| 1528 | .usize_type, | ||
| 1529 | .isize_type, | ||
| 1530 | .c_short_type, | ||
| 1531 | .c_ushort_type, | ||
| 1532 | .c_int_type, | ||
| 1533 | .c_uint_type, | ||
| 1534 | .c_long_type, | ||
| 1535 | .c_ulong_type, | ||
| 1536 | .c_longlong_type, | ||
| 1537 | .c_ulonglong_type, | ||
| 1538 | .c_longdouble_type, | ||
| 1539 | .f16_type, | ||
| 1540 | .f32_type, | ||
| 1541 | .f64_type, | ||
| 1542 | .f128_type, | ||
| 1543 | .c_void_type, | ||
| 1544 | .bool_type, | ||
| 1545 | .void_type, | ||
| 1546 | .type_type, | ||
| 1547 | .anyerror_type, | ||
| 1548 | .comptime_int_type, | ||
| 1549 | .comptime_float_type, | ||
| 1550 | .noreturn_type, | ||
| 1551 | .null_type, | ||
| 1552 | .undefined_type, | ||
| 1553 | .fn_noreturn_no_args_type, | ||
| 1554 | .fn_void_no_args_type, | ||
| 1555 | .fn_naked_noreturn_no_args_type, | ||
| 1556 | .fn_ccc_void_no_args_type, | ||
| 1557 | .single_const_pointer_to_comptime_int_type, | ||
| 1558 | .anyframe_type, | ||
| 1559 | .const_slice_u8_type, | ||
| 1560 | .enum_literal_type, | ||
| 1561 | .manyptr_u8_type, | ||
| 1562 | .manyptr_const_u8_type, | ||
| 1563 | .atomic_ordering_type, | ||
| 1564 | .atomic_rmw_op_type, | ||
| 1565 | .calling_convention_type, | ||
| 1566 | .float_mode_type, | ||
| 1567 | .reduce_op_type, | ||
| 1568 | .call_options_type, | ||
| 1569 | .export_options_type, | ||
| 1570 | .extern_options_type, | ||
| 1571 | => true, | ||
| 1572 | |||
| 1573 | .zero, | ||
| 1574 | .one, | ||
| 1575 | .empty_array, | ||
| 1576 | .bool_true, | ||
| 1577 | .bool_false, | ||
| 1578 | .function, | ||
| 1579 | .extern_fn, | ||
| 1580 | .variable, | ||
| 1581 | .int_u64, | ||
| 1582 | .int_i64, | ||
| 1583 | .int_big_positive, | ||
| 1584 | .int_big_negative, | ||
| 1585 | .comptime_alloc, | ||
| 1586 | .decl_ref, | ||
| 1587 | .elem_ptr, | ||
| 1588 | .field_ptr, | ||
| 1589 | .bytes, | ||
| 1590 | .repeated, | ||
| 1591 | .array, | ||
| 1592 | .slice, | ||
| 1593 | .float_16, | ||
| 1594 | .float_32, | ||
| 1595 | .float_64, | ||
| 1596 | .float_128, | ||
| 1597 | .void_value, | ||
| 1598 | .enum_literal, | ||
| 1599 | .enum_field_index, | ||
| 1600 | .@"error", | ||
| 1601 | .error_union, | ||
| 1602 | .empty_struct_value, | ||
| 1603 | .@"struct", | ||
| 1604 | .@"union", | ||
| 1605 | .null_value, | ||
| 1606 | .abi_align_default, | ||
| 1607 | .eu_payload_ptr, | ||
| 1608 | => false, | ||
| 1609 | |||
| 1610 | .undef => unreachable, | ||
| 1611 | .unreachable_value => unreachable, | ||
| 1612 | .inferred_alloc => unreachable, | ||
| 1613 | }; | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | /// This type is not copyable since it may contain pointers to its inner data. | 1393 | /// This type is not copyable since it may contain pointers to its inner data. |
| 1617 | pub const Payload = struct { | 1394 | pub const Payload = struct { |
| 1618 | tag: Tag, | 1395 | tag: Tag, |
| ... | @@ -1806,27 +1583,3 @@ pub const Value = extern union { | ... | @@ -1806,27 +1583,3 @@ pub const Value = extern union { |
| 1806 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, | 1583 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, |
| 1807 | }; | 1584 | }; |
| 1808 | }; | 1585 | }; |
| 1809 | |||
| 1810 | test "hash same value different representation" { | ||
| 1811 | const zero_1 = Value.initTag(.zero); | ||
| 1812 | var payload_1 = Value.Payload.U64{ | ||
| 1813 | .base = .{ .tag = .int_u64 }, | ||
| 1814 | .data = 0, | ||
| 1815 | }; | ||
| 1816 | const zero_2 = Value.initPayload(&payload_1.base); | ||
| 1817 | try std.testing.expectEqual(zero_1.hash(), zero_2.hash()); | ||
| 1818 | |||
| 1819 | var payload_2 = Value.Payload.I64{ | ||
| 1820 | .base = .{ .tag = .int_i64 }, | ||
| 1821 | .data = 0, | ||
| 1822 | }; | ||
| 1823 | const zero_3 = Value.initPayload(&payload_2.base); | ||
| 1824 | try std.testing.expectEqual(zero_2.hash(), zero_3.hash()); | ||
| 1825 | |||
| 1826 | var payload_3 = Value.Payload.BigInt{ | ||
| 1827 | .base = .{ .tag = .int_big_negative }, | ||
| 1828 | .data = &[_]std.math.big.Limb{0}, | ||
| 1829 | }; | ||
| 1830 | const zero_4 = Value.initPayload(&payload_3.base); | ||
| 1831 | try std.testing.expectEqual(zero_3.hash(), zero_4.hash()); | ||
| 1832 | } |