| 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 | 503 | pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type { |
| 504 | 504 | const ref_int = @enumToInt(ref); |
| 505 | 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 | 509 | const inst_index = ref_int - Air.Inst.Ref.typed_value_map.len; |
| 509 | 510 | const air_tags = air.instructions.items(.tag); |
src/Module.zig+2-9| ... | ... | @@ -4299,7 +4299,6 @@ pub fn simplePtrType( |
| 4299 | 4299 | } |
| 4300 | 4300 | |
| 4301 | 4301 | pub fn ptrType( |
| 4302 | mod: *Module, | |
| 4303 | 4302 | arena: *Allocator, |
| 4304 | 4303 | elem_ty: Type, |
| 4305 | 4304 | sentinel: ?Value, |
| ... | ... | @@ -4311,7 +4310,6 @@ pub fn ptrType( |
| 4311 | 4310 | @"volatile": bool, |
| 4312 | 4311 | size: std.builtin.TypeInfo.Pointer.Size, |
| 4313 | 4312 | ) Allocator.Error!Type { |
| 4314 | _ = mod; | |
| 4315 | 4313 | assert(host_size == 0 or bit_offset < host_size * 8); |
| 4316 | 4314 | |
| 4317 | 4315 | // TODO check if type can be represented by simplePtrType |
| ... | ... | @@ -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 { | |
| 4332 | _ = mod; | |
| 4329 | pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type { | |
| 4333 | 4330 | switch (child_type.tag()) { |
| 4334 | 4331 | .single_const_pointer => return Type.Tag.optional_single_const_pointer.create( |
| 4335 | 4332 | arena, |
| ... | ... | @@ -4344,16 +4341,14 @@ pub fn optionalType(mod: *Module, arena: *Allocator, child_type: Type) Allocator |
| 4344 | 4341 | } |
| 4345 | 4342 | |
| 4346 | 4343 | pub fn arrayType( |
| 4347 | mod: *Module, | |
| 4348 | 4344 | arena: *Allocator, |
| 4349 | 4345 | len: u64, |
| 4350 | 4346 | sentinel: ?Value, |
| 4351 | 4347 | elem_type: Type, |
| 4352 | 4348 | ) Allocator.Error!Type { |
| 4353 | _ = mod; | |
| 4354 | 4349 | if (elem_type.eql(Type.initTag(.u8))) { |
| 4355 | 4350 | if (sentinel) |some| { |
| 4356 | if (some.eql(Value.initTag(.zero))) { | |
| 4351 | if (some.eql(Value.initTag(.zero), elem_type)) { | |
| 4357 | 4352 | return Type.Tag.array_u8_sentinel_0.create(arena, len); |
| 4358 | 4353 | } |
| 4359 | 4354 | } else { |
| ... | ... | @@ -4376,12 +4371,10 @@ pub fn arrayType( |
| 4376 | 4371 | } |
| 4377 | 4372 | |
| 4378 | 4373 | pub fn errorUnionType( |
| 4379 | mod: *Module, | |
| 4380 | 4374 | arena: *Allocator, |
| 4381 | 4375 | error_set: Type, |
| 4382 | 4376 | payload: Type, |
| 4383 | 4377 | ) Allocator.Error!Type { |
| 4384 | _ = mod; | |
| 4385 | 4378 | assert(error_set.zigTypeTag() == .ErrorSet); |
| 4386 | 4379 | if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) { |
| 4387 | 4380 | return Type.initTag(.anyerror_void_error_union); |
src/RangeSet.zig+15-8| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const Order = std.math.Order; |
| 3 | const Type = @import("type.zig").Type; | |
| 3 | 4 | const Value = @import("value.zig").Value; |
| 4 | 5 | const RangeSet = @This(); |
| 5 | 6 | const SwitchProngSrc = @import("Module.zig").SwitchProngSrc; |
| ... | ... | @@ -22,9 +23,15 @@ pub fn deinit(self: *RangeSet) void { |
| 22 | 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 | 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 | 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 | 44 | } |
| 38 | 45 | |
| 39 | 46 | /// Assumes a and b do not overlap |
| 40 | fn lessThan(_: void, a: Range, b: Range) bool { | |
| 41 | return a.first.compare(.lt, b.first); | |
| 47 | fn lessThan(ty: Type, a: Range, b: Range) bool { | |
| 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 | 52 | if (self.ranges.items.len == 0) |
| 46 | 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 | |
| 51 | !self.ranges.items[self.ranges.items.len - 1].last.eql(last)) | |
| 57 | if (!self.ranges.items[0].first.eql(first, ty) or | |
| 58 | !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty)) | |
| 52 | 59 | { |
| 53 | 60 | return false; |
| 54 | 61 | } |
src/Sema.zig+75-59| ... | ... | @@ -634,7 +634,9 @@ fn analyzeAsType( |
| 634 | 634 | const wanted_type = Type.initTag(.@"type"); |
| 635 | 635 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); |
| 636 | 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 | 642 | /// May return Value Tags: `variable`, `undef`. |
| ... | ... | @@ -1022,7 +1024,9 @@ fn zirEnumDecl( |
| 1022 | 1024 | if (bag != 0) break true; |
| 1023 | 1025 | } else false; |
| 1024 | 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 | 1104 | // that points to this default value expression rather than the struct. |
| 1101 | 1105 | // But only resolve the source location if we need to emit a compile error. |
| 1102 | 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 | 1108 | } else if (any_values) { |
| 1105 | 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 | 2520 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2517 | 2521 | const src = inst_data.src(); |
| 2518 | 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 | 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 | 2551 | const tracy = trace(@src()); |
| 2548 | 2552 | defer tracy.end(); |
| 2549 | 2553 | |
| 2550 | // TODO these should be lazily evaluated | |
| 2551 | 2554 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; |
| 2552 | 2555 | const len = try sema.resolveInstConst(block, .unneeded, bin_inst.lhs); |
| 2553 | 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 | 2559 | return sema.addType(array_ty); |
| 2557 | 2560 | } |
| ... | ... | @@ -2560,13 +2563,12 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) |
| 2560 | 2563 | const tracy = trace(@src()); |
| 2561 | 2564 | defer tracy.end(); |
| 2562 | 2565 | |
| 2563 | // TODO these should be lazily evaluated | |
| 2564 | 2566 | const inst_data = sema.code.instructions.items(.data)[inst].array_type_sentinel; |
| 2565 | 2567 | const len = try sema.resolveInstConst(block, .unneeded, inst_data.len); |
| 2566 | 2568 | const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; |
| 2567 | 2569 | const sentinel = try sema.resolveInstConst(block, .unneeded, extra.sentinel); |
| 2568 | 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 | 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 | 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 | 2605 | return sema.addType(err_union_ty); |
| 2604 | 2606 | } |
| 2605 | 2607 | |
| ... | ... | @@ -3890,6 +3892,7 @@ fn analyzeSwitch( |
| 3890 | 3892 | block, |
| 3891 | 3893 | &range_set, |
| 3892 | 3894 | item_ref, |
| 3895 | operand_ty, | |
| 3893 | 3896 | src_node_offset, |
| 3894 | 3897 | .{ .scalar = scalar_i }, |
| 3895 | 3898 | ); |
| ... | ... | @@ -3912,6 +3915,7 @@ fn analyzeSwitch( |
| 3912 | 3915 | block, |
| 3913 | 3916 | &range_set, |
| 3914 | 3917 | item_ref, |
| 3918 | operand_ty, | |
| 3915 | 3919 | src_node_offset, |
| 3916 | 3920 | .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, |
| 3917 | 3921 | ); |
| ... | ... | @@ -3929,6 +3933,7 @@ fn analyzeSwitch( |
| 3929 | 3933 | &range_set, |
| 3930 | 3934 | item_first, |
| 3931 | 3935 | item_last, |
| 3936 | operand_ty, | |
| 3932 | 3937 | src_node_offset, |
| 3933 | 3938 | .{ .range = .{ .prong = multi_i, .item = range_i } }, |
| 3934 | 3939 | ); |
| ... | ... | @@ -3945,7 +3950,7 @@ fn analyzeSwitch( |
| 3945 | 3950 | |
| 3946 | 3951 | const min_int = try operand_ty.minInt(&arena, mod.getTarget()); |
| 3947 | 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 | 3954 | if (special_prong == .@"else") { |
| 3950 | 3955 | return mod.fail( |
| 3951 | 3956 | &block.base, |
| ... | ... | @@ -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 | 4059 | defer seen_values.deinit(); |
| 4055 | 4060 | |
| 4056 | 4061 | var extra_index: usize = special.end; |
| ... | ... | @@ -4161,7 +4166,7 @@ fn analyzeSwitch( |
| 4161 | 4166 | const item = sema.resolveInst(item_ref); |
| 4162 | 4167 | // Validation above ensured these will succeed. |
| 4163 | 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 | 4170 | return sema.resolveBlockBody(block, src, &child_block, body, merges); |
| 4166 | 4171 | } |
| 4167 | 4172 | } |
| ... | ... | @@ -4183,7 +4188,7 @@ fn analyzeSwitch( |
| 4183 | 4188 | const item = sema.resolveInst(item_ref); |
| 4184 | 4189 | // Validation above ensured these will succeed. |
| 4185 | 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 | 4192 | return sema.resolveBlockBody(block, src, &child_block, body, merges); |
| 4188 | 4193 | } |
| 4189 | 4194 | } |
| ... | ... | @@ -4198,8 +4203,8 @@ fn analyzeSwitch( |
| 4198 | 4203 | // Validation above ensured these will succeed. |
| 4199 | 4204 | const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable; |
| 4200 | 4205 | const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable; |
| 4201 | if (Value.compare(operand_val, .gte, first_tv.val) and | |
| 4202 | Value.compare(operand_val, .lte, last_tv.val)) | |
| 4206 | if (Value.compare(operand_val, .gte, first_tv.val, operand_ty) and | |
| 4207 | Value.compare(operand_val, .lte, last_tv.val, operand_ty)) | |
| 4203 | 4208 | { |
| 4204 | 4209 | return sema.resolveBlockBody(block, src, &child_block, body, merges); |
| 4205 | 4210 | } |
| ... | ... | @@ -4450,12 +4455,13 @@ fn validateSwitchRange( |
| 4450 | 4455 | range_set: *RangeSet, |
| 4451 | 4456 | first_ref: Zir.Inst.Ref, |
| 4452 | 4457 | last_ref: Zir.Inst.Ref, |
| 4458 | operand_ty: Type, | |
| 4453 | 4459 | src_node_offset: i32, |
| 4454 | 4460 | switch_prong_src: Module.SwitchProngSrc, |
| 4455 | 4461 | ) CompileError!void { |
| 4456 | 4462 | const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val; |
| 4457 | 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 | 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 | 4470 | block: *Scope.Block, |
| 4465 | 4471 | range_set: *RangeSet, |
| 4466 | 4472 | item_ref: Zir.Inst.Ref, |
| 4473 | operand_ty: Type, | |
| 4467 | 4474 | src_node_offset: i32, |
| 4468 | 4475 | switch_prong_src: Module.SwitchProngSrc, |
| 4469 | 4476 | ) CompileError!void { |
| 4470 | 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 | 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 | 5144 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 5138 | 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| { | |
| 5141 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { | |
| 5142 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 5143 | return sema.addConstUndef(resolved_type); | |
| 5144 | } | |
| 5145 | if (lhs_val.compare(op, rhs_val)) { | |
| 5146 | return Air.Inst.Ref.bool_true; | |
| 5147 | const runtime_src: LazySrcLoc = src: { | |
| 5148 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { | |
| 5149 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { | |
| 5150 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 5151 | return sema.addConstUndef(resolved_type); | |
| 5152 | } | |
| 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 | 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 | 5167 | const tag: Air.Inst.Tag = switch (op) { |
| 5155 | 5168 | .lt => .cmp_lt, |
| 5156 | 5169 | .lte => .cmp_lte, |
| ... | ... | @@ -5626,7 +5639,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp |
| 5626 | 5639 | |
| 5627 | 5640 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; |
| 5628 | 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 | 5643 | sema.arena, |
| 5631 | 5644 | elem_type, |
| 5632 | 5645 | null, |
| ... | ... | @@ -5680,7 +5693,7 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 5680 | 5693 | |
| 5681 | 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 | 5697 | sema.arena, |
| 5685 | 5698 | elem_type, |
| 5686 | 5699 | sentinel, |
| ... | ... | @@ -6569,7 +6582,7 @@ fn panicWithMsg( |
| 6569 | 6582 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); |
| 6570 | 6583 | const ptr_stack_trace_ty = try Module.simplePtrType(arena, stack_trace_ty, true, .One); |
| 6571 | 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 | 6586 | Value.initTag(.null_value), |
| 6574 | 6587 | ); |
| 6575 | 6588 | const args = try arena.create([2]Air.Inst.Ref); |
| ... | ... | @@ -6713,7 +6726,8 @@ fn fieldVal( |
| 6713 | 6726 | }, |
| 6714 | 6727 | .Type => { |
| 6715 | 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 | 6731 | switch (child_type.zigTypeTag()) { |
| 6718 | 6732 | .ErrorSet => { |
| 6719 | 6733 | // TODO resolve inferred error sets |
| ... | ... | @@ -6733,7 +6747,7 @@ fn fieldVal( |
| 6733 | 6747 | } else (try mod.getErrorValue(field_name)).key; |
| 6734 | 6748 | |
| 6735 | 6749 | return sema.addConstant( |
| 6736 | child_type, | |
| 6750 | try child_type.copy(arena), | |
| 6737 | 6751 | try Value.Tag.@"error".create(arena, .{ .name = name }), |
| 6738 | 6752 | ); |
| 6739 | 6753 | }, |
| ... | ... | @@ -6781,7 +6795,7 @@ fn fieldVal( |
| 6781 | 6795 | }; |
| 6782 | 6796 | const field_index_u32 = @intCast(u32, field_index); |
| 6783 | 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 | 6800 | else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}), |
| 6787 | 6801 | } |
| ... | ... | @@ -6805,7 +6819,6 @@ fn fieldPtr( |
| 6805 | 6819 | // in `fieldVal`. This function takes a pointer and returns a pointer. |
| 6806 | 6820 | |
| 6807 | 6821 | const mod = sema.mod; |
| 6808 | const arena = sema.arena; | |
| 6809 | 6822 | const object_ptr_src = src; // TODO better source location |
| 6810 | 6823 | const object_ptr_ty = sema.typeOf(object_ptr); |
| 6811 | 6824 | const object_ty = switch (object_ptr_ty.zigTypeTag()) { |
| ... | ... | @@ -6887,7 +6900,8 @@ fn fieldPtr( |
| 6887 | 6900 | _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr); |
| 6888 | 6901 | const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); |
| 6889 | 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 | 6905 | switch (child_type.zigTypeTag()) { |
| 6892 | 6906 | .ErrorSet => { |
| 6893 | 6907 | // TODO resolve inferred error sets |
| ... | ... | @@ -6902,15 +6916,14 @@ fn fieldPtr( |
| 6902 | 6916 | } |
| 6903 | 6917 | } |
| 6904 | 6918 | return mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ |
| 6905 | field_name, | |
| 6906 | child_type, | |
| 6919 | field_name, child_type, | |
| 6907 | 6920 | }); |
| 6908 | 6921 | } else (try mod.getErrorValue(field_name)).key; |
| 6909 | 6922 | |
| 6910 | 6923 | var anon_decl = try block.startAnonDecl(); |
| 6911 | 6924 | defer anon_decl.deinit(); |
| 6912 | 6925 | return sema.analyzeDeclRef(try anon_decl.finish( |
| 6913 | child_type, | |
| 6926 | try child_type.copy(anon_decl.arena()), | |
| 6914 | 6927 | try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }), |
| 6915 | 6928 | )); |
| 6916 | 6929 | }, |
| ... | ... | @@ -6960,7 +6973,7 @@ fn fieldPtr( |
| 6960 | 6973 | var anon_decl = try block.startAnonDecl(); |
| 6961 | 6974 | defer anon_decl.deinit(); |
| 6962 | 6975 | return sema.analyzeDeclRef(try anon_decl.finish( |
| 6963 | child_type, | |
| 6976 | try child_type.copy(anon_decl.arena()), | |
| 6964 | 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 | 7365 | |
| 7353 | 7366 | if (src_sentinel) |src_s| { |
| 7354 | 7367 | if (dst_sentinel) |dst_s| { |
| 7355 | if (src_s.eql(dst_s)) { | |
| 7368 | if (src_s.eql(dst_s, dst_elem_type)) { | |
| 7356 | 7369 | return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src); |
| 7357 | 7370 | } |
| 7358 | 7371 | } |
| ... | ... | @@ -7474,7 +7487,7 @@ fn coerceNum( |
| 7474 | 7487 | } |
| 7475 | 7488 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { |
| 7476 | 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 | 7491 | error.Overflow => return sema.mod.fail( |
| 7479 | 7492 | &block.base, |
| 7480 | 7493 | inst_src, |
| ... | ... | @@ -7813,12 +7826,12 @@ fn analyzeSlice( |
| 7813 | 7826 | array_type.sentinel() |
| 7814 | 7827 | else |
| 7815 | 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 | 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 | 7835 | sema.arena, |
| 7823 | 7836 | return_elem_type, |
| 7824 | 7837 | if (end_opt == .none) slice_sentinel else null, |
| ... | ... | @@ -7858,39 +7871,42 @@ fn cmpNumeric( |
| 7858 | 7871 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { |
| 7859 | 7872 | if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { |
| 7860 | 7873 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ |
| 7861 | lhs_ty.arrayLen(), | |
| 7862 | rhs_ty.arrayLen(), | |
| 7874 | lhs_ty.arrayLen(), rhs_ty.arrayLen(), | |
| 7863 | 7875 | }); |
| 7864 | 7876 | } |
| 7865 | 7877 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{}); |
| 7866 | 7878 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { |
| 7867 | 7879 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ |
| 7868 | lhs_ty, | |
| 7869 | rhs_ty, | |
| 7880 | lhs_ty, rhs_ty, | |
| 7870 | 7881 | }); |
| 7871 | 7882 | } |
| 7872 | 7883 | |
| 7873 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { | |
| 7874 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { | |
| 7875 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 7876 | return sema.addConstUndef(Type.initTag(.bool)); | |
| 7877 | } | |
| 7878 | if (Value.compare(lhs_val, op, rhs_val)) { | |
| 7879 | return Air.Inst.Ref.bool_true; | |
| 7884 | const runtime_src: LazySrcLoc = src: { | |
| 7885 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { | |
| 7886 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { | |
| 7887 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 7888 | return sema.addConstUndef(Type.initTag(.bool)); | |
| 7889 | } | |
| 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 | 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 | 7903 | // TODO handle comparisons against lazy zero values |
| 7887 | 7904 | // Some values can be compared against zero without being runtime known or without forcing |
| 7888 | 7905 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to |
| 7889 | 7906 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout |
| 7890 | 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 | 7910 | // For floats, emit a float comparison instruction. |
| 7895 | 7911 | const lhs_is_float = switch (lhs_ty_tag) { |
| 7896 | 7912 | .Float, .ComptimeFloat => true, |
src/type.zig+30-13| ... | ... | @@ -426,7 +426,7 @@ pub const Type = extern union { |
| 426 | 426 | const sentinel_b = info_b.sentinel; |
| 427 | 427 | if (sentinel_a) |sa| { |
| 428 | 428 | if (sentinel_b) |sb| { |
| 429 | if (!sa.eql(sb)) | |
| 429 | if (!sa.eql(sb, info_a.pointee_type)) | |
| 430 | 430 | return false; |
| 431 | 431 | } else { |
| 432 | 432 | return false; |
| ... | ... | @@ -455,13 +455,14 @@ pub const Type = extern union { |
| 455 | 455 | .Array, .Vector => { |
| 456 | 456 | if (a.arrayLen() != b.arrayLen()) |
| 457 | 457 | return false; |
| 458 | if (!a.elemType().eql(b.elemType())) | |
| 458 | const elem_ty = a.elemType(); | |
| 459 | if (!elem_ty.eql(b.elemType())) | |
| 459 | 460 | return false; |
| 460 | 461 | const sentinel_a = a.sentinel(); |
| 461 | 462 | const sentinel_b = b.sentinel(); |
| 462 | 463 | if (sentinel_a) |sa| { |
| 463 | 464 | if (sentinel_b) |sb| { |
| 464 | return sa.eql(sb); | |
| 465 | return sa.eql(sb, elem_ty); | |
| 465 | 466 | } else { |
| 466 | 467 | return false; |
| 467 | 468 | } |
| ... | ... | @@ -2744,29 +2745,37 @@ pub const Type = extern union { |
| 2744 | 2745 | return @as(usize, payload.data); |
| 2745 | 2746 | } |
| 2746 | 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 | 2749 | if (int_val.compareWithZero(.lt)) return null; |
| 2749 | 2750 | var end_payload: Value.Payload.U64 = .{ |
| 2750 | 2751 | .base = .{ .tag = .int_u64 }, |
| 2751 | 2752 | .data = end, |
| 2752 | 2753 | }; |
| 2753 | 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 | 2756 | return @intCast(usize, int_val.toUnsignedInt()); |
| 2756 | 2757 | } |
| 2757 | 2758 | }; |
| 2758 | 2759 | switch (ty.tag()) { |
| 2759 | 2760 | .enum_full, .enum_nonexhaustive => { |
| 2760 | 2761 | const enum_full = ty.cast(Payload.EnumFull).?.data; |
| 2762 | const tag_ty = enum_full.tag_ty; | |
| 2761 | 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 | 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 | 2769 | .enum_simple => { |
| 2768 | 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 | 2780 | .atomic_ordering, |
| 2772 | 2781 | .atomic_rmw_op, |
| ... | ... | @@ -2875,14 +2884,14 @@ pub const Type = extern union { |
| 2875 | 2884 | /// Asserts the type is an enum. |
| 2876 | 2885 | pub fn enumHasInt(ty: Type, int: Value, target: Target) bool { |
| 2877 | 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 | 2888 | if (int_val.compareWithZero(.lt)) return false; |
| 2880 | 2889 | var end_payload: Value.Payload.U64 = .{ |
| 2881 | 2890 | .base = .{ .tag = .int_u64 }, |
| 2882 | 2891 | .data = end, |
| 2883 | 2892 | }; |
| 2884 | 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 | 2895 | return true; |
| 2887 | 2896 | } |
| 2888 | 2897 | }; |
| ... | ... | @@ -2890,15 +2899,23 @@ pub const Type = extern union { |
| 2890 | 2899 | .enum_nonexhaustive => return int.intFitsInType(ty, target), |
| 2891 | 2900 | .enum_full => { |
| 2892 | 2901 | const enum_full = ty.castTag(.enum_full).?.data; |
| 2902 | const tag_ty = enum_full.tag_ty; | |
| 2893 | 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 | 2905 | } else { |
| 2896 | return enum_full.values.contains(int); | |
| 2906 | return enum_full.values.containsContext(int, .{ .ty = tag_ty }); | |
| 2897 | 2907 | } |
| 2898 | 2908 | }, |
| 2899 | 2909 | .enum_simple => { |
| 2900 | 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 | 2920 | .atomic_ordering, |
| 2904 | 2921 | .atomic_rmw_op, |
src/value.zig+105-352| ... | ... | @@ -653,8 +653,10 @@ pub const Value = extern union { |
| 653 | 653 | unreachable; |
| 654 | 654 | } |
| 655 | 655 | |
| 656 | pub const ToTypeBuffer = Type.Payload.Bits; | |
| 657 | ||
| 656 | 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 | 660 | return switch (self.tag()) { |
| 659 | 661 | .ty => self.castTag(.ty).?.data, |
| 660 | 662 | .u1_type => Type.initTag(.u1), |
| ... | ... | @@ -714,14 +716,13 @@ pub const Value = extern union { |
| 714 | 716 | |
| 715 | 717 | .int_type => { |
| 716 | 718 | const payload = self.castTag(.int_type).?.data; |
| 717 | const new = try allocator.create(Type.Payload.Bits); | |
| 718 | new.* = .{ | |
| 719 | buffer.* = .{ | |
| 719 | 720 | .base = .{ |
| 720 | 721 | .tag = if (payload.signed) .int_signed else .int_unsigned, |
| 721 | 722 | }, |
| 722 | 723 | .data = payload.bits, |
| 723 | 724 | }; |
| 724 | return Type.initPayload(&new.base); | |
| 725 | return Type.initPayload(&buffer.base); | |
| 725 | 726 | }, |
| 726 | 727 | |
| 727 | 728 | .undef, |
| ... | ... | @@ -958,9 +959,8 @@ pub const Value = extern union { |
| 958 | 959 | |
| 959 | 960 | /// Converts an integer or a float to a float. |
| 960 | 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 | _ = target; | |
| 963 | switch (ty.tag()) { | |
| 962 | pub fn floatCast(self: Value, allocator: *Allocator, dest_ty: Type) !Value { | |
| 963 | switch (dest_ty.tag()) { | |
| 964 | 964 | .f16 => { |
| 965 | 965 | @panic("TODO add __trunctfhf2 to compiler-rt"); |
| 966 | 966 | //const res = try Value.Tag.float_16.create(allocator, self.toFloat(f16)); |
| ... | ... | @@ -970,13 +970,13 @@ pub const Value = extern union { |
| 970 | 970 | }, |
| 971 | 971 | .f32 => { |
| 972 | 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 | 974 | return error.Overflow; |
| 975 | 975 | return res; |
| 976 | 976 | }, |
| 977 | 977 | .f64 => { |
| 978 | 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 | 980 | return error.Overflow; |
| 981 | 981 | return res; |
| 982 | 982 | }, |
| ... | ... | @@ -1083,12 +1083,18 @@ pub const Value = extern union { |
| 1083 | 1083 | return lhs_bigint.order(rhs_bigint); |
| 1084 | 1084 | } |
| 1085 | 1085 | |
| 1086 | /// Asserts the value is comparable. | |
| 1087 | pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool { | |
| 1086 | /// Asserts the value is comparable. Does not take a type parameter because it supports | |
| 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 | 1094 | return switch (op) { |
| 1089 | .eq => lhs.eql(rhs), | |
| 1090 | .neq => !lhs.eql(rhs), | |
| 1091 | else => order(lhs, rhs).compare(op), | |
| 1095 | .eq => lhs.eql(rhs, ty), | |
| 1096 | .neq => !lhs.eql(rhs, ty), | |
| 1097 | else => compareHetero(lhs, op, rhs), | |
| 1092 | 1098 | }; |
| 1093 | 1099 | } |
| 1094 | 1100 | |
| ... | ... | @@ -1097,11 +1103,11 @@ pub const Value = extern union { |
| 1097 | 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 | |
| 1101 | /// the values as | |
| 1102 | pub fn eql(a: Value, b: Value) bool { | |
| 1106 | pub fn eql(a: Value, b: Value, ty: Type) bool { | |
| 1103 | 1107 | const a_tag = a.tag(); |
| 1104 | 1108 | const b_tag = b.tag(); |
| 1109 | assert(a_tag != .undef); | |
| 1110 | assert(b_tag != .undef); | |
| 1105 | 1111 | if (a_tag == b_tag) { |
| 1106 | 1112 | switch (a_tag) { |
| 1107 | 1113 | .void_value, .null_value => return true, |
| ... | ... | @@ -1118,230 +1124,106 @@ pub const Value = extern union { |
| 1118 | 1124 | else => {}, |
| 1119 | 1125 | } |
| 1120 | 1126 | } |
| 1121 | if (a.isType() and b.isType()) { | |
| 1122 | // 128 bytes should be enough to hold both types | |
| 1123 | var buf: [128]u8 = undefined; | |
| 1124 | var fib = std.heap.FixedBufferAllocator.init(&buf); | |
| 1125 | const a_type = a.toType(&fib.allocator) catch unreachable; | |
| 1126 | const b_type = b.toType(&fib.allocator) catch unreachable; | |
| 1127 | if (ty.zigTypeTag() == .Type) { | |
| 1128 | var buf_a: ToTypeBuffer = undefined; | |
| 1129 | var buf_b: ToTypeBuffer = undefined; | |
| 1130 | const a_type = a.toType(&buf_a); | |
| 1131 | const b_type = b.toType(&buf_b); | |
| 1127 | 1132 | return a_type.eql(b_type); |
| 1128 | 1133 | } |
| 1129 | 1134 | return order(a, b).compare(.eq); |
| 1130 | 1135 | } |
| 1131 | 1136 | |
| 1132 | pub fn hash_u32(self: Value) u32 { | |
| 1133 | return @truncate(u32, self.hash()); | |
| 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); | |
| 1137 | pub const ArrayHashContext = struct { | |
| 1138 | ty: Type, | |
| 1140 | 1139 | |
| 1141 | switch (self.tag()) { | |
| 1142 | .u1_type, | |
| 1143 | .u8_type, | |
| 1144 | .i8_type, | |
| 1145 | .u16_type, | |
| 1146 | .i16_type, | |
| 1147 | .u32_type, | |
| 1148 | .i32_type, | |
| 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 | }, | |
| 1140 | pub fn hash(self: @This(), v: Value) u32 { | |
| 1141 | const other_context: HashContext = .{ .ty = self.ty }; | |
| 1142 | return @truncate(u32, other_context.hash(v)); | |
| 1143 | } | |
| 1144 | pub fn eql(self: @This(), a: Value, b: Value) bool { | |
| 1145 | return a.eql(b, self.ty); | |
| 1146 | } | |
| 1147 | }; | |
| 1203 | 1148 | |
| 1204 | .empty_struct_value, | |
| 1205 | .empty_array, | |
| 1206 | => {}, | |
| 1149 | pub const HashContext = struct { | |
| 1150 | ty: Type, | |
| 1207 | 1151 | |
| 1208 | .undef, | |
| 1209 | .null_value, | |
| 1210 | .void_value, | |
| 1211 | .unreachable_value, | |
| 1212 | => std.hash.autoHash(&hasher, self.tag()), | |
| 1152 | pub fn hash(self: @This(), v: Value) u64 { | |
| 1153 | var hasher = std.hash.Wyhash.init(0); | |
| 1213 | 1154 | |
| 1214 | .zero, .bool_false => std.hash.autoHash(&hasher, @as(u64, 0)), | |
| 1215 | .one, .bool_true => std.hash.autoHash(&hasher, @as(u64, 1)), | |
| 1155 | switch (self.ty.zigTypeTag()) { | |
| 1156 | .BoundFn => unreachable, // TODO remove this from the language | |
| 1216 | 1157 | |
| 1217 | .float_16, .float_32, .float_64, .float_128 => { | |
| 1218 | @panic("TODO implement Value.hash for floats"); | |
| 1219 | }, | |
| 1158 | .Void, | |
| 1159 | .NoReturn, | |
| 1160 | .Undefined, | |
| 1161 | .Null, | |
| 1162 | => {}, | |
| 1220 | 1163 | |
| 1221 | .enum_literal => { | |
| 1222 | const payload = self.castTag(.enum_literal).?; | |
| 1223 | hasher.update(payload.data); | |
| 1224 | }, | |
| 1225 | .enum_field_index => { | |
| 1226 | const payload = self.castTag(.enum_field_index).?; | |
| 1227 | std.hash.autoHash(&hasher, payload.data); | |
| 1228 | }, | |
| 1229 | .bytes => { | |
| 1230 | const payload = self.castTag(.bytes).?; | |
| 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 { | |
| 1164 | .Type => { | |
| 1165 | var buf: ToTypeBuffer = undefined; | |
| 1166 | return v.toType(&buf).hash(); | |
| 1167 | }, | |
| 1168 | .Bool => { | |
| 1169 | std.hash.autoHash(&hasher, v.toBool()); | |
| 1170 | }, | |
| 1171 | .Int, .ComptimeInt => { | |
| 1172 | var space: BigIntSpace = undefined; | |
| 1173 | const big = v.toBigInt(&space); | |
| 1268 | 1174 | std.hash.autoHash(&hasher, big.positive); |
| 1269 | 1175 | for (big.limbs) |limb| { |
| 1270 | 1176 | std.hash.autoHash(&hasher, limb); |
| 1271 | 1177 | } |
| 1272 | } | |
| 1273 | }, | |
| 1274 | .elem_ptr => { | |
| 1275 | const payload = self.castTag(.elem_ptr).?.data; | |
| 1276 | std.hash.autoHash(&hasher, payload.array_ptr.hash()); | |
| 1277 | std.hash.autoHash(&hasher, payload.index); | |
| 1278 | }, | |
| 1279 | .field_ptr => { | |
| 1280 | const payload = self.castTag(.field_ptr).?.data; | |
| 1281 | std.hash.autoHash(&hasher, payload.container_ptr.hash()); | |
| 1282 | std.hash.autoHash(&hasher, payload.field_index); | |
| 1283 | }, | |
| 1284 | .decl_ref => { | |
| 1285 | const decl = self.castTag(.decl_ref).?.data; | |
| 1286 | std.hash.autoHash(&hasher, decl); | |
| 1287 | }, | |
| 1288 | .function => { | |
| 1289 | const func = self.castTag(.function).?.data; | |
| 1290 | std.hash.autoHash(&hasher, func); | |
| 1291 | }, | |
| 1292 | .extern_fn => { | |
| 1293 | const decl = self.castTag(.extern_fn).?.data; | |
| 1294 | std.hash.autoHash(&hasher, decl); | |
| 1295 | }, | |
| 1296 | .variable => { | |
| 1297 | const variable = self.castTag(.variable).?.data; | |
| 1298 | std.hash.autoHash(&hasher, variable); | |
| 1299 | }, | |
| 1300 | .@"error" => { | |
| 1301 | const payload = self.castTag(.@"error").?.data; | |
| 1302 | hasher.update(payload.name); | |
| 1303 | }, | |
| 1304 | .error_union => { | |
| 1305 | const payload = self.castTag(.error_union).?.data; | |
| 1306 | std.hash.autoHash(&hasher, payload.hash()); | |
| 1307 | }, | |
| 1308 | .inferred_alloc => unreachable, | |
| 1309 | ||
| 1310 | .manyptr_u8_type, | |
| 1311 | .manyptr_const_u8_type, | |
| 1312 | .atomic_ordering_type, | |
| 1313 | .atomic_rmw_op_type, | |
| 1314 | .calling_convention_type, | |
| 1315 | .float_mode_type, | |
| 1316 | .reduce_op_type, | |
| 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"), | |
| 1178 | }, | |
| 1179 | .Float, .ComptimeFloat => { | |
| 1180 | @panic("TODO implement hashing float values"); | |
| 1181 | }, | |
| 1182 | .Pointer => { | |
| 1183 | @panic("TODO implement hashing pointer values"); | |
| 1184 | }, | |
| 1185 | .Array, .Vector => { | |
| 1186 | @panic("TODO implement hashing array/vector values"); | |
| 1187 | }, | |
| 1188 | .Struct => { | |
| 1189 | @panic("TODO implement hashing struct values"); | |
| 1190 | }, | |
| 1191 | .Optional => { | |
| 1192 | @panic("TODO implement hashing optional values"); | |
| 1193 | }, | |
| 1194 | .ErrorUnion => { | |
| 1195 | @panic("TODO implement hashing error union values"); | |
| 1196 | }, | |
| 1197 | .ErrorSet => { | |
| 1198 | @panic("TODO implement hashing error set values"); | |
| 1199 | }, | |
| 1200 | .Enum => { | |
| 1201 | @panic("TODO implement hashing enum values"); | |
| 1202 | }, | |
| 1203 | .Union => { | |
| 1204 | @panic("TODO implement hashing union values"); | |
| 1205 | }, | |
| 1206 | .Fn => { | |
| 1207 | @panic("TODO implement hashing function values"); | |
| 1208 | }, | |
| 1209 | .Opaque => { | |
| 1210 | @panic("TODO implement hashing opaque values"); | |
| 1211 | }, | |
| 1212 | .Frame => { | |
| 1213 | @panic("TODO implement hashing frame values"); | |
| 1214 | }, | |
| 1215 | .AnyFrame => { | |
| 1216 | @panic("TODO implement hashing anyframe values"); | |
| 1217 | }, | |
| 1218 | .EnumLiteral => { | |
| 1219 | @panic("TODO implement hashing enum literal values"); | |
| 1220 | }, | |
| 1221 | } | |
| 1222 | return hasher.final(); | |
| 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 | 1225 | pub fn eql(self: @This(), a: Value, b: Value) bool { |
| 1343 | _ = self; | |
| 1344 | return a.eql(b); | |
| 1226 | return a.eql(b, self.ty); | |
| 1345 | 1227 | } |
| 1346 | 1228 | }; |
| 1347 | 1229 | |
| ... | ... | @@ -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 | 1393 | /// This type is not copyable since it may contain pointers to its inner data. |
| 1617 | 1394 | pub const Payload = struct { |
| 1618 | 1395 | tag: Tag, |
| ... | ... | @@ -1806,27 +1583,3 @@ pub const Value = extern union { |
| 1806 | 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 | } |