authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-30 16:05:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-30 16:17:59-07:00
log507dc1f2e7fac212e79f152e557cbec98a3c30e9
tree34b057caf8ebadef2d199af39b1b44fca48c1a23
parent84039a57e4684e8df10e657bb76c6acb3fb89238

stage2: fix hashing and comparison design flaw with Value

* `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 {
503pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type {503pub 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}
43004300
4301pub fn ptrType(4301pub 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);
43164314
4317 // TODO check if type can be represented by simplePtrType4315 // 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}
43304328
4331pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator.Error!Type {4329pub 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}
43454342
4346pub fn arrayType(4343pub 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}
43774372
4378pub fn errorUnionType(4373pub 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 @@
1const std = @import("std");1const std = @import("std");
2const Order = std.math.Order;2const Order = std.math.Order;
3const Type = @import("type.zig").Type;
3const Value = @import("value.zig").Value;4const Value = @import("value.zig").Value;
4const RangeSet = @This();5const RangeSet = @This();
5const SwitchProngSrc = @import("Module.zig").SwitchProngSrc;6const 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}
2425
25pub fn add(self: *RangeSet, first: Value, last: Value, src: SwitchProngSrc) !?SwitchProngSrc {26pub 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}
3845
39/// Assumes a and b do not overlap46/// Assumes a and b do not overlap
40fn lessThan(_: void, a: Range, b: Range) bool {47fn 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}
4350
44pub fn spans(self: *RangeSet, first: Value, last: Value) !bool {51pub 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;
4754
48 std.sort.sort(Range, self.ranges.items, {}, lessThan);55 std.sort.sort(Range, self.ranges.items, ty, lessThan);
4956
50 if (!self.ranges.items[0].first.eql(first) or57 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}
639641
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 }
10271031
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 }
11091113
...@@ -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);
25202524
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();
25492553
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);
25552558
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();
25622565
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);
25702572
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}
26052607
...@@ -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(
39453950
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 }
40524057
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();
40554060
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) and4206 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}
44614467
...@@ -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}
44744481
...@@ -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);
51395146
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);
51525166
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
56265639
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
56805693
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);
56825695
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 sets6733 // 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;
67346748
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.
68066820
6807 const mod = sema.mod;6821 const mod = sema.mod;
6808 const arena = sema.arena;
6809 const object_ptr_src = src; // TODO better source location6822 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 sets6907 // 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;
69096922
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(
73527365
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 else7827 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 }
78727883
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 };
78857902
7886 // TODO handle comparisons against lazy zero values7903 // TODO handle comparisons against lazy zero values
7887 // Some values can be compared against zero without being runtime known or without forcing7904 // 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 to7905 // 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 layout7906 // 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);
78917909
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 }
655655
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 {
714716
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 },
726727
727 .undef,728 .undef,
...@@ -958,9 +959,8 @@ pub const Value = extern union {...@@ -958,9 +959,8 @@ pub const Value = extern union {
958959
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 }
10851085
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 }
10941100
...@@ -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 }
10991105
1100 /// TODO we can't compare value equality without also knowing the type to treat1106 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 types1128 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 }
11311136
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);
11401139
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 },
12031148
1204 .empty_struct_value,1149 pub const HashContext = struct {
1205 .empty_array,1150 ty: Type,
1206 => {},
12071151
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()),
12131154
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
12161157
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 => {},
12201163
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 },
13091215 .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 }
13261224
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 };
13471229
...@@ -1508,111 +1390,6 @@ pub const Value = extern union {...@@ -1508,111 +1390,6 @@ pub const Value = extern union {
1508 };1390 };
1509 }1391 }
15101392
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
1810test "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}