diff --git a/src/Module.zig b/src/Module.zig index 67ca91266c..b1cbd88297 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -932,7 +932,7 @@ pub const Decl = struct { assert(decl.has_tv); return switch (decl.val.tag()) { .extern_fn => true, - .variable => decl.val.castTag(.variable).?.data.init.tag() == .unreachable_value, + .variable => decl.val.castTag(.variable).?.data.init.ip_index == .unreachable_value, else => false, }; } @@ -4849,6 +4849,8 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { var is_extern = false; switch (decl_tv.val.ip_index) { .generic_poison => unreachable, + .unreachable_value => unreachable, + .none => switch (decl_tv.val.tag()) { .variable => { const variable = decl_tv.val.castTag(.variable).?.data; @@ -4869,8 +4871,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { } }, - .unreachable_value => unreachable, - .function => {}, else => { @@ -6592,7 +6592,7 @@ pub fn populateTestFunctions( .len = try Value.Tag.int_u64.create(arena, test_name_slice.len), }), // name try Value.Tag.decl_ref.create(arena, test_decl_index), // func - Value.initTag(.null_value), // async_frame_size + Value.null, // async_frame_size }; test_fn_vals[i] = try Value.Tag.aggregate.create(arena, field_vals); } diff --git a/src/Sema.zig b/src/Sema.zig index 87df2f23e1..3406d0d80f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1877,8 +1877,8 @@ fn resolveConstValue( if (try sema.resolveMaybeUndefValAllowVariables(air_ref)) |val| { switch (val.ip_index) { .generic_poison => return error.GenericPoison, + .undef => return sema.failWithUseOfUndef(block, src), .none => switch (val.tag()) { - .undef => return sema.failWithUseOfUndef(block, src), .variable => return sema.failWithNeededComptime(block, src, reason), else => return val, }, @@ -4409,7 +4409,7 @@ fn validateStructInit( if (field_ptr != 0) continue; const default_val = struct_ty.structFieldDefaultValue(i); - if (default_val.tag() == .unreachable_value) { + if (default_val.ip_index == .unreachable_value) { if (struct_ty.isTuple()) { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { @@ -4554,7 +4554,7 @@ fn validateStructInit( } const default_val = struct_ty.structFieldDefaultValue(i); - if (default_val.tag() == .unreachable_value) { + if (default_val.ip_index == .unreachable_value) { if (struct_ty.isTuple()) { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { @@ -4644,7 +4644,7 @@ fn zirValidateArrayInit( var i = instrs.len; while (i < array_len) : (i += 1) { const default_val = array_ty.structFieldDefaultValue(i); - if (default_val.tag() == .unreachable_value) { + if (default_val.ip_index == .unreachable_value) { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { try sema.errNote(block, init_src, msg, template, .{i}); @@ -7885,7 +7885,7 @@ fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) const tuple = ty.tupleFields(); for (tuple.values, 0..) |field_val, i| { try sema.resolveTupleLazyValues(block, src, tuple.types[i]); - if (field_val.tag() == .unreachable_value) continue; + if (field_val.ip_index == .unreachable_value) continue; try sema.resolveLazyValue(field_val); } } @@ -12641,7 +12641,7 @@ fn analyzeTupleCat( const default_val = lhs_ty.structFieldDefaultValue(i); values[i] = default_val; const operand_src = lhs_src; // TODO better source location - if (default_val.tag() == .unreachable_value) { + if (default_val.ip_index == .unreachable_value) { runtime_src = operand_src; } } @@ -12651,7 +12651,7 @@ fn analyzeTupleCat( const default_val = rhs_ty.structFieldDefaultValue(i); values[i + lhs_len] = default_val; const operand_src = rhs_src; // TODO better source location - if (default_val.tag() == .unreachable_value) { + if (default_val.ip_index == .unreachable_value) { runtime_src = operand_src; } } @@ -12809,8 +12809,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai while (elem_i < lhs_len) : (elem_i += 1) { const lhs_elem_i = elem_i; const elem_ty = if (lhs_is_tuple) lhs_ty.structFieldType(lhs_elem_i) else lhs_info.elem_type; - const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i) else Value.initTag(.unreachable_value); - const elem_val = if (elem_default_val.tag() == .unreachable_value) try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_elem_i) else elem_default_val; + const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i) else Value.@"unreachable"; + const elem_val = if (elem_default_val.ip_index == .unreachable_value) try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_elem_i) else elem_default_val; const elem_val_inst = try sema.addConstant(elem_ty, elem_val); const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); @@ -12819,8 +12819,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai while (elem_i < result_len) : (elem_i += 1) { const rhs_elem_i = elem_i - lhs_len; const elem_ty = if (rhs_is_tuple) rhs_ty.structFieldType(rhs_elem_i) else rhs_info.elem_type; - const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i) else Value.initTag(.unreachable_value); - const elem_val = if (elem_default_val.tag() == .unreachable_value) try rhs_sub_val.elemValue(sema.mod, sema.arena, rhs_elem_i) else elem_default_val; + const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i) else Value.@"unreachable"; + const elem_val = if (elem_default_val.ip_index == .unreachable_value) try rhs_sub_val.elemValue(sema.mod, sema.arena, rhs_elem_i) else elem_default_val; const elem_val_inst = try sema.addConstant(elem_ty, elem_val); const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); @@ -12962,7 +12962,7 @@ fn analyzeTupleMul( types[i] = operand_ty.structFieldType(i); values[i] = operand_ty.structFieldDefaultValue(i); const operand_src = lhs_src; // TODO better source location - if (values[i].tag() == .unreachable_value) { + if (values[i].ip_index == .unreachable_value) { runtime_src = operand_src; } } @@ -14332,7 +14332,7 @@ fn zirOverflowArithmetic( var result: struct { inst: Air.Inst.Ref = .none, - wrapped: Value = Value.initTag(.unreachable_value), + wrapped: Value = Value.@"unreachable", overflow_bit: Value, } = result: { switch (zir_tag) { @@ -14508,8 +14508,8 @@ fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { types[0] = ty; types[1] = ov_ty; - values[0] = Value.initTag(.unreachable_value); - values[1] = Value.initTag(.unreachable_value); + values[0] = Value.@"unreachable"; + values[1] = Value.@"unreachable"; return tuple_ty; } @@ -15647,7 +15647,7 @@ fn zirClosureCapture( // value only. In such case we preserve the type and use a dummy runtime value. const operand = try sema.resolveInst(inst_data.operand); const val = (try sema.resolveMaybeUndefValAllowVariables(operand)) orelse - Value.initTag(.unreachable_value); + Value.@"unreachable"; try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, .{ .ty = try sema.typeOf(operand).copy(sema.perm_arena), @@ -15684,7 +15684,7 @@ fn zirClosureGet( scope = scope.parent.?; }; - if (tv.val.tag() == .unreachable_value and !block.is_typeof and sema.func == null) { + if (tv.val.ip_index == .unreachable_value and !block.is_typeof and sema.func == null) { const msg = msg: { const name = name: { const file = sema.owner_decl.getFileScope(); @@ -15712,7 +15712,7 @@ fn zirClosureGet( return sema.failWithOwnedErrorMsg(msg); } - if (tv.val.tag() == .unreachable_value and !block.is_typeof and !block.is_comptime and sema.func != null) { + if (tv.val.ip_index == .unreachable_value and !block.is_typeof and !block.is_comptime and sema.func != null) { const msg = msg: { const name = name: { const file = sema.owner_decl.getFileScope(); @@ -15742,7 +15742,7 @@ fn zirClosureGet( return sema.failWithOwnedErrorMsg(msg); } - if (tv.val.tag() == .unreachable_value) { + if (tv.val.ip_index == .unreachable_value) { assert(block.is_typeof); // We need a dummy runtime instruction with the correct type. return block.addTy(.alloc, tv.ty); @@ -16477,7 +16477,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const struct_field_fields = try fields_anon_decl.arena().create([5]Value); const field_val = tuple.values[i]; - const is_comptime = field_val.tag() != .unreachable_value; + const is_comptime = field_val.ip_index != .unreachable_value; const opt_default_val = if (is_comptime) field_val else null; const default_val_ptr = try sema.optRefValue(block, field_ty, opt_default_val); struct_field_fields.* = .{ @@ -16518,7 +16518,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; const struct_field_fields = try fields_anon_decl.arena().create([5]Value); - const opt_default_val = if (field.default_val.tag() == .unreachable_value) + const opt_default_val = if (field.default_val.ip_index == .unreachable_value) null else field.default_val; @@ -16570,7 +16570,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const backing_int_ty_val = try Value.Tag.ty.create(sema.arena, struct_obj.backing_int_ty); break :blk try Value.Tag.opt_payload.create(sema.arena, backing_int_ty_val); } else { - break :blk Value.initTag(.null_value); + break :blk Value.null; } }; @@ -17974,7 +17974,7 @@ fn finishStructInit( for (struct_obj.values, 0..) |default_val, i| { if (field_inits[i] != .none) continue; - if (default_val.tag() == .unreachable_value) { + if (default_val.ip_index == .unreachable_value) { const field_name = struct_obj.names[i]; const template = "missing struct field: {s}"; const args = .{field_name}; @@ -17994,7 +17994,7 @@ fn finishStructInit( if (field_inits[i] != .none) continue; const default_val = struct_ty.structFieldDefaultValue(i); - if (default_val.tag() == .unreachable_value) { + if (default_val.ip_index == .unreachable_value) { const template = "missing tuple field with index {d}"; if (root_msg) |msg| { try sema.errNote(block, init_src, msg, template, .{i}); @@ -18010,7 +18010,7 @@ fn finishStructInit( for (struct_obj.fields.values(), 0..) |field, i| { if (field_inits[i] != .none) continue; - if (field.default_val.tag() == .unreachable_value) { + if (field.default_val.ip_index == .unreachable_value) { const field_name = struct_obj.fields.keys()[i]; const template = "missing struct field: {s}"; const args = .{field_name}; @@ -18145,7 +18145,7 @@ fn zirStructInitAnon( if (try sema.resolveMaybeUndefVal(init)) |init_val| { values[i] = init_val; } else { - values[i] = Value.initTag(.unreachable_value); + values[i] = Value.@"unreachable"; runtime_index = i; } } @@ -18191,7 +18191,7 @@ fn zirStructInitAnon( .@"addrspace" = target_util.defaultAddressSpace(target, .local), .pointee_type = field_ty, }); - if (values[i].tag() == .unreachable_value) { + if (values[i].ip_index == .unreachable_value) { const init = try sema.resolveInst(item.data.init); const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); _ = try block.addBinOp(.store, field_ptr, init); @@ -18357,7 +18357,7 @@ fn zirArrayInitAnon( if (try sema.resolveMaybeUndefVal(elem)) |val| { values[i] = val; } else { - values[i] = Value.initTag(.unreachable_value); + values[i] = Value.@"unreachable"; runtime_src = operand_src; } } @@ -18390,7 +18390,7 @@ fn zirArrayInitAnon( .@"addrspace" = target_util.defaultAddressSpace(target, .local), .pointee_type = types[i], }); - if (values[i].tag() == .unreachable_value) { + if (values[i].ip_index == .unreachable_value) { const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); } @@ -19545,8 +19545,8 @@ fn reifyStruct( else opt_val; break :blk try payload_val.copy(new_decl_arena_allocator); - } else Value.initTag(.unreachable_value); - if (is_comptime_val.toBool(mod) and default_val.tag() == .unreachable_value) { + } else Value.@"unreachable"; + if (is_comptime_val.toBool(mod) and default_val.ip_index == .unreachable_value) { return sema.fail(block, src, "comptime field without default initialization value", .{}); } @@ -22579,7 +22579,7 @@ fn zirVarExtended( break :blk (try sema.resolveMaybeUndefVal(init)) orelse return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known"); - } else Value.initTag(.unreachable_value); + } else Value.@"unreachable"; try sema.validateVarType(block, ty_src, var_ty, small.is_extern); @@ -23080,7 +23080,7 @@ fn zirBuiltinExtern( const new_var = try new_decl_arena_allocator.create(Module.Var); new_var.* = .{ .owner_decl = sema.owner_decl_index, - .init = Value.initTag(.unreachable_value), + .init = Value.@"unreachable", .is_extern = true, .is_mutable = false, .is_threadlocal = options.is_thread_local, @@ -25736,7 +25736,7 @@ fn coerceExtra( } } else { in_memory_result = .{ .ptr_sentinel = .{ - .actual = Value.initTag(.unreachable_value), + .actual = Value.@"unreachable", .wanted = dest_sent, .ty = dst_elem_type, } }; @@ -26116,26 +26116,28 @@ fn coerceExtra( .ErrorUnion => switch (inst_ty.zigTypeTag(mod)) { .ErrorUnion => eu: { if (maybe_inst_val) |inst_val| { - switch (inst_val.tag()) { + switch (inst_val.ip_index) { .undef => return sema.addConstUndef(dest_ty), - .eu_payload => { - const payload = try sema.addConstant( - inst_ty.errorUnionPayload(), - inst_val.castTag(.eu_payload).?.data, - ); - return sema.wrapErrorUnionPayload(block, dest_ty, payload, inst_src) catch |err| switch (err) { - error.NotCoercible => break :eu, - else => |e| return e, - }; - }, - else => { - const error_set = try sema.addConstant( - inst_ty.errorUnionSet(), - inst_val, - ); - return sema.wrapErrorUnionSet(block, dest_ty, error_set, inst_src); + .none => switch (inst_val.tag()) { + .eu_payload => { + const payload = try sema.addConstant( + inst_ty.errorUnionPayload(), + inst_val.castTag(.eu_payload).?.data, + ); + return sema.wrapErrorUnionPayload(block, dest_ty, payload, inst_src) catch |err| switch (err) { + error.NotCoercible => break :eu, + else => |e| return e, + }; + }, + else => {}, }, + else => {}, } + const error_set = try sema.addConstant( + inst_ty.errorUnionSet(), + inst_val, + ); + return sema.wrapErrorUnionSet(block, dest_ty, error_set, inst_src); } }, .ErrorSet => { @@ -26413,7 +26415,7 @@ const InMemoryCoercionResult = union(enum) { break; }, .array_sentinel => |sentinel| { - if (sentinel.actual.tag() != .unreachable_value) { + if (sentinel.actual.ip_index != .unreachable_value) { try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{ sentinel.actual.fmtValue(sentinel.ty, sema.mod), sentinel.wanted.fmtValue(sentinel.ty, sema.mod), }); @@ -26539,7 +26541,7 @@ const InMemoryCoercionResult = union(enum) { break; }, .ptr_sentinel => |sentinel| { - if (sentinel.actual.tag() != .unreachable_value) { + if (sentinel.actual.ip_index != .unreachable_value) { try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{ sentinel.actual.fmtValue(sentinel.ty, sema.mod), sentinel.wanted.fmtValue(sentinel.ty, sema.mod), }); @@ -26747,8 +26749,8 @@ fn coerceInMemoryAllowed( dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, mod)); if (!ok_sent) { return InMemoryCoercionResult{ .array_sentinel = .{ - .actual = src_info.sentinel orelse Value.initTag(.unreachable_value), - .wanted = dest_info.sentinel orelse Value.initTag(.unreachable_value), + .actual = src_info.sentinel orelse Value.@"unreachable", + .wanted = dest_info.sentinel orelse Value.@"unreachable", .ty = dest_info.elem_type, } }; } @@ -27129,8 +27131,8 @@ fn coerceInMemoryAllowedPtrs( dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, sema.mod)); if (!ok_sent) { return InMemoryCoercionResult{ .ptr_sentinel = .{ - .actual = src_info.sentinel orelse Value.initTag(.unreachable_value), - .wanted = dest_info.sentinel orelse Value.initTag(.unreachable_value), + .actual = src_info.sentinel orelse Value.@"unreachable", + .wanted = dest_info.sentinel orelse Value.@"unreachable", .ty = dest_info.pointee_type, } }; } @@ -27540,7 +27542,7 @@ fn beginComptimePtrMutation( }; } - switch (val_ptr.tag()) { + switch (val_ptr.ip_index) { .undef => { // An array has been initialized to undefined at comptime and now we // are for the first time setting an element. We must change the representation @@ -27565,127 +27567,130 @@ fn beginComptimePtrMutation( parent.decl_ref_mut, ); }, - .bytes => { - // An array is memory-optimized to store a slice of bytes, but we are about - // to modify an individual field and the representation has to change. - // If we wanted to avoid this, there would need to be special detection - // elsewhere to identify when writing a value to an array element that is stored - // using the `bytes` tag, and handle it without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); + .none => switch (val_ptr.tag()) { + .bytes => { + // An array is memory-optimized to store a slice of bytes, but we are about + // to modify an individual field and the representation has to change. + // If we wanted to avoid this, there would need to be special detection + // elsewhere to identify when writing a value to an array element that is stored + // using the `bytes` tag, and handle it without making a call to this function. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); - const bytes = val_ptr.castTag(.bytes).?.data; - const dest_len = parent.ty.arrayLenIncludingSentinel(mod); - // bytes.len may be one greater than dest_len because of the case when - // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. - assert(bytes.len >= dest_len); - const elems = try arena.alloc(Value, @intCast(usize, dest_len)); - for (elems, 0..) |*elem, i| { - elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); - } + const bytes = val_ptr.castTag(.bytes).?.data; + const dest_len = parent.ty.arrayLenIncludingSentinel(mod); + // bytes.len may be one greater than dest_len because of the case when + // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. + assert(bytes.len >= dest_len); + const elems = try arena.alloc(Value, @intCast(usize, dest_len)); + for (elems, 0..) |*elem, i| { + elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); + } - val_ptr.* = try Value.Tag.aggregate.create(arena, elems); + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - return beginComptimePtrMutationInner( + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[elem_ptr.index], + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .str_lit => { + // An array is memory-optimized to store a slice of bytes, but we are about + // to modify an individual field and the representation has to change. + // If we wanted to avoid this, there would need to be special detection + // elsewhere to identify when writing a value to an array element that is stored + // using the `str_lit` tag, and handle it without making a call to this function. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); + + const str_lit = val_ptr.castTag(.str_lit).?.data; + const dest_len = parent.ty.arrayLenIncludingSentinel(mod); + const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + const elems = try arena.alloc(Value, @intCast(usize, dest_len)); + for (bytes, 0..) |byte, i| { + elems[i] = try Value.Tag.int_u64.create(arena, byte); + } + if (parent.ty.sentinel(mod)) |sent_val| { + assert(elems.len == bytes.len + 1); + elems[bytes.len] = sent_val; + } + + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); + + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[elem_ptr.index], + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .repeated => { + // An array is memory-optimized to store only a single element value, and + // that value is understood to be the same for the entire length of the array. + // However, now we want to modify an individual field and so the + // representation has to change. If we wanted to avoid this, there would + // need to be special detection elsewhere to identify when writing a value to an + // array element that is stored using the `repeated` tag, and handle it + // without making a call to this function. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); + + const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena); + const array_len_including_sentinel = + try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); + const elems = try arena.alloc(Value, array_len_including_sentinel); + if (elems.len > 0) elems[0] = repeated_val; + for (elems[1..]) |*elem| { + elem.* = try repeated_val.copy(arena); + } + + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); + + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[elem_ptr.index], + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + + .aggregate => return beginComptimePtrMutationInner( sema, block, src, elem_ty, - &elems[elem_ptr.index], + &val_ptr.castTag(.aggregate).?.data[elem_ptr.index], ptr_elem_ty, parent.decl_ref_mut, - ); + ), + + .the_only_possible_value => { + const duped = try sema.arena.create(Value); + duped.* = Value.initTag(.the_only_possible_value); + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + duped, + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + + else => unreachable, }, - .str_lit => { - // An array is memory-optimized to store a slice of bytes, but we are about - // to modify an individual field and the representation has to change. - // If we wanted to avoid this, there would need to be special detection - // elsewhere to identify when writing a value to an array element that is stored - // using the `str_lit` tag, and handle it without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const str_lit = val_ptr.castTag(.str_lit).?.data; - const dest_len = parent.ty.arrayLenIncludingSentinel(mod); - const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; - const elems = try arena.alloc(Value, @intCast(usize, dest_len)); - for (bytes, 0..) |byte, i| { - elems[i] = try Value.Tag.int_u64.create(arena, byte); - } - if (parent.ty.sentinel(mod)) |sent_val| { - assert(elems.len == bytes.len + 1); - elems[bytes.len] = sent_val; - } - - val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - - return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - &elems[elem_ptr.index], - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - .repeated => { - // An array is memory-optimized to store only a single element value, and - // that value is understood to be the same for the entire length of the array. - // However, now we want to modify an individual field and so the - // representation has to change. If we wanted to avoid this, there would - // need to be special detection elsewhere to identify when writing a value to an - // array element that is stored using the `repeated` tag, and handle it - // without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena); - const array_len_including_sentinel = - try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod)); - const elems = try arena.alloc(Value, array_len_including_sentinel); - if (elems.len > 0) elems[0] = repeated_val; - for (elems[1..]) |*elem| { - elem.* = try repeated_val.copy(arena); - } - - val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - - return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - &elems[elem_ptr.index], - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - - .aggregate => return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - &val_ptr.castTag(.aggregate).?.data[elem_ptr.index], - ptr_elem_ty, - parent.decl_ref_mut, - ), - - .the_only_possible_value => { - const duped = try sema.arena.create(Value); - duped.* = Value.initTag(.the_only_possible_value); - return beginComptimePtrMutationInner( - sema, - block, - src, - elem_ty, - duped, - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - else => unreachable, } }, @@ -27738,7 +27743,7 @@ fn beginComptimePtrMutation( var parent = try sema.beginComptimePtrMutation(block, src, field_ptr.container_ptr, field_ptr.container_ty); switch (parent.pointee) { - .direct => |val_ptr| switch (val_ptr.tag()) { + .direct => |val_ptr| switch (val_ptr.ip_index) { .undef => { // A struct or union has been initialized to undefined at comptime and now we // are for the first time setting a field. We must change the representation @@ -27815,72 +27820,75 @@ fn beginComptimePtrMutation( else => unreachable, } }, - .aggregate => return beginComptimePtrMutationInner( - sema, - block, - src, - parent.ty.structFieldType(field_index), - &val_ptr.castTag(.aggregate).?.data[field_index], - ptr_elem_ty, - parent.decl_ref_mut, - ), - - .@"union" => { - // We need to set the active field of the union. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const payload = &val_ptr.castTag(.@"union").?.data; - payload.tag = try Value.Tag.enum_field_index.create(arena, field_index); - - return beginComptimePtrMutationInner( + .none => switch (val_ptr.tag()) { + .aggregate => return beginComptimePtrMutationInner( sema, block, src, parent.ty.structFieldType(field_index), - &payload.val, - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - .slice => switch (field_index) { - Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner( - sema, - block, - src, - parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), - &val_ptr.castTag(.slice).?.data.ptr, + &val_ptr.castTag(.aggregate).?.data[field_index], ptr_elem_ty, parent.decl_ref_mut, ), - Value.Payload.Slice.len_index => return beginComptimePtrMutationInner( - sema, - block, - src, - Type.usize, - &val_ptr.castTag(.slice).?.data.len, - ptr_elem_ty, - parent.decl_ref_mut, - ), + .@"union" => { + // We need to set the active field of the union. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); + + const payload = &val_ptr.castTag(.@"union").?.data; + payload.tag = try Value.Tag.enum_field_index.create(arena, field_index); + + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index), + &payload.val, + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .slice => switch (field_index) { + Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), + &val_ptr.castTag(.slice).?.data.ptr, + ptr_elem_ty, + parent.decl_ref_mut, + ), + + Value.Payload.Slice.len_index => return beginComptimePtrMutationInner( + sema, + block, + src, + Type.usize, + &val_ptr.castTag(.slice).?.data.len, + ptr_elem_ty, + parent.decl_ref_mut, + ), + + else => unreachable, + }, + + .empty_struct_value => { + const duped = try sema.arena.create(Value); + duped.* = Value.initTag(.the_only_possible_value); + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index), + duped, + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, else => unreachable, }, - - .empty_struct_value => { - const duped = try sema.arena.create(Value); - duped.* = Value.initTag(.the_only_possible_value); - return beginComptimePtrMutationInner( - sema, - block, - src, - parent.ty.structFieldType(field_index), - duped, - ptr_elem_ty, - parent.decl_ref_mut, - ); - }, - else => unreachable, }, .reinterpret => |reinterpret| { @@ -27951,7 +27959,7 @@ fn beginComptimePtrMutation( switch (parent.pointee) { .direct => |val_ptr| { const payload_ty = parent.ty.optionalChild(mod); - switch (val_ptr.tag()) { + switch (val_ptr.ip_index) { .undef, .null_value => { // An optional has been initialized to undefined at comptime and now we // are for the first time setting the payload. We must change the @@ -27973,12 +27981,19 @@ fn beginComptimePtrMutation( .ty = payload_ty, }; }, - .opt_payload => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data }, - .ty = payload_ty, - }, + .none => switch (val_ptr.tag()) { + .opt_payload => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data }, + .ty = payload_ty, + }, + else => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .direct = val_ptr }, + .ty = payload_ty, + }, + }, else => return ComptimePtrMutationKit{ .decl_ref_mut = parent.decl_ref_mut, .pointee = .{ .direct = val_ptr }, @@ -28092,231 +28107,236 @@ fn beginComptimePtrLoad( ) ComptimePtrLoadError!ComptimePtrLoadKit { const mod = sema.mod; const target = sema.mod.getTarget(); - var deref: ComptimePtrLoadKit = switch (ptr_val.tag()) { - .decl_ref, - .decl_ref_mut, - => blk: { - const decl_index = switch (ptr_val.tag()) { - .decl_ref => ptr_val.castTag(.decl_ref).?.data, - .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index, - else => unreachable, - }; - const is_mutable = ptr_val.tag() == .decl_ref_mut; - const decl = sema.mod.declPtr(decl_index); - const decl_tv = try decl.typedValue(); - if (decl_tv.val.tag() == .variable) return error.RuntimeLoad; - const layout_defined = decl.ty.hasWellDefinedLayout(mod); - break :blk ComptimePtrLoadKit{ - .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, - .pointee = decl_tv, - .is_mutable = is_mutable, - .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, - }; - }, - - .elem_ptr => blk: { - const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; - const elem_ty = elem_ptr.elem_ty; - var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.array_ptr, null); - - // This code assumes that elem_ptrs have been "flattened" in order for direct dereference - // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that - // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" - if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| { - assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, sema.mod))); - } - - if (elem_ptr.index != 0) { - if (elem_ty.hasWellDefinedLayout(mod)) { - if (deref.parent) |*parent| { - // Update the byte offset (in-place) - const elem_size = try sema.typeAbiSize(elem_ty); - const offset = parent.byte_offset + elem_size * elem_ptr.index; - parent.byte_offset = try sema.usizeCast(block, src, offset); - } - } else { - deref.parent = null; - deref.ty_without_well_defined_layout = elem_ty; - } - } - - // If we're loading an elem_ptr that was derived from a different type - // than the true type of the underlying decl, we cannot deref directly - const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: { - const deref_elem_ty = deref.pointee.?.ty.childType(mod); - break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or - (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; - } else false; - if (!ty_matches) { - deref.pointee = null; - break :blk deref; - } - - var array_tv = deref.pointee.?; - const check_len = array_tv.ty.arrayLenIncludingSentinel(mod); - if (maybe_array_ty) |load_ty| { - // It's possible that we're loading a [N]T, in which case we'd like to slice - // the pointee array directly from our parent array. - if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, sema.mod)) { - const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod)); - deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{ - .ty = try Type.array(sema.arena, N, null, elem_ty, sema.mod), - .val = try array_tv.val.sliceArray(sema.mod, sema.arena, elem_ptr.index, elem_ptr.index + N), - } else null; - break :blk deref; - } - } - - if (elem_ptr.index >= check_len) { - deref.pointee = null; - break :blk deref; - } - if (elem_ptr.index == check_len - 1) { - if (array_tv.ty.sentinel(mod)) |sent| { - deref.pointee = TypedValue{ - .ty = elem_ty, - .val = sent, - }; - break :blk deref; - } - } - deref.pointee = TypedValue{ - .ty = elem_ty, - .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index), - }; - break :blk deref; - }, - - .slice => blk: { - const slice = ptr_val.castTag(.slice).?.data; - break :blk try sema.beginComptimePtrLoad(block, src, slice.ptr, null); - }, - - .field_ptr => blk: { - const field_ptr = ptr_val.castTag(.field_ptr).?.data; - const field_index = @intCast(u32, field_ptr.field_index); - var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.container_ptr, field_ptr.container_ty); - - if (field_ptr.container_ty.hasWellDefinedLayout(mod)) { - const struct_ty = field_ptr.container_ty.castTag(.@"struct"); - if (struct_ty != null and struct_ty.?.data.layout == .Packed) { - // packed structs are not byte addressable - deref.parent = null; - } else if (deref.parent) |*parent| { - // Update the byte offset (in-place) - try sema.resolveTypeLayout(field_ptr.container_ty); - const field_offset = field_ptr.container_ty.structFieldOffset(field_index, mod); - parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset); - } - } else { - deref.parent = null; - deref.ty_without_well_defined_layout = field_ptr.container_ty; - } - - const tv = deref.pointee orelse { - deref.pointee = null; - break :blk deref; - }; - const coerce_in_mem_ok = - (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or - (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok; - if (!coerce_in_mem_ok) { - deref.pointee = null; - break :blk deref; - } - - if (field_ptr.container_ty.isSlice(mod)) { - const slice_val = tv.val.castTag(.slice).?.data; - deref.pointee = switch (field_index) { - Value.Payload.Slice.ptr_index => TypedValue{ - .ty = field_ptr.container_ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), - .val = slice_val.ptr, - }, - Value.Payload.Slice.len_index => TypedValue{ - .ty = Type.usize, - .val = slice_val.len, - }, - else => unreachable, - }; - } else { - const field_ty = field_ptr.container_ty.structFieldType(field_index); - deref.pointee = TypedValue{ - .ty = field_ty, - .val = tv.val.fieldValue(tv.ty, mod, field_index), - }; - } - break :blk deref; - }, - - .comptime_field_ptr => blk: { - const comptime_field_ptr = ptr_val.castTag(.comptime_field_ptr).?.data; - break :blk ComptimePtrLoadKit{ - .parent = null, - .pointee = .{ .ty = comptime_field_ptr.field_ty, .val = comptime_field_ptr.field_val }, - .is_mutable = false, - .ty_without_well_defined_layout = comptime_field_ptr.field_ty, - }; - }, - - .opt_payload_ptr, - .eu_payload_ptr, - => blk: { - const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data; - const payload_ty = switch (ptr_val.tag()) { - .eu_payload_ptr => payload_ptr.container_ty.errorUnionPayload(), - .opt_payload_ptr => payload_ptr.container_ty.optionalChild(mod), - else => unreachable, - }; - var deref = try sema.beginComptimePtrLoad(block, src, payload_ptr.container_ptr, payload_ptr.container_ty); - - // eu_payload_ptr and opt_payload_ptr never have a well-defined layout - if (deref.parent != null) { - deref.parent = null; - deref.ty_without_well_defined_layout = payload_ptr.container_ty; - } - - if (deref.pointee) |*tv| { - const coerce_in_mem_ok = - (try sema.coerceInMemoryAllowed(block, payload_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or - (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok; - if (coerce_in_mem_ok) { - const payload_val = switch (ptr_val.tag()) { - .eu_payload_ptr => if (tv.val.castTag(.eu_payload)) |some| some.data else { - return sema.fail(block, src, "attempt to unwrap error: {s}", .{tv.val.castTag(.@"error").?.data.name}); - }, - .opt_payload_ptr => if (tv.val.castTag(.opt_payload)) |some| some.data else opt: { - if (tv.val.isNull(mod)) return sema.fail(block, src, "attempt to use null value", .{}); - break :opt tv.val; - }, - else => unreachable, - }; - tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; - break :blk deref; - } - } - deref.pointee = null; - break :blk deref; - }, + var deref: ComptimePtrLoadKit = switch (ptr_val.ip_index) { .null_value => { return sema.fail(block, src, "attempt to use null value", .{}); }, - .opt_payload => blk: { - const opt_payload = ptr_val.castTag(.opt_payload).?.data; - break :blk try sema.beginComptimePtrLoad(block, src, opt_payload, null); + + .none => switch (ptr_val.tag()) { + .decl_ref, + .decl_ref_mut, + => blk: { + const decl_index = switch (ptr_val.tag()) { + .decl_ref => ptr_val.castTag(.decl_ref).?.data, + .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index, + else => unreachable, + }; + const is_mutable = ptr_val.tag() == .decl_ref_mut; + const decl = sema.mod.declPtr(decl_index); + const decl_tv = try decl.typedValue(); + if (decl_tv.val.tag() == .variable) return error.RuntimeLoad; + + const layout_defined = decl.ty.hasWellDefinedLayout(mod); + break :blk ComptimePtrLoadKit{ + .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null, + .pointee = decl_tv, + .is_mutable = is_mutable, + .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, + }; + }, + + .elem_ptr => blk: { + const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; + const elem_ty = elem_ptr.elem_ty; + var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.array_ptr, null); + + // This code assumes that elem_ptrs have been "flattened" in order for direct dereference + // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that + // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" + if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| { + assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, sema.mod))); + } + + if (elem_ptr.index != 0) { + if (elem_ty.hasWellDefinedLayout(mod)) { + if (deref.parent) |*parent| { + // Update the byte offset (in-place) + const elem_size = try sema.typeAbiSize(elem_ty); + const offset = parent.byte_offset + elem_size * elem_ptr.index; + parent.byte_offset = try sema.usizeCast(block, src, offset); + } + } else { + deref.parent = null; + deref.ty_without_well_defined_layout = elem_ty; + } + } + + // If we're loading an elem_ptr that was derived from a different type + // than the true type of the underlying decl, we cannot deref directly + const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: { + const deref_elem_ty = deref.pointee.?.ty.childType(mod); + break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or + (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; + } else false; + if (!ty_matches) { + deref.pointee = null; + break :blk deref; + } + + var array_tv = deref.pointee.?; + const check_len = array_tv.ty.arrayLenIncludingSentinel(mod); + if (maybe_array_ty) |load_ty| { + // It's possible that we're loading a [N]T, in which case we'd like to slice + // the pointee array directly from our parent array. + if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, sema.mod)) { + const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod)); + deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{ + .ty = try Type.array(sema.arena, N, null, elem_ty, sema.mod), + .val = try array_tv.val.sliceArray(sema.mod, sema.arena, elem_ptr.index, elem_ptr.index + N), + } else null; + break :blk deref; + } + } + + if (elem_ptr.index >= check_len) { + deref.pointee = null; + break :blk deref; + } + if (elem_ptr.index == check_len - 1) { + if (array_tv.ty.sentinel(mod)) |sent| { + deref.pointee = TypedValue{ + .ty = elem_ty, + .val = sent, + }; + break :blk deref; + } + } + deref.pointee = TypedValue{ + .ty = elem_ty, + .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index), + }; + break :blk deref; + }, + + .slice => blk: { + const slice = ptr_val.castTag(.slice).?.data; + break :blk try sema.beginComptimePtrLoad(block, src, slice.ptr, null); + }, + + .field_ptr => blk: { + const field_ptr = ptr_val.castTag(.field_ptr).?.data; + const field_index = @intCast(u32, field_ptr.field_index); + var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.container_ptr, field_ptr.container_ty); + + if (field_ptr.container_ty.hasWellDefinedLayout(mod)) { + const struct_ty = field_ptr.container_ty.castTag(.@"struct"); + if (struct_ty != null and struct_ty.?.data.layout == .Packed) { + // packed structs are not byte addressable + deref.parent = null; + } else if (deref.parent) |*parent| { + // Update the byte offset (in-place) + try sema.resolveTypeLayout(field_ptr.container_ty); + const field_offset = field_ptr.container_ty.structFieldOffset(field_index, mod); + parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset); + } + } else { + deref.parent = null; + deref.ty_without_well_defined_layout = field_ptr.container_ty; + } + + const tv = deref.pointee orelse { + deref.pointee = null; + break :blk deref; + }; + const coerce_in_mem_ok = + (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or + (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok; + if (!coerce_in_mem_ok) { + deref.pointee = null; + break :blk deref; + } + + if (field_ptr.container_ty.isSlice(mod)) { + const slice_val = tv.val.castTag(.slice).?.data; + deref.pointee = switch (field_index) { + Value.Payload.Slice.ptr_index => TypedValue{ + .ty = field_ptr.container_ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), + .val = slice_val.ptr, + }, + Value.Payload.Slice.len_index => TypedValue{ + .ty = Type.usize, + .val = slice_val.len, + }, + else => unreachable, + }; + } else { + const field_ty = field_ptr.container_ty.structFieldType(field_index); + deref.pointee = TypedValue{ + .ty = field_ty, + .val = tv.val.fieldValue(tv.ty, mod, field_index), + }; + } + break :blk deref; + }, + + .comptime_field_ptr => blk: { + const comptime_field_ptr = ptr_val.castTag(.comptime_field_ptr).?.data; + break :blk ComptimePtrLoadKit{ + .parent = null, + .pointee = .{ .ty = comptime_field_ptr.field_ty, .val = comptime_field_ptr.field_val }, + .is_mutable = false, + .ty_without_well_defined_layout = comptime_field_ptr.field_ty, + }; + }, + + .opt_payload_ptr, + .eu_payload_ptr, + => blk: { + const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data; + const payload_ty = switch (ptr_val.tag()) { + .eu_payload_ptr => payload_ptr.container_ty.errorUnionPayload(), + .opt_payload_ptr => payload_ptr.container_ty.optionalChild(mod), + else => unreachable, + }; + var deref = try sema.beginComptimePtrLoad(block, src, payload_ptr.container_ptr, payload_ptr.container_ty); + + // eu_payload_ptr and opt_payload_ptr never have a well-defined layout + if (deref.parent != null) { + deref.parent = null; + deref.ty_without_well_defined_layout = payload_ptr.container_ty; + } + + if (deref.pointee) |*tv| { + const coerce_in_mem_ok = + (try sema.coerceInMemoryAllowed(block, payload_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or + (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok; + if (coerce_in_mem_ok) { + const payload_val = switch (ptr_val.tag()) { + .eu_payload_ptr => if (tv.val.castTag(.eu_payload)) |some| some.data else { + return sema.fail(block, src, "attempt to unwrap error: {s}", .{tv.val.castTag(.@"error").?.data.name}); + }, + .opt_payload_ptr => if (tv.val.castTag(.opt_payload)) |some| some.data else opt: { + if (tv.val.isNull(mod)) return sema.fail(block, src, "attempt to use null value", .{}); + break :opt tv.val; + }, + else => unreachable, + }; + tv.* = TypedValue{ .ty = payload_ty, .val = payload_val }; + break :blk deref; + } + } + deref.pointee = null; + break :blk deref; + }, + .opt_payload => blk: { + const opt_payload = ptr_val.castTag(.opt_payload).?.data; + break :blk try sema.beginComptimePtrLoad(block, src, opt_payload, null); + }, + + .zero, + .one, + .int_u64, + .int_i64, + .int_big_positive, + .int_big_negative, + .variable, + .extern_fn, + .function, + => return error.RuntimeLoad, + + else => unreachable, }, - - .zero, - .one, - .int_u64, - .int_i64, - .int_big_positive, - .int_big_negative, - .variable, - .extern_fn, - .function, - => return error.RuntimeLoad, - else => unreachable, }; @@ -28953,7 +28973,7 @@ fn coerceTupleToStruct( const field_name = fields.keys()[i]; const field = fields.values()[i]; const field_src = inst_src; // TODO better source location - if (field.default_val.tag() == .unreachable_value) { + if (field.default_val.ip_index == .unreachable_value) { const template = "missing struct field: {s}"; const args = .{field_name}; if (root_msg) |msg| { @@ -29023,7 +29043,7 @@ fn coerceTupleToTuple( const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); field_refs[field_index] = coerced; - if (default_val.tag() != .unreachable_value) { + if (default_val.ip_index != .unreachable_value) { const init_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime-known"); }; @@ -29052,7 +29072,7 @@ fn coerceTupleToTuple( const field_ty = tuple_ty.structFieldType(i); const field_src = inst_src; // TODO better source location - if (default_val.tag() == .unreachable_value) { + if (default_val.ip_index == .unreachable_value) { if (tuple_ty.isTuple()) { const template = "missing tuple field: {d}"; if (root_msg) |msg| { @@ -31557,7 +31577,7 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .tuple, .anon_struct => { const tuple = ty.tupleFields(); for (tuple.types, 0..) |field_ty, i| { - const have_comptime_val = tuple.values[i].tag() != .unreachable_value; + const have_comptime_val = tuple.values[i].ip_index != .unreachable_value; if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty)) { return true; } @@ -32141,7 +32161,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void gop.value_ptr.* = .{ .ty = Type.noreturn, .abi_align = 0, - .default_val = Value.initTag(.unreachable_value), + .default_val = Value.@"unreachable", .is_comptime = is_comptime, .offset = undefined, }; @@ -32965,7 +32985,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { => return null, .void => return Value.void, - .noreturn => return Value.initTag(.unreachable_value), + .noreturn => return Value.@"unreachable", .null => return Value.null, .undefined => return Value.undef, @@ -33027,7 +33047,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .tuple, .anon_struct => { const tuple = ty.tupleFields(); for (tuple.values, 0..) |val, i| { - const is_comptime = val.tag() != .unreachable_value; + const is_comptime = val.ip_index != .unreachable_value; if (is_comptime) continue; if ((try sema.typeHasOnePossibleValue(tuple.types[i])) != null) continue; return null; @@ -33059,7 +33079,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { return null; } switch (enum_obj.fields.count()) { - 0 => return Value.initTag(.unreachable_value), + 0 => return Value.@"unreachable", 1 => if (enum_obj.values.count() == 0) { return Value.zero; // auto-numbered } else { @@ -33072,7 +33092,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { const resolved_ty = try sema.resolveTypeFields(ty); const enum_simple = resolved_ty.castTag(.enum_simple).?.data; switch (enum_simple.fields.count()) { - 0 => return Value.initTag(.unreachable_value), + 0 => return Value.@"unreachable", 1 => return Value.zero, else => return null, } @@ -33091,7 +33111,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse return null; const fields = union_obj.fields.values(); - if (fields.len == 0) return Value.initTag(.unreachable_value); + if (fields.len == 0) return Value.@"unreachable"; const only_field = fields[0]; if (only_field.ty.eql(resolved_ty, sema.mod)) { const msg = try Module.ErrorMsg.create( @@ -33600,7 +33620,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .tuple, .anon_struct => { const tuple = ty.tupleFields(); for (tuple.types, 0..) |field_ty, i| { - const have_comptime_val = tuple.values[i].tag() != .unreachable_value; + const have_comptime_val = tuple.values[i].ip_index != .unreachable_value; if (!have_comptime_val and try sema.typeRequiresComptime(field_ty)) { return true; } @@ -33814,7 +33834,7 @@ fn numberAddWrapScalar( rhs: Value, ty: Type, ) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + if (lhs.isUndef() or rhs.isUndef()) return Value.undef; const mod = sema.mod; if (ty.zigTypeTag(mod) == .ComptimeInt) { @@ -33874,7 +33894,7 @@ fn numberSubWrapScalar( rhs: Value, ty: Type, ) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + if (lhs.isUndef() or rhs.isUndef()) return Value.undef; const mod = sema.mod; if (ty.zigTypeTag(mod) == .ComptimeInt) { @@ -34156,12 +34176,16 @@ fn intFitsInType( ) CompileError!bool { const mod = sema.mod; const target = mod.getTarget(); - switch (val.tag()) { - .zero, + switch (val.ip_index) { .undef, + .zero, + .zero_usize, + .zero_u8, => return true, - .one => switch (ty.zigTypeTag(mod)) { + .one, + .one_usize, + => switch (ty.zigTypeTag(mod)) { .Int => { const info = ty.intInfo(mod); return switch (info.signedness) { @@ -34173,111 +34197,129 @@ fn intFitsInType( else => unreachable, }, - .lazy_align => switch (ty.zigTypeTag(mod)) { - .Int => { - const info = ty.intInfo(mod); - const max_needed_bits = @as(u16, 16) + @boolToInt(info.signedness == .signed); - // If it is u16 or bigger we know the alignment fits without resolving it. - if (info.bits >= max_needed_bits) return true; - const x = try sema.typeAbiAlignment(val.castTag(.lazy_align).?.data); - if (x == 0) return true; - const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); - return info.bits >= actual_needed_bits; - }, - .ComptimeInt => return true, - else => unreachable, - }, - .lazy_size => switch (ty.zigTypeTag(mod)) { - .Int => { - const info = ty.intInfo(mod); - const max_needed_bits = @as(u16, 64) + @boolToInt(info.signedness == .signed); - // If it is u64 or bigger we know the size fits without resolving it. - if (info.bits >= max_needed_bits) return true; - const x = try sema.typeAbiSize(val.castTag(.lazy_size).?.data); - if (x == 0) return true; - const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); - return info.bits >= actual_needed_bits; - }, - .ComptimeInt => return true, - else => unreachable, - }, + .none => switch (val.tag()) { + .zero => return true, - .int_u64 => switch (ty.zigTypeTag(mod)) { - .Int => { - const x = val.castTag(.int_u64).?.data; - if (x == 0) return true; - const info = ty.intInfo(mod); - const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); - return info.bits >= needed_bits; + .one => switch (ty.zigTypeTag(mod)) { + .Int => { + const info = ty.intInfo(mod); + return switch (info.signedness) { + .signed => info.bits >= 2, + .unsigned => info.bits >= 1, + }; + }, + .ComptimeInt => return true, + else => unreachable, }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_i64 => switch (ty.zigTypeTag(mod)) { - .Int => { - const x = val.castTag(.int_i64).?.data; - if (x == 0) return true; - const info = ty.intInfo(mod); - if (info.signedness == .unsigned and x < 0) - return false; - var buffer: Value.BigIntSpace = undefined; - return (try val.toBigIntAdvanced(&buffer, mod, sema)).fitsInTwosComp(info.signedness, info.bits); - }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_big_positive => switch (ty.zigTypeTag(mod)) { - .Int => { - const info = ty.intInfo(mod); - return val.castTag(.int_big_positive).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); - }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_big_negative => switch (ty.zigTypeTag(mod)) { - .Int => { - const info = ty.intInfo(mod); - return val.castTag(.int_big_negative).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); - }, - .ComptimeInt => return true, - else => unreachable, - }, - .the_only_possible_value => { - assert(ty.intInfo(mod).bits == 0); - return true; - }, - - .decl_ref_mut, - .extern_fn, - .decl_ref, - .function, - .variable, - => switch (ty.zigTypeTag(mod)) { - .Int => { - const info = ty.intInfo(mod); - const ptr_bits = target.ptrBitWidth(); - return switch (info.signedness) { - .signed => info.bits > ptr_bits, - .unsigned => info.bits >= ptr_bits, - }; + .lazy_align => switch (ty.zigTypeTag(mod)) { + .Int => { + const info = ty.intInfo(mod); + const max_needed_bits = @as(u16, 16) + @boolToInt(info.signedness == .signed); + // If it is u16 or bigger we know the alignment fits without resolving it. + if (info.bits >= max_needed_bits) return true; + const x = try sema.typeAbiAlignment(val.castTag(.lazy_align).?.data); + if (x == 0) return true; + const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= actual_needed_bits; + }, + .ComptimeInt => return true, + else => unreachable, + }, + .lazy_size => switch (ty.zigTypeTag(mod)) { + .Int => { + const info = ty.intInfo(mod); + const max_needed_bits = @as(u16, 64) + @boolToInt(info.signedness == .signed); + // If it is u64 or bigger we know the size fits without resolving it. + if (info.bits >= max_needed_bits) return true; + const x = try sema.typeAbiSize(val.castTag(.lazy_size).?.data); + if (x == 0) return true; + const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= actual_needed_bits; + }, + .ComptimeInt => return true, + else => unreachable, }, - .ComptimeInt => return true, - else => unreachable, - }, - .aggregate => { - assert(ty.zigTypeTag(mod) == .Vector); - for (val.castTag(.aggregate).?.data, 0..) |elem, i| { - if (!(try sema.intFitsInType(elem, ty.scalarType(mod), null))) { - if (vector_index) |some| some.* = i; - return false; + .int_u64 => switch (ty.zigTypeTag(mod)) { + .Int => { + const x = val.castTag(.int_u64).?.data; + if (x == 0) return true; + const info = ty.intInfo(mod); + const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= needed_bits; + }, + .ComptimeInt => return true, + else => unreachable, + }, + .int_i64 => switch (ty.zigTypeTag(mod)) { + .Int => { + const x = val.castTag(.int_i64).?.data; + if (x == 0) return true; + const info = ty.intInfo(mod); + if (info.signedness == .unsigned and x < 0) + return false; + var buffer: Value.BigIntSpace = undefined; + return (try val.toBigIntAdvanced(&buffer, mod, sema)).fitsInTwosComp(info.signedness, info.bits); + }, + .ComptimeInt => return true, + else => unreachable, + }, + .int_big_positive => switch (ty.zigTypeTag(mod)) { + .Int => { + const info = ty.intInfo(mod); + return val.castTag(.int_big_positive).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); + }, + .ComptimeInt => return true, + else => unreachable, + }, + .int_big_negative => switch (ty.zigTypeTag(mod)) { + .Int => { + const info = ty.intInfo(mod); + return val.castTag(.int_big_negative).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); + }, + .ComptimeInt => return true, + else => unreachable, + }, + + .the_only_possible_value => { + assert(ty.intInfo(mod).bits == 0); + return true; + }, + + .decl_ref_mut, + .extern_fn, + .decl_ref, + .function, + .variable, + => switch (ty.zigTypeTag(mod)) { + .Int => { + const info = ty.intInfo(mod); + const ptr_bits = target.ptrBitWidth(); + return switch (info.signedness) { + .signed => info.bits > ptr_bits, + .unsigned => info.bits >= ptr_bits, + }; + }, + .ComptimeInt => return true, + else => unreachable, + }, + + .aggregate => { + assert(ty.zigTypeTag(mod) == .Vector); + for (val.castTag(.aggregate).?.data, 0..) |elem, i| { + if (!(try sema.intFitsInType(elem, ty.scalarType(mod), null))) { + if (vector_index) |some| some.* = i; + return false; + } } - } - return true; + return true; + }, + + else => unreachable, }, - else => unreachable, + else => @panic("TODO"), } } diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 7f599caafb..0efd396373 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -76,34 +76,236 @@ pub fn print( if (val.isVariable(mod)) return writer.writeAll("(variable)"); - while (true) switch (val.tag()) { - .empty_struct_value, .aggregate => { - if (level == 0) { - return writer.writeAll(".{ ... }"); - } - if (ty.zigTypeTag(mod) == .Struct) { - try writer.writeAll(".{"); - const max_len = std.math.min(ty.structFieldCount(), max_aggregate_items); + while (true) switch (val.ip_index) { + .none => switch (val.tag()) { + .empty_struct_value, .aggregate => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + if (ty.zigTypeTag(mod) == .Struct) { + try writer.writeAll(".{"); + const max_len = std.math.min(ty.structFieldCount(), max_aggregate_items); - var i: u32 = 0; - while (i < max_len) : (i += 1) { - if (i != 0) try writer.writeAll(", "); - switch (ty.tag()) { - .anon_struct, .@"struct" => try writer.print(".{s} = ", .{ty.structFieldName(i)}), - else => {}, + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + switch (ty.tag()) { + .anon_struct, .@"struct" => try writer.print(".{s} = ", .{ty.structFieldName(i)}), + else => {}, + } + try print(.{ + .ty = ty.structFieldType(i), + .val = val.fieldValue(ty, mod, i), + }, writer, level - 1, mod); } + if (ty.structFieldCount() > max_aggregate_items) { + try writer.writeAll(", ..."); + } + return writer.writeAll("}"); + } else { + const elem_ty = ty.elemType2(mod); + const len = ty.arrayLen(mod); + + if (elem_ty.eql(Type.u8, mod)) str: { + const max_len = @intCast(usize, std.math.min(len, max_string_len)); + var buf: [max_string_len]u8 = undefined; + + var i: u32 = 0; + while (i < max_len) : (i += 1) { + const elem = val.fieldValue(ty, mod, i); + if (elem.isUndef()) break :str; + buf[i] = std.math.cast(u8, elem.toUnsignedInt(mod)) orelse break :str; + } + + const truncated = if (len > max_string_len) " (truncated)" else ""; + return writer.print("\"{}{s}\"", .{ std.zig.fmtEscapes(buf[0..max_len]), truncated }); + } + + try writer.writeAll(".{ "); + + const max_len = std.math.min(len, max_aggregate_items); + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try print(.{ + .ty = elem_ty, + .val = val.fieldValue(ty, mod, i), + }, writer, level - 1, mod); + } + if (len > max_aggregate_items) { + try writer.writeAll(", ..."); + } + return writer.writeAll(" }"); + } + }, + .@"union" => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + const union_val = val.castTag(.@"union").?.data; + try writer.writeAll(".{ "); + + try print(.{ + .ty = ty.cast(Type.Payload.Union).?.data.tag_ty, + .val = union_val.tag, + }, writer, level - 1, mod); + try writer.writeAll(" = "); + try print(.{ + .ty = ty.unionFieldType(union_val.tag, mod), + .val = union_val.val, + }, writer, level - 1, mod); + + return writer.writeAll(" }"); + }, + .zero => return writer.writeAll("0"), + .one => return writer.writeAll("1"), + .the_only_possible_value => return writer.writeAll("0"), + .ty => return val.castTag(.ty).?.data.print(writer, mod), + .int_u64 => return std.fmt.formatIntValue(val.castTag(.int_u64).?.data, "", .{}, writer), + .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", .{}, writer), + .int_big_positive => return writer.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}), + .int_big_negative => return writer.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}), + .lazy_align => { + const sub_ty = val.castTag(.lazy_align).?.data; + const x = sub_ty.abiAlignment(mod); + return writer.print("{d}", .{x}); + }, + .lazy_size => { + const sub_ty = val.castTag(.lazy_size).?.data; + const x = sub_ty.abiSize(mod); + return writer.print("{d}", .{x}); + }, + .function => return writer.print("(function '{s}')", .{ + mod.declPtr(val.castTag(.function).?.data.owner_decl).name, + }), + .extern_fn => return writer.writeAll("(extern function)"), + .variable => unreachable, + .decl_ref_mut => { + const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index; + const decl = mod.declPtr(decl_index); + if (level == 0) { + return writer.print("(decl ref mut '{s}')", .{decl.name}); + } + return print(.{ + .ty = decl.ty, + .val = decl.val, + }, writer, level - 1, mod); + }, + .decl_ref => { + const decl_index = val.castTag(.decl_ref).?.data; + const decl = mod.declPtr(decl_index); + if (level == 0) { + return writer.print("(decl ref '{s}')", .{decl.name}); + } + return print(.{ + .ty = decl.ty, + .val = decl.val, + }, writer, level - 1, mod); + }, + .comptime_field_ptr => { + const payload = val.castTag(.comptime_field_ptr).?.data; + if (level == 0) { + return writer.writeAll("(comptime field ptr)"); + } + return print(.{ + .ty = payload.field_ty, + .val = payload.field_val, + }, writer, level - 1, mod); + }, + .elem_ptr => { + const elem_ptr = val.castTag(.elem_ptr).?.data; + try writer.writeAll("&"); + if (level == 0) { + try writer.writeAll("(ptr)"); + } else { try print(.{ - .ty = ty.structFieldType(i), - .val = val.fieldValue(ty, mod, i), + .ty = elem_ptr.elem_ty, + .val = elem_ptr.array_ptr, }, writer, level - 1, mod); } - if (ty.structFieldCount() > max_aggregate_items) { + return writer.print("[{}]", .{elem_ptr.index}); + }, + .field_ptr => { + const field_ptr = val.castTag(.field_ptr).?.data; + try writer.writeAll("&"); + if (level == 0) { + try writer.writeAll("(ptr)"); + } else { + try print(.{ + .ty = field_ptr.container_ty, + .val = field_ptr.container_ptr, + }, writer, level - 1, mod); + } + + if (field_ptr.container_ty.zigTypeTag(mod) == .Struct) { + switch (field_ptr.container_ty.tag()) { + .tuple => return writer.print(".@\"{d}\"", .{field_ptr.field_index}), + else => { + const field_name = field_ptr.container_ty.structFieldName(field_ptr.field_index); + return writer.print(".{s}", .{field_name}); + }, + } + } else if (field_ptr.container_ty.zigTypeTag(mod) == .Union) { + const field_name = field_ptr.container_ty.unionFields().keys()[field_ptr.field_index]; + return writer.print(".{s}", .{field_name}); + } else if (field_ptr.container_ty.isSlice(mod)) { + switch (field_ptr.field_index) { + Value.Payload.Slice.ptr_index => return writer.writeAll(".ptr"), + Value.Payload.Slice.len_index => return writer.writeAll(".len"), + else => unreachable, + } + } + }, + .empty_array => return writer.writeAll(".{}"), + .enum_literal => return writer.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}), + .enum_field_index => { + return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data)}); + }, + .bytes => return writer.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), + .str_lit => { + const str_lit = val.castTag(.str_lit).?.data; + const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + return writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}); + }, + .repeated => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + var i: u32 = 0; + try writer.writeAll(".{ "); + const elem_tv = TypedValue{ + .ty = ty.elemType2(mod), + .val = val.castTag(.repeated).?.data, + }; + const len = ty.arrayLen(mod); + const max_len = std.math.min(len, max_aggregate_items); + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try print(elem_tv, writer, level - 1, mod); + } + if (len > max_aggregate_items) { try writer.writeAll(", ..."); } - return writer.writeAll("}"); - } else { + return writer.writeAll(" }"); + }, + .empty_array_sentinel => { + if (level == 0) { + return writer.writeAll(".{ (sentinel) }"); + } + try writer.writeAll(".{ "); + try print(.{ + .ty = ty.elemType2(mod), + .val = ty.sentinel(mod).?, + }, writer, level - 1, mod); + return writer.writeAll(" }"); + }, + .slice => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + const payload = val.castTag(.slice).?.data; const elem_ty = ty.elemType2(mod); - const len = ty.arrayLen(mod); + const len = payload.len.toUnsignedInt(mod); if (elem_ty.eql(Type.u8, mod)) str: { const max_len = @intCast(usize, std.math.min(len, max_string_len)); @@ -111,11 +313,13 @@ pub fn print( var i: u32 = 0; while (i < max_len) : (i += 1) { - const elem = val.fieldValue(ty, mod, i); - if (elem.isUndef()) break :str; - buf[i] = std.math.cast(u8, elem.toUnsignedInt(mod)) orelse break :str; + var elem_buf: Value.ElemValueBuffer = undefined; + const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf); + if (elem_val.isUndef()) break :str; + buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(mod)) orelse break :str; } + // TODO would be nice if this had a bit of unicode awareness. const truncated = if (len > max_string_len) " (truncated)" else ""; return writer.print("\"{}{s}\"", .{ std.zig.fmtEscapes(buf[0..max_len]), truncated }); } @@ -126,292 +330,91 @@ pub fn print( var i: u32 = 0; while (i < max_len) : (i += 1) { if (i != 0) try writer.writeAll(", "); + var buf: Value.ElemValueBuffer = undefined; try print(.{ .ty = elem_ty, - .val = val.fieldValue(ty, mod, i), + .val = payload.ptr.elemValueBuffer(mod, i, &buf), }, writer, level - 1, mod); } if (len > max_aggregate_items) { try writer.writeAll(", ..."); } return writer.writeAll(" }"); - } - }, - .@"union" => { - if (level == 0) { - return writer.writeAll(".{ ... }"); - } - const union_val = val.castTag(.@"union").?.data; - try writer.writeAll(".{ "); + }, + .float_16 => return writer.print("{d}", .{val.castTag(.float_16).?.data}), + .float_32 => return writer.print("{d}", .{val.castTag(.float_32).?.data}), + .float_64 => return writer.print("{d}", .{val.castTag(.float_64).?.data}), + .float_80 => return writer.print("{d}", .{@floatCast(f64, val.castTag(.float_80).?.data)}), + .float_128 => return writer.print("{d}", .{@floatCast(f64, val.castTag(.float_128).?.data)}), + .@"error" => return writer.print("error.{s}", .{val.castTag(.@"error").?.data.name}), + .eu_payload => { + val = val.castTag(.eu_payload).?.data; + ty = ty.errorUnionPayload(); + }, + .opt_payload => { + val = val.castTag(.opt_payload).?.data; + ty = ty.optionalChild(mod); + return print(.{ .ty = ty, .val = val }, writer, level, mod); + }, + .eu_payload_ptr => { + try writer.writeAll("&"); - try print(.{ - .ty = ty.cast(Type.Payload.Union).?.data.tag_ty, - .val = union_val.tag, - }, writer, level - 1, mod); - try writer.writeAll(" = "); - try print(.{ - .ty = ty.unionFieldType(union_val.tag, mod), - .val = union_val.val, - }, writer, level - 1, mod); + const data = val.castTag(.eu_payload_ptr).?.data; - return writer.writeAll(" }"); - }, - .null_value => return writer.writeAll("null"), - .undef => return writer.writeAll("undefined"), - .zero => return writer.writeAll("0"), - .one => return writer.writeAll("1"), - .unreachable_value => return writer.writeAll("unreachable"), - .the_only_possible_value => return writer.writeAll("0"), - .ty => return val.castTag(.ty).?.data.print(writer, mod), - .int_u64 => return std.fmt.formatIntValue(val.castTag(.int_u64).?.data, "", .{}, writer), - .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", .{}, writer), - .int_big_positive => return writer.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}), - .int_big_negative => return writer.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}), - .lazy_align => { - const sub_ty = val.castTag(.lazy_align).?.data; - const x = sub_ty.abiAlignment(mod); - return writer.print("{d}", .{x}); - }, - .lazy_size => { - const sub_ty = val.castTag(.lazy_size).?.data; - const x = sub_ty.abiSize(mod); - return writer.print("{d}", .{x}); - }, - .function => return writer.print("(function '{s}')", .{ - mod.declPtr(val.castTag(.function).?.data.owner_decl).name, - }), - .extern_fn => return writer.writeAll("(extern function)"), - .variable => unreachable, - .decl_ref_mut => { - const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index; - const decl = mod.declPtr(decl_index); - if (level == 0) { - return writer.print("(decl ref mut '{s}')", .{decl.name}); - } - return print(.{ - .ty = decl.ty, - .val = decl.val, - }, writer, level - 1, mod); - }, - .decl_ref => { - const decl_index = val.castTag(.decl_ref).?.data; - const decl = mod.declPtr(decl_index); - if (level == 0) { - return writer.print("(decl ref '{s}')", .{decl.name}); - } - return print(.{ - .ty = decl.ty, - .val = decl.val, - }, writer, level - 1, mod); - }, - .comptime_field_ptr => { - const payload = val.castTag(.comptime_field_ptr).?.data; - if (level == 0) { - return writer.writeAll("(comptime field ptr)"); - } - return print(.{ - .ty = payload.field_ty, - .val = payload.field_val, - }, writer, level - 1, mod); - }, - .elem_ptr => { - const elem_ptr = val.castTag(.elem_ptr).?.data; - try writer.writeAll("&"); - if (level == 0) { - try writer.writeAll("(ptr)"); - } else { + var ty_val: Value.Payload.Ty = .{ + .base = .{ .tag = .ty }, + .data = ty, + }; + + try writer.writeAll("@as("); try print(.{ - .ty = elem_ptr.elem_ty, - .val = elem_ptr.array_ptr, + .ty = Type.type, + .val = Value.initPayload(&ty_val.base), }, writer, level - 1, mod); - } - return writer.print("[{}]", .{elem_ptr.index}); - }, - .field_ptr => { - const field_ptr = val.castTag(.field_ptr).?.data; - try writer.writeAll("&"); - if (level == 0) { - try writer.writeAll("(ptr)"); - } else { + + try writer.writeAll(", &(payload of "); + try print(.{ - .ty = field_ptr.container_ty, - .val = field_ptr.container_ptr, + .ty = mod.singleMutPtrType(data.container_ty) catch @panic("OOM"), + .val = data.container_ptr, }, writer, level - 1, mod); - } - if (field_ptr.container_ty.zigTypeTag(mod) == .Struct) { - switch (field_ptr.container_ty.tag()) { - .tuple => return writer.print(".@\"{d}\"", .{field_ptr.field_index}), - else => { - const field_name = field_ptr.container_ty.structFieldName(field_ptr.field_index); - return writer.print(".{s}", .{field_name}); - }, - } - } else if (field_ptr.container_ty.zigTypeTag(mod) == .Union) { - const field_name = field_ptr.container_ty.unionFields().keys()[field_ptr.field_index]; - return writer.print(".{s}", .{field_name}); - } else if (field_ptr.container_ty.isSlice(mod)) { - switch (field_ptr.field_index) { - Value.Payload.Slice.ptr_index => return writer.writeAll(".ptr"), - Value.Payload.Slice.len_index => return writer.writeAll(".len"), - else => unreachable, - } - } - }, - .empty_array => return writer.writeAll(".{}"), - .enum_literal => return writer.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}), - .enum_field_index => { - return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data)}); - }, - .bytes => return writer.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), - .str_lit => { - const str_lit = val.castTag(.str_lit).?.data; - const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; - return writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}); - }, - .repeated => { - if (level == 0) { - return writer.writeAll(".{ ... }"); - } - var i: u32 = 0; - try writer.writeAll(".{ "); - const elem_tv = TypedValue{ - .ty = ty.elemType2(mod), - .val = val.castTag(.repeated).?.data, - }; - const len = ty.arrayLen(mod); - const max_len = std.math.min(len, max_aggregate_items); - while (i < max_len) : (i += 1) { - if (i != 0) try writer.writeAll(", "); - try print(elem_tv, writer, level - 1, mod); - } - if (len > max_aggregate_items) { - try writer.writeAll(", ..."); - } - return writer.writeAll(" }"); - }, - .empty_array_sentinel => { - if (level == 0) { - return writer.writeAll(".{ (sentinel) }"); - } - try writer.writeAll(".{ "); - try print(.{ - .ty = ty.elemType2(mod), - .val = ty.sentinel(mod).?, - }, writer, level - 1, mod); - return writer.writeAll(" }"); - }, - .slice => { - if (level == 0) { - return writer.writeAll(".{ ... }"); - } - const payload = val.castTag(.slice).?.data; - const elem_ty = ty.elemType2(mod); - const len = payload.len.toUnsignedInt(mod); + try writer.writeAll("))"); + return; + }, + .opt_payload_ptr => { + const data = val.castTag(.opt_payload_ptr).?.data; - if (elem_ty.eql(Type.u8, mod)) str: { - const max_len = @intCast(usize, std.math.min(len, max_string_len)); - var buf: [max_string_len]u8 = undefined; + var ty_val: Value.Payload.Ty = .{ + .base = .{ .tag = .ty }, + .data = ty, + }; - var i: u32 = 0; - while (i < max_len) : (i += 1) { - var elem_buf: Value.ElemValueBuffer = undefined; - const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf); - if (elem_val.isUndef()) break :str; - buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(mod)) orelse break :str; - } - - // TODO would be nice if this had a bit of unicode awareness. - const truncated = if (len > max_string_len) " (truncated)" else ""; - return writer.print("\"{}{s}\"", .{ std.zig.fmtEscapes(buf[0..max_len]), truncated }); - } - - try writer.writeAll(".{ "); - - const max_len = std.math.min(len, max_aggregate_items); - var i: u32 = 0; - while (i < max_len) : (i += 1) { - if (i != 0) try writer.writeAll(", "); - var buf: Value.ElemValueBuffer = undefined; + try writer.writeAll("@as("); try print(.{ - .ty = elem_ty, - .val = payload.ptr.elemValueBuffer(mod, i, &buf), + .ty = Type.type, + .val = Value.initPayload(&ty_val.base), }, writer, level - 1, mod); - } - if (len > max_aggregate_items) { - try writer.writeAll(", ..."); - } - return writer.writeAll(" }"); + + try writer.writeAll(", &(payload of "); + + try print(.{ + .ty = mod.singleMutPtrType(data.container_ty) catch @panic("OOM"), + .val = data.container_ptr, + }, writer, level - 1, mod); + + try writer.writeAll("))"); + return; + }, + + // TODO these should not appear in this function + .inferred_alloc => return writer.writeAll("(inferred allocation value)"), + .inferred_alloc_comptime => return writer.writeAll("(inferred comptime allocation value)"), + .runtime_value => return writer.writeAll("[runtime value]"), }, - .float_16 => return writer.print("{d}", .{val.castTag(.float_16).?.data}), - .float_32 => return writer.print("{d}", .{val.castTag(.float_32).?.data}), - .float_64 => return writer.print("{d}", .{val.castTag(.float_64).?.data}), - .float_80 => return writer.print("{d}", .{@floatCast(f64, val.castTag(.float_80).?.data)}), - .float_128 => return writer.print("{d}", .{@floatCast(f64, val.castTag(.float_128).?.data)}), - .@"error" => return writer.print("error.{s}", .{val.castTag(.@"error").?.data.name}), - .eu_payload => { - val = val.castTag(.eu_payload).?.data; - ty = ty.errorUnionPayload(); - }, - .opt_payload => { - val = val.castTag(.opt_payload).?.data; - ty = ty.optionalChild(mod); - return print(.{ .ty = ty, .val = val }, writer, level, mod); - }, - .eu_payload_ptr => { - try writer.writeAll("&"); - - const data = val.castTag(.eu_payload_ptr).?.data; - - var ty_val: Value.Payload.Ty = .{ - .base = .{ .tag = .ty }, - .data = ty, - }; - - try writer.writeAll("@as("); - try print(.{ - .ty = Type.type, - .val = Value.initPayload(&ty_val.base), - }, writer, level - 1, mod); - - try writer.writeAll(", &(payload of "); - - try print(.{ - .ty = mod.singleMutPtrType(data.container_ty) catch @panic("OOM"), - .val = data.container_ptr, - }, writer, level - 1, mod); - - try writer.writeAll("))"); + else => { + try writer.print("(interned: {})", .{val.ip_index}); return; }, - .opt_payload_ptr => { - const data = val.castTag(.opt_payload_ptr).?.data; - - var ty_val: Value.Payload.Ty = .{ - .base = .{ .tag = .ty }, - .data = ty, - }; - - try writer.writeAll("@as("); - try print(.{ - .ty = Type.type, - .val = Value.initPayload(&ty_val.base), - }, writer, level - 1, mod); - - try writer.writeAll(", &(payload of "); - - try print(.{ - .ty = mod.singleMutPtrType(data.container_ty) catch @panic("OOM"), - .val = data.container_ptr, - }, writer, level - 1, mod); - - try writer.writeAll("))"); - return; - }, - - // TODO these should not appear in this function - .inferred_alloc => return writer.writeAll("(inferred allocation value)"), - .inferred_alloc_comptime => return writer.writeAll("(inferred comptime allocation value)"), - .runtime_value => return writer.writeAll("[runtime value]"), }; } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 96304628e9..ea7134c603 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3088,11 +3088,15 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { 64 => return WValue{ .float64 = val.toFloat(f64) }, else => unreachable, }, - .Pointer => switch (val.tag()) { - .field_ptr, .elem_ptr, .opt_payload_ptr => return func.lowerParentPtr(val, 0), - .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(mod)) }, - .zero, .null_value => return WValue{ .imm32 = 0 }, - else => return func.fail("Wasm TODO: lowerConstant for other const pointer tag {}", .{val.tag()}), + .Pointer => switch (val.ip_index) { + .null_value => return WValue{ .imm32 = 0 }, + .none => switch (val.tag()) { + .field_ptr, .elem_ptr, .opt_payload_ptr => return func.lowerParentPtr(val, 0), + .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(mod)) }, + .zero => return WValue{ .imm32 = 0 }, + else => return func.fail("Wasm TODO: lowerConstant for other const pointer tag {}", .{val.tag()}), + }, + else => unreachable, }, .Enum => { if (val.castTag(.enum_field_index)) |field_index| { diff --git a/src/codegen.zig b/src/codegen.zig index a807400502..25e8d892d8 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -312,7 +312,7 @@ pub fn generateSymbol( ), }, }, - .Pointer => switch (typed_value.val.tag()) { + .Pointer => switch (typed_value.val.ip_index) { .null_value => { switch (target.ptrBitWidth()) { 32 => { @@ -327,76 +327,79 @@ pub fn generateSymbol( } return Result.ok; }, - .zero, .one, .int_u64, .int_big_positive => { - switch (target.ptrBitWidth()) { - 32 => { - const x = typed_value.val.toUnsignedInt(mod); - mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, x), endian); - }, - 64 => { - const x = typed_value.val.toUnsignedInt(mod); - mem.writeInt(u64, try code.addManyAsArray(8), x, endian); - }, - else => unreachable, - } - return Result.ok; - }, - .variable, .decl_ref, .decl_ref_mut => |tag| return lowerDeclRef( - bin_file, - src_loc, - typed_value, - switch (tag) { - .variable => typed_value.val.castTag(.variable).?.data.owner_decl, - .decl_ref => typed_value.val.castTag(.decl_ref).?.data, - .decl_ref_mut => typed_value.val.castTag(.decl_ref_mut).?.data.decl_index, - else => unreachable, + .none => switch (typed_value.val.tag()) { + .zero, .one, .int_u64, .int_big_positive => { + switch (target.ptrBitWidth()) { + 32 => { + const x = typed_value.val.toUnsignedInt(mod); + mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, x), endian); + }, + 64 => { + const x = typed_value.val.toUnsignedInt(mod); + mem.writeInt(u64, try code.addManyAsArray(8), x, endian); + }, + else => unreachable, + } + return Result.ok; }, - code, - debug_output, - reloc_info, - ), - .slice => { - const slice = typed_value.val.castTag(.slice).?.data; - - // generate ptr - var buf: Type.SlicePtrFieldTypeBuffer = undefined; - const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf); - switch (try generateSymbol(bin_file, src_loc, .{ - .ty = slice_ptr_field_type, - .val = slice.ptr, - }, code, debug_output, reloc_info)) { - .ok => {}, - .fail => |em| return Result{ .fail = em }, - } - - // generate length - switch (try generateSymbol(bin_file, src_loc, .{ - .ty = Type.usize, - .val = slice.len, - }, code, debug_output, reloc_info)) { - .ok => {}, - .fail => |em| return Result{ .fail = em }, - } - - return Result.ok; - }, - .field_ptr, .elem_ptr, .opt_payload_ptr => return lowerParentPtr( - bin_file, - src_loc, - typed_value, - typed_value.val, - code, - debug_output, - reloc_info, - ), - else => return Result{ - .fail = try ErrorMsg.create( - bin_file.allocator, + .variable, .decl_ref, .decl_ref_mut => |tag| return lowerDeclRef( + bin_file, src_loc, - "TODO implement generateSymbol for pointer type value: '{s}'", - .{@tagName(typed_value.val.tag())}, + typed_value, + switch (tag) { + .variable => typed_value.val.castTag(.variable).?.data.owner_decl, + .decl_ref => typed_value.val.castTag(.decl_ref).?.data, + .decl_ref_mut => typed_value.val.castTag(.decl_ref_mut).?.data.decl_index, + else => unreachable, + }, + code, + debug_output, + reloc_info, ), + .slice => { + const slice = typed_value.val.castTag(.slice).?.data; + + // generate ptr + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf); + switch (try generateSymbol(bin_file, src_loc, .{ + .ty = slice_ptr_field_type, + .val = slice.ptr, + }, code, debug_output, reloc_info)) { + .ok => {}, + .fail => |em| return Result{ .fail = em }, + } + + // generate length + switch (try generateSymbol(bin_file, src_loc, .{ + .ty = Type.usize, + .val = slice.len, + }, code, debug_output, reloc_info)) { + .ok => {}, + .fail => |em| return Result{ .fail = em }, + } + + return Result.ok; + }, + .field_ptr, .elem_ptr, .opt_payload_ptr => return lowerParentPtr( + bin_file, + src_loc, + typed_value, + typed_value.val, + code, + debug_output, + reloc_info, + ), + else => return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "TODO implement generateSymbol for pointer type value: '{s}'", + .{@tagName(typed_value.val.tag())}, + ), + }, }, + else => unreachable, }, .Int => { const info = typed_value.ty.intInfo(mod); @@ -652,7 +655,7 @@ pub fn generateSymbol( } const padding = abi_size - (math.cast(usize, payload_type.abiSize(mod)) orelse return error.Overflow) - 1; - const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.initTag(.undef); + const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.undef; switch (try generateSymbol(bin_file, src_loc, .{ .ty = payload_type, .val = value, @@ -696,7 +699,7 @@ pub fn generateSymbol( // emit payload part of the error union { const begin = code.items.len; - const payload_val = if (typed_value.val.castTag(.eu_payload)) |val| val.data else Value.initTag(.undef); + const payload_val = if (typed_value.val.castTag(.eu_payload)) |val| val.data else Value.undef; switch (try generateSymbol(bin_file, src_loc, .{ .ty = payload_ty, .val = payload_val, @@ -1189,16 +1192,17 @@ pub fn genTypedValue( .Void => return GenResult.mcv(.none), .Pointer => switch (typed_value.ty.ptrSize(mod)) { .Slice => {}, - else => { - switch (typed_value.val.tag()) { - .null_value => { - return GenResult.mcv(.{ .immediate = 0 }); - }, + else => switch (typed_value.val.ip_index) { + .null_value => { + return GenResult.mcv(.{ .immediate = 0 }); + }, + .none => switch (typed_value.val.tag()) { .int_u64 => { return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(mod) }); }, else => {}, - } + }, + else => {}, }, }, .Int => { @@ -1216,7 +1220,7 @@ pub fn genTypedValue( }, .Optional => { if (typed_value.ty.isPtrLikeOptional(mod)) { - if (typed_value.val.tag() == .null_value) return GenResult.mcv(.{ .immediate = 0 }); + if (typed_value.val.ip_index == .null_value) return GenResult.mcv(.{ .immediate = 0 }); return genTypedValue(bin_file, src_loc, .{ .ty = typed_value.ty.optionalChild(mod), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index e6ec461e43..cd3974bc91 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1045,8 +1045,8 @@ pub const DeclGen = struct { if (!empty) try writer.writeByte(')'); return; }, - .Pointer => switch (val.tag()) { - .null_value, .zero => if (ty.isSlice(mod)) { + .Pointer => switch (val.ip_index) { + .null_value => if (ty.isSlice(mod)) { var slice_pl = Value.Payload.Slice{ .base = .{ .tag = .slice }, .data = .{ .ptr = val, .len = Value.undef }, @@ -1059,46 +1059,63 @@ pub const DeclGen = struct { try dg.renderType(writer, ty); try writer.writeAll(")NULL)"); }, - .variable => { - const decl = val.castTag(.variable).?.data.owner_decl; - return dg.renderDeclValue(writer, ty, val, decl, location); - }, - .slice => { - if (!location.isInitializer()) { - try writer.writeByte('('); + .none => switch (val.tag()) { + .zero => if (ty.isSlice(mod)) { + var slice_pl = Value.Payload.Slice{ + .base = .{ .tag = .slice }, + .data = .{ .ptr = val, .len = Value.undef }, + }; + const slice_val = Value.initPayload(&slice_pl.base); + + return dg.renderValue(writer, ty, slice_val, location); + } else { + try writer.writeAll("(("); try dg.renderType(writer, ty); - try writer.writeByte(')'); - } + try writer.writeAll(")NULL)"); + }, + .variable => { + const decl = val.castTag(.variable).?.data.owner_decl; + return dg.renderDeclValue(writer, ty, val, decl, location); + }, + .slice => { + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderType(writer, ty); + try writer.writeByte(')'); + } - const slice = val.castTag(.slice).?.data; - var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const slice = val.castTag(.slice).?.data; + var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try writer.writeByte('{'); - try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, initializer_type); - try writer.writeAll(", "); - try dg.renderValue(writer, Type.usize, slice.len, initializer_type); - try writer.writeByte('}'); + try writer.writeByte('{'); + try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, initializer_type); + try writer.writeAll(", "); + try dg.renderValue(writer, Type.usize, slice.len, initializer_type); + try writer.writeByte('}'); + }, + .function => { + const func = val.castTag(.function).?.data; + try dg.renderDeclName(writer, func.owner_decl, 0); + }, + .extern_fn => { + const extern_fn = val.castTag(.extern_fn).?.data; + try dg.renderDeclName(writer, extern_fn.owner_decl, 0); + }, + .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => { + try writer.writeAll("(("); + try dg.renderType(writer, ty); + return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); + }, + .field_ptr, + .elem_ptr, + .opt_payload_ptr, + .eu_payload_ptr, + .decl_ref_mut, + .decl_ref, + => try dg.renderParentPtr(writer, val, ty, location), + + else => unreachable, }, - .function => { - const func = val.castTag(.function).?.data; - try dg.renderDeclName(writer, func.owner_decl, 0); - }, - .extern_fn => { - const extern_fn = val.castTag(.extern_fn).?.data; - try dg.renderDeclName(writer, extern_fn.owner_decl, 0); - }, - .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => { - try writer.writeAll("(("); - try dg.renderType(writer, ty); - return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); - }, - .field_ptr, - .elem_ptr, - .opt_payload_ptr, - .eu_payload_ptr, - .decl_ref_mut, - .decl_ref, - => try dg.renderParentPtr(writer, val, ty, location), else => unreachable, }, .Array, .Vector => { @@ -1109,8 +1126,8 @@ pub const DeclGen = struct { } // First try specific tag representations for more efficiency. - switch (val.tag()) { - .undef, .empty_struct_value, .empty_array => { + switch (val.ip_index) { + .undef => { const ai = ty.arrayInfo(mod); try writer.writeByte('{'); if (ai.sentinel) |s| { @@ -1119,76 +1136,91 @@ pub const DeclGen = struct { try writer.writeByte('0'); } try writer.writeByte('}'); + return; }, - .bytes, .str_lit => |t| { - const bytes = switch (t) { - .bytes => val.castTag(.bytes).?.data, - .str_lit => bytes: { - const str_lit = val.castTag(.str_lit).?.data; - break :bytes dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; - }, - else => unreachable, - }; - const sentinel = if (ty.sentinel(mod)) |sentinel| @intCast(u8, sentinel.toUnsignedInt(mod)) else null; - try writer.print("{s}", .{ - fmtStringLiteral(bytes[0..@intCast(usize, ty.arrayLen(mod))], sentinel), - }); - }, - else => { - // Fall back to generic implementation. - var arena = std.heap.ArenaAllocator.init(dg.gpa); - defer arena.deinit(); - const arena_allocator = arena.allocator(); - - // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal - const max_string_initializer_len = 65535; - - const ai = ty.arrayInfo(mod); - if (ai.elem_type.eql(Type.u8, dg.module)) { - if (ai.len <= max_string_initializer_len) { - var literal = stringLiteral(writer); - try literal.start(); - var index: usize = 0; - while (index < ai.len) : (index += 1) { - const elem_val = try val.elemValue(dg.module, arena_allocator, index); - const elem_val_u8 = if (elem_val.isUndef()) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(mod)); - try literal.writeChar(elem_val_u8); - } - if (ai.sentinel) |s| { - const s_u8 = @intCast(u8, s.toUnsignedInt(mod)); - if (s_u8 != 0) try literal.writeChar(s_u8); - } - try literal.end(); - } else { - try writer.writeByte('{'); - var index: usize = 0; - while (index < ai.len) : (index += 1) { - if (index != 0) try writer.writeByte(','); - const elem_val = try val.elemValue(dg.module, arena_allocator, index); - const elem_val_u8 = if (elem_val.isUndef()) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(mod)); - try writer.print("'\\x{x}'", .{elem_val_u8}); - } - if (ai.sentinel) |s| { - if (index != 0) try writer.writeByte(','); - try dg.renderValue(writer, ai.elem_type, s, initializer_type); - } - try writer.writeByte('}'); - } - } else { + .none => switch (val.tag()) { + .empty_struct_value, .empty_array => { + const ai = ty.arrayInfo(mod); try writer.writeByte('{'); - var index: usize = 0; - while (index < ai.len) : (index += 1) { - if (index != 0) try writer.writeByte(','); - const elem_val = try val.elemValue(dg.module, arena_allocator, index); - try dg.renderValue(writer, ai.elem_type, elem_val, initializer_type); - } if (ai.sentinel) |s| { - if (index != 0) try writer.writeByte(','); try dg.renderValue(writer, ai.elem_type, s, initializer_type); + } else { + try writer.writeByte('0'); } try writer.writeByte('}'); - } + return; + }, + .bytes, .str_lit => |t| { + const bytes = switch (t) { + .bytes => val.castTag(.bytes).?.data, + .str_lit => bytes: { + const str_lit = val.castTag(.str_lit).?.data; + break :bytes dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + }, + else => unreachable, + }; + const sentinel = if (ty.sentinel(mod)) |sentinel| @intCast(u8, sentinel.toUnsignedInt(mod)) else null; + try writer.print("{s}", .{ + fmtStringLiteral(bytes[0..@intCast(usize, ty.arrayLen(mod))], sentinel), + }); + return; + }, + else => {}, }, + else => {}, + } + // Fall back to generic implementation. + var arena = std.heap.ArenaAllocator.init(dg.gpa); + defer arena.deinit(); + const arena_allocator = arena.allocator(); + + // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal + const max_string_initializer_len = 65535; + + const ai = ty.arrayInfo(mod); + if (ai.elem_type.eql(Type.u8, dg.module)) { + if (ai.len <= max_string_initializer_len) { + var literal = stringLiteral(writer); + try literal.start(); + var index: usize = 0; + while (index < ai.len) : (index += 1) { + const elem_val = try val.elemValue(dg.module, arena_allocator, index); + const elem_val_u8 = if (elem_val.isUndef()) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(mod)); + try literal.writeChar(elem_val_u8); + } + if (ai.sentinel) |s| { + const s_u8 = @intCast(u8, s.toUnsignedInt(mod)); + if (s_u8 != 0) try literal.writeChar(s_u8); + } + try literal.end(); + } else { + try writer.writeByte('{'); + var index: usize = 0; + while (index < ai.len) : (index += 1) { + if (index != 0) try writer.writeByte(','); + const elem_val = try val.elemValue(dg.module, arena_allocator, index); + const elem_val_u8 = if (elem_val.isUndef()) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(mod)); + try writer.print("'\\x{x}'", .{elem_val_u8}); + } + if (ai.sentinel) |s| { + if (index != 0) try writer.writeByte(','); + try dg.renderValue(writer, ai.elem_type, s, initializer_type); + } + try writer.writeByte('}'); + } + } else { + try writer.writeByte('{'); + var index: usize = 0; + while (index < ai.len) : (index += 1) { + if (index != 0) try writer.writeByte(','); + const elem_val = try val.elemValue(dg.module, arena_allocator, index); + try dg.renderValue(writer, ai.elem_type, elem_val, initializer_type); + } + if (ai.sentinel) |s| { + if (index != 0) try writer.writeByte(','); + try dg.renderValue(writer, ai.elem_type, s, initializer_type); + } + try writer.writeByte('}'); } }, .Bool => { @@ -1201,7 +1233,7 @@ pub const DeclGen = struct { .Optional => { const payload_ty = ty.optionalChild(mod); - const is_null_val = Value.makeBool(val.tag() == .null_value); + const is_null_val = Value.makeBool(val.ip_index == .null_value); if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return dg.renderValue(writer, Type.bool, is_null_val, location); @@ -7765,7 +7797,7 @@ fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, mod: *const Module) T if (lowersToArray(ret_ty, mod)) { buffer.names = [1][]const u8{"array"}; buffer.types = [1]Type{ret_ty}; - buffer.values = [1]Value{Value.initTag(.unreachable_value)}; + buffer.values = [1]Value{Value.@"unreachable"}; buffer.payload = .{ .data = .{ .names = &buffer.names, .types = &buffer.types, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f45a63df72..558534a651 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2028,7 +2028,7 @@ pub const Object = struct { for (tuple.types, 0..) |field_ty, i| { const field_val = tuple.values[i]; - if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; + if (field_val.ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; const field_size = field_ty.abiSize(mod); const field_align = field_ty.abiAlignment(mod); @@ -2498,7 +2498,7 @@ pub const DeclGen = struct { global.setGlobalConstant(.True); break :init_val decl.val; }; - if (init_val.tag() != .unreachable_value) { + if (init_val.ip_index != .unreachable_value) { const llvm_init = try dg.lowerValue(.{ .ty = decl.ty, .val = init_val }); if (global.globalGetValueType() == llvm_init.typeOf()) { global.setInitializer(llvm_init); @@ -2954,7 +2954,7 @@ pub const DeclGen = struct { for (tuple.types, 0..) |field_ty, i| { const field_val = tuple.values[i]; - if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; + if (field_val.ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; const field_align = field_ty.abiAlignment(mod); big_align = @max(big_align, field_align); @@ -3359,58 +3359,65 @@ pub const DeclGen = struct { else => unreachable, } }, - .Pointer => switch (tv.val.tag()) { - .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl_index), - .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data), - .variable => { - const decl_index = tv.val.castTag(.variable).?.data.owner_decl; - const decl = dg.module.declPtr(decl_index); - dg.module.markDeclAlive(decl); - - const llvm_wanted_addrspace = toLlvmAddressSpace(decl.@"addrspace", target); - const llvm_actual_addrspace = toLlvmGlobalAddressSpace(decl.@"addrspace", target); - - const val = try dg.resolveGlobalDecl(decl_index); - const addrspace_casted_ptr = if (llvm_actual_addrspace != llvm_wanted_addrspace) - val.constAddrSpaceCast(dg.context.pointerType(llvm_wanted_addrspace)) - else - val; - return addrspace_casted_ptr; - }, - .slice => { - const slice = tv.val.castTag(.slice).?.data; - var buf: Type.SlicePtrFieldTypeBuffer = undefined; - const fields: [2]*llvm.Value = .{ - try dg.lowerValue(.{ - .ty = tv.ty.slicePtrFieldType(&buf), - .val = slice.ptr, - }), - try dg.lowerValue(.{ - .ty = Type.usize, - .val = slice.len, - }), - }; - return dg.context.constStruct(&fields, fields.len, .False); - }, - .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => { - const llvm_usize = try dg.lowerType(Type.usize); - const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(mod), .False); - return llvm_int.constIntToPtr(try dg.lowerType(tv.ty)); - }, - .field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => { - return dg.lowerParentPtr(tv.val, tv.ty.ptrInfo(mod).bit_offset % 8 == 0); - }, - .null_value, .zero => { + .Pointer => switch (tv.val.ip_index) { + .null_value => { const llvm_type = try dg.lowerType(tv.ty); return llvm_type.constNull(); }, - .opt_payload => { - const payload = tv.val.castTag(.opt_payload).?.data; - return dg.lowerParentPtr(payload, tv.ty.ptrInfo(mod).bit_offset % 8 == 0); + .none => switch (tv.val.tag()) { + .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl_index), + .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data), + .variable => { + const decl_index = tv.val.castTag(.variable).?.data.owner_decl; + const decl = dg.module.declPtr(decl_index); + dg.module.markDeclAlive(decl); + + const llvm_wanted_addrspace = toLlvmAddressSpace(decl.@"addrspace", target); + const llvm_actual_addrspace = toLlvmGlobalAddressSpace(decl.@"addrspace", target); + + const val = try dg.resolveGlobalDecl(decl_index); + const addrspace_casted_ptr = if (llvm_actual_addrspace != llvm_wanted_addrspace) + val.constAddrSpaceCast(dg.context.pointerType(llvm_wanted_addrspace)) + else + val; + return addrspace_casted_ptr; + }, + .slice => { + const slice = tv.val.castTag(.slice).?.data; + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const fields: [2]*llvm.Value = .{ + try dg.lowerValue(.{ + .ty = tv.ty.slicePtrFieldType(&buf), + .val = slice.ptr, + }), + try dg.lowerValue(.{ + .ty = Type.usize, + .val = slice.len, + }), + }; + return dg.context.constStruct(&fields, fields.len, .False); + }, + .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => { + const llvm_usize = try dg.lowerType(Type.usize); + const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(mod), .False); + return llvm_int.constIntToPtr(try dg.lowerType(tv.ty)); + }, + .field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => { + return dg.lowerParentPtr(tv.val, tv.ty.ptrInfo(mod).bit_offset % 8 == 0); + }, + .zero => { + const llvm_type = try dg.lowerType(tv.ty); + return llvm_type.constNull(); + }, + .opt_payload => { + const payload = tv.val.castTag(.opt_payload).?.data; + return dg.lowerParentPtr(payload, tv.ty.ptrInfo(mod).bit_offset % 8 == 0); + }, + else => |tag| return dg.todo("implement const of pointer type '{}' ({})", .{ + tv.ty.fmtDebug(), tag, + }), }, - else => |tag| return dg.todo("implement const of pointer type '{}' ({})", .{ - tv.ty.fmtDebug(), tag, - }), + else => unreachable, }, .Array => switch (tv.val.tag()) { .bytes => { @@ -3555,7 +3562,7 @@ pub const DeclGen = struct { var fields_buf: [3]*llvm.Value = undefined; fields_buf[0] = try dg.lowerValue(.{ .ty = payload_ty, - .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef), + .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.undef, }); fields_buf[1] = non_null_bit; if (llvm_field_count > 2) { @@ -3606,7 +3613,7 @@ pub const DeclGen = struct { }); const llvm_payload_value = try dg.lowerValue(.{ .ty = payload_type, - .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef), + .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.undef, }); var fields_buf: [3]*llvm.Value = undefined; @@ -3645,7 +3652,7 @@ pub const DeclGen = struct { var need_unnamed = false; for (tuple.types, 0..) |field_ty, i| { - if (tuple.values[i].tag() != .unreachable_value) continue; + if (tuple.values[i].ip_index != .unreachable_value) continue; if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; const field_align = field_ty.abiAlignment(mod); @@ -10501,7 +10508,7 @@ fn llvmFieldIndex( const tuple = ty.tupleFields(); var llvm_field_index: c_uint = 0; for (tuple.types, 0..) |field_ty, i| { - if (tuple.values[i].tag() != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; + if (tuple.values[i].ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; const field_align = field_ty.abiAlignment(mod); big_align = @max(big_align, field_align); @@ -11117,7 +11124,7 @@ fn isByRef(ty: Type, mod: *const Module) bool { const tuple = ty.tupleFields(); var count: usize = 0; for (tuple.values, 0..) |field_val, i| { - if (field_val.tag() != .unreachable_value or !tuple.types[i].hasRuntimeBits(mod)) continue; + if (field_val.ip_index != .unreachable_value or !tuple.types[i].hasRuntimeBits(mod)) continue; count += 1; if (count > max_fields_byval) return true; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 9de2c03142..5fa81d19ff 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -674,7 +674,7 @@ pub const DeclGen = struct { try self.lower(ptr_ty, slice.ptr); try self.addInt(Type.usize, slice.len); }, - .null_value, .zero => try self.addNullPtr(try dg.resolveType(ty, .indirect)), + .zero => try self.addNullPtr(try dg.resolveType(ty, .indirect)), .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => { try self.addInt(Type.usize, val); }, @@ -813,7 +813,8 @@ pub const DeclGen = struct { const error_size = Type.anyerror.abiAlignment(mod); const ty_size = ty.abiSize(mod); const padding = ty_size - payload_size - error_size; - const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); + + const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.undef; if (eu_layout.error_first) { try self.lower(Type.anyerror, error_val); @@ -1021,7 +1022,7 @@ pub const DeclGen = struct { return try self.constant(Type.anyerror, error_val, repr); } - const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); + const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.undef; var members: [2]IdRef = undefined; if (eu_layout.error_first) { @@ -1292,7 +1293,7 @@ pub const DeclGen = struct { var member_index: usize = 0; for (tuple.types, 0..) |field_ty, i| { const field_val = tuple.values[i]; - if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; + if (field_val.ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) continue; member_types[member_index] = try self.resolveType(field_ty, .indirect); member_index += 1; @@ -1596,7 +1597,7 @@ pub const DeclGen = struct { else decl.val; - if (init_val.tag() == .unreachable_value) { + if (init_val.ip_index == .unreachable_value) { return self.todo("importing extern variables", .{}); } diff --git a/src/type.zig b/src/type.zig index 1f970919c9..8cffddb31c 100644 --- a/src/type.zig +++ b/src/type.zig @@ -533,14 +533,14 @@ pub const Type = struct { for (a_tuple.values, 0..) |a_val, i| { const ty = a_tuple.types[i]; const b_val = b_tuple.values[i]; - if (a_val.tag() == .unreachable_value) { - if (b_val.tag() == .unreachable_value) { + if (a_val.ip_index == .unreachable_value) { + if (b_val.ip_index == .unreachable_value) { continue; } else { return false; } } else { - if (b_val.tag() == .unreachable_value) { + if (b_val.ip_index == .unreachable_value) { return false; } else { if (!Value.eql(a_val, b_val, ty, mod)) return false; @@ -569,14 +569,14 @@ pub const Type = struct { for (a_struct_obj.values, 0..) |a_val, i| { const ty = a_struct_obj.types[i]; const b_val = b_struct_obj.values[i]; - if (a_val.tag() == .unreachable_value) { - if (b_val.tag() == .unreachable_value) { + if (a_val.ip_index == .unreachable_value) { + if (b_val.ip_index == .unreachable_value) { continue; } else { return false; } } else { - if (b_val.tag() == .unreachable_value) { + if (b_val.ip_index == .unreachable_value) { return false; } else { if (!Value.eql(a_val, b_val, ty, mod)) return false; @@ -750,7 +750,7 @@ pub const Type = struct { for (tuple.types, 0..) |field_ty, i| { hashWithHasher(field_ty, hasher, mod); const field_val = tuple.values[i]; - if (field_val.tag() == .unreachable_value) continue; + if (field_val.ip_index == .unreachable_value) continue; field_val.hash(field_ty, hasher, mod); } }, @@ -764,7 +764,7 @@ pub const Type = struct { const field_val = struct_obj.values[i]; hasher.update(field_name); hashWithHasher(field_ty, hasher, mod); - if (field_val.tag() == .unreachable_value) continue; + if (field_val.ip_index == .unreachable_value) continue; field_val.hash(field_ty, hasher, mod); } }, @@ -1139,11 +1139,11 @@ pub const Type = struct { for (tuple.types, 0..) |field_ty, i| { if (i != 0) try writer.writeAll(", "); const val = tuple.values[i]; - if (val.tag() != .unreachable_value) { + if (val.ip_index != .unreachable_value) { try writer.writeAll("comptime "); } try field_ty.dump("", .{}, writer); - if (val.tag() != .unreachable_value) { + if (val.ip_index != .unreachable_value) { try writer.print(" = {}", .{val.fmtDebug()}); } } @@ -1156,13 +1156,13 @@ pub const Type = struct { for (anon_struct.types, 0..) |field_ty, i| { if (i != 0) try writer.writeAll(", "); const val = anon_struct.values[i]; - if (val.tag() != .unreachable_value) { + if (val.ip_index != .unreachable_value) { try writer.writeAll("comptime "); } try writer.writeAll(anon_struct.names[i]); try writer.writeAll(": "); try field_ty.dump("", .{}, writer); - if (val.tag() != .unreachable_value) { + if (val.ip_index != .unreachable_value) { try writer.print(" = {}", .{val.fmtDebug()}); } } @@ -1408,11 +1408,11 @@ pub const Type = struct { for (tuple.types, 0..) |field_ty, i| { if (i != 0) try writer.writeAll(", "); const val = tuple.values[i]; - if (val.tag() != .unreachable_value) { + if (val.ip_index != .unreachable_value) { try writer.writeAll("comptime "); } try print(field_ty, writer, mod); - if (val.tag() != .unreachable_value) { + if (val.ip_index != .unreachable_value) { try writer.print(" = {}", .{val.fmtValue(field_ty, mod)}); } } @@ -1425,7 +1425,7 @@ pub const Type = struct { for (anon_struct.types, 0..) |field_ty, i| { if (i != 0) try writer.writeAll(", "); const val = anon_struct.values[i]; - if (val.tag() != .unreachable_value) { + if (val.ip_index != .unreachable_value) { try writer.writeAll("comptime "); } try writer.writeAll(anon_struct.names[i]); @@ -1433,7 +1433,7 @@ pub const Type = struct { try print(field_ty, writer, mod); - if (val.tag() != .unreachable_value) { + if (val.ip_index != .unreachable_value) { try writer.print(" = {}", .{val.fmtValue(field_ty, mod)}); } } @@ -1770,7 +1770,7 @@ pub const Type = struct { const tuple = ty.tupleFields(); for (tuple.types, 0..) |field_ty, i| { const val = tuple.values[i]; - if (val.tag() != .unreachable_value) continue; // comptime field + if (val.ip_index != .unreachable_value) continue; // comptime field if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) return true; } return false; @@ -2283,7 +2283,7 @@ pub const Type = struct { var big_align: u32 = 0; for (tuple.types, 0..) |field_ty, i| { const val = tuple.values[i]; - if (val.tag() != .unreachable_value) continue; // comptime field + if (val.ip_index != .unreachable_value) continue; // comptime field if (!(field_ty.hasRuntimeBits(mod))) continue; switch (try field_ty.abiAlignmentAdvanced(mod, strat)) { @@ -3845,7 +3845,7 @@ pub const Type = struct { => return null, .void => return Value.void, - .noreturn => return Value.initTag(.unreachable_value), + .noreturn => return Value.@"unreachable", .null => return Value.null, .undefined => return Value.undef, @@ -3896,7 +3896,7 @@ pub const Type = struct { .tuple, .anon_struct => { const tuple = ty.tupleFields(); for (tuple.values, 0..) |val, i| { - const is_comptime = val.tag() != .unreachable_value; + const is_comptime = val.ip_index != .unreachable_value; if (is_comptime) continue; if (tuple.types[i].onePossibleValue(mod) != null) continue; return null; @@ -3919,7 +3919,7 @@ pub const Type = struct { return null; } switch (enum_full.fields.count()) { - 0 => return Value.initTag(.unreachable_value), + 0 => return Value.@"unreachable", 1 => if (enum_full.values.count() == 0) { return Value.zero; // auto-numbered } else { @@ -3931,7 +3931,7 @@ pub const Type = struct { .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; switch (enum_simple.fields.count()) { - 0 => return Value.initTag(.unreachable_value), + 0 => return Value.@"unreachable", 1 => return Value.zero, else => return null, } @@ -3947,7 +3947,7 @@ pub const Type = struct { .@"union", .union_safety_tagged, .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; const tag_val = union_obj.tag_ty.onePossibleValue(mod) orelse return null; - if (union_obj.fields.count() == 0) return Value.initTag(.unreachable_value); + if (union_obj.fields.count() == 0) return Value.@"unreachable"; const only_field = union_obj.fields.values()[0]; const val_val = only_field.ty.onePossibleValue(mod) orelse return null; _ = tag_val; @@ -4075,7 +4075,7 @@ pub const Type = struct { .tuple, .anon_struct => { const tuple = ty.tupleFields(); for (tuple.types, 0..) |field_ty, i| { - const have_comptime_val = tuple.values[i].tag() != .unreachable_value; + const have_comptime_val = tuple.values[i].ip_index != .unreachable_value; if (!have_comptime_val and field_ty.comptimeOnly(mod)) return true; } return false; @@ -4514,7 +4514,7 @@ pub const Type = struct { .tuple => { const tuple = ty.castTag(.tuple).?.data; const val = tuple.values[index]; - if (val.tag() == .unreachable_value) { + if (val.ip_index == .unreachable_value) { return tuple.types[index].onePossibleValue(mod); } else { return val; @@ -4523,7 +4523,7 @@ pub const Type = struct { .anon_struct => { const anon_struct = ty.castTag(.anon_struct).?.data; const val = anon_struct.values[index]; - if (val.tag() == .unreachable_value) { + if (val.ip_index == .unreachable_value) { return anon_struct.types[index].onePossibleValue(mod); } else { return val; @@ -4544,12 +4544,12 @@ pub const Type = struct { .tuple => { const tuple = ty.castTag(.tuple).?.data; const val = tuple.values[index]; - return val.tag() != .unreachable_value; + return val.ip_index != .unreachable_value; }, .anon_struct => { const anon_struct = ty.castTag(.anon_struct).?.data; const val = anon_struct.values[index]; - return val.tag() != .unreachable_value; + return val.ip_index != .unreachable_value; }, else => unreachable, } @@ -4647,7 +4647,7 @@ pub const Type = struct { for (tuple.types, 0..) |field_ty, i| { const field_val = tuple.values[i]; - if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits(mod)) { + if (field_val.ip_index != .unreachable_value or !field_ty.hasRuntimeBits(mod)) { // comptime field if (i == index) return offset; continue; diff --git a/src/value.zig b/src/value.zig index 6f7210c884..f1d706aa09 100644 --- a/src/value.zig +++ b/src/value.zig @@ -33,13 +33,10 @@ pub const Value = struct { // Keep in sync with tools/stage2_pretty_printers_common.py pub const Tag = enum(usize) { // The first section of this enum are tags that require no payload. - undef, zero, one, - unreachable_value, /// The only possible value for a particular type, which is stored externally. the_only_possible_value, - null_value, empty_struct_value, empty_array, // See last_no_payload_tag below. @@ -132,14 +129,11 @@ pub const Value = struct { pub fn Type(comptime t: Tag) type { return switch (t) { - .undef, .zero, .one, - .unreachable_value, .the_only_possible_value, .empty_struct_value, .empty_array, - .null_value, => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"), .int_big_positive, @@ -287,13 +281,10 @@ pub const Value = struct { .legacy = .{ .tag_if_small_enough = self.legacy.tag_if_small_enough }, }; } else switch (self.legacy.ptr_otherwise.tag) { - .undef, .zero, .one, - .unreachable_value, .the_only_possible_value, .empty_array, - .null_value, .empty_struct_value, => unreachable, @@ -522,7 +513,7 @@ pub const Value = struct { ) !void { comptime assert(fmt.len == 0); if (start_val.ip_index != .none) { - try out_stream.print("(interned {d})", .{@enumToInt(start_val.ip_index)}); + try out_stream.print("(interned: {})", .{start_val.ip_index}); return; } var val = start_val; @@ -534,11 +525,8 @@ pub const Value = struct { .@"union" => { return out_stream.writeAll("(union value)"); }, - .null_value => return out_stream.writeAll("null"), - .undef => return out_stream.writeAll("undefined"), .zero => return out_stream.writeAll("0"), .one => return out_stream.writeAll("1"), - .unreachable_value => return out_stream.writeAll("unreachable"), .the_only_possible_value => return out_stream.writeAll("(the only possible value)"), .ty => return val.castTag(.ty).?.data.dump("", options, out_stream), .lazy_align => { @@ -811,8 +799,9 @@ pub const Value = struct { switch (val.ip_index) { .bool_false => return BigIntMutable.init(&space.limbs, 0).toConst(), .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(), + .undef => unreachable, + .null_value => return BigIntMutable.init(&space.limbs, 0).toConst(), .none => switch (val.tag()) { - .null_value, .zero, .the_only_possible_value, // i0, u0 => return BigIntMutable.init(&space.limbs, 0).toConst(), @@ -832,8 +821,6 @@ pub const Value = struct { .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt(), .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt(), - .undef => unreachable, - .lazy_align => { const ty = val.castTag(.lazy_align).?.data; if (opt_sema) |sema| { @@ -880,6 +867,7 @@ pub const Value = struct { switch (val.ip_index) { .bool_false => return 0, .bool_true => return 1, + .undef => unreachable, .none => switch (val.tag()) { .zero, .the_only_possible_value, // i0, u0 @@ -892,8 +880,6 @@ pub const Value = struct { .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(u64) catch null, .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(u64) catch null, - .undef => unreachable, - .lazy_align => { const ty = val.castTag(.lazy_align).?.data; if (opt_sema) |sema| { @@ -913,9 +899,9 @@ pub const Value = struct { else => return null, }, - else => switch (mod.intern_pool.indexToKey(val.ip_index)) { - .int => |int| return int.big_int.to(u64) catch null, - else => unreachable, + else => return switch (mod.intern_pool.indexToKey(val.ip_index)) { + .int => |int| int.big_int.to(u64) catch null, + else => null, }, } } @@ -930,6 +916,7 @@ pub const Value = struct { switch (val.ip_index) { .bool_false => return 0, .bool_true => return 1, + .undef => unreachable, .none => switch (val.tag()) { .zero, .the_only_possible_value, // i0, u0 @@ -951,7 +938,6 @@ pub const Value = struct { return @intCast(i64, ty.abiSize(mod)); }, - .undef => unreachable, else => unreachable, }, else => switch (mod.intern_pool.indexToKey(val.ip_index)) { @@ -2032,8 +2018,7 @@ pub const Value = struct { const a_tag = a.tag(); const b_tag = b.tag(); if (a_tag == b_tag) switch (a_tag) { - .undef => return true, - .null_value, .the_only_possible_value, .empty_struct_value => return true, + .the_only_possible_value, .empty_struct_value => return true, .enum_literal => { const a_name = a.castTag(.enum_literal).?.data; const b_name = b.castTag(.enum_literal).?.data; @@ -2162,9 +2147,7 @@ pub const Value = struct { return eqlAdvanced(a_union.val, active_field_ty, b_union.val, active_field_ty, mod, opt_sema); }, else => {}, - } else if (b_tag == .null_value or b_tag == .@"error") { - return false; - } else if (a_tag == .undef or b_tag == .undef) { + } else if (b_tag == .@"error") { return false; } @@ -2283,7 +2266,7 @@ pub const Value = struct { if (a_nan) return true; return a_float == b_float; }, - .Optional => if (a_tag != .null_value and b_tag == .opt_payload) { + .Optional => if (b_tag == .opt_payload) { var sub_pl: Payload.SubValue = .{ .base = .{ .tag = b.tag() }, .data = a, @@ -2301,7 +2284,7 @@ pub const Value = struct { }, else => {}, } - if (a_tag == .null_value or a_tag == .@"error") return false; + if (a_tag == .@"error") return false; return (try orderAdvanced(a, b, mod, opt_sema)).compare(.eq); } @@ -2642,7 +2625,6 @@ pub const Value = struct { .zero, .one, - .null_value, .int_u64, .int_i64, .int_big_positive, @@ -2717,102 +2699,108 @@ pub const Value = struct { arena: ?Allocator, buffer: *ElemValueBuffer, ) error{OutOfMemory}!Value { - switch (val.tag()) { - // This is the case of accessing an element of an undef array. + switch (val.ip_index) { .undef => return Value.undef, - .empty_array => unreachable, // out of bounds array index - .empty_struct_value => unreachable, // out of bounds array index + .none => switch (val.tag()) { + // This is the case of accessing an element of an undef array. + .empty_array => unreachable, // out of bounds array index + .empty_struct_value => unreachable, // out of bounds array index - .empty_array_sentinel => { - assert(index == 0); // The only valid index for an empty array with sentinel. - return val.castTag(.empty_array_sentinel).?.data; + .empty_array_sentinel => { + assert(index == 0); // The only valid index for an empty array with sentinel. + return val.castTag(.empty_array_sentinel).?.data; + }, + + .bytes => { + const byte = val.castTag(.bytes).?.data[index]; + if (arena) |a| { + return Tag.int_u64.create(a, byte); + } else { + buffer.* = .{ + .base = .{ .tag = .int_u64 }, + .data = byte, + }; + return initPayload(&buffer.base); + } + }, + .str_lit => { + const str_lit = val.castTag(.str_lit).?.data; + const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + const byte = bytes[index]; + if (arena) |a| { + return Tag.int_u64.create(a, byte); + } else { + buffer.* = .{ + .base = .{ .tag = .int_u64 }, + .data = byte, + }; + return initPayload(&buffer.base); + } + }, + + // No matter the index; all the elements are the same! + .repeated => return val.castTag(.repeated).?.data, + + .aggregate => return val.castTag(.aggregate).?.data[index], + .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(mod, index, arena, buffer), + + .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer), + .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValueAdvanced(mod, index, arena, buffer), + .comptime_field_ptr => return val.castTag(.comptime_field_ptr).?.data.field_val.elemValueAdvanced(mod, index, arena, buffer), + .elem_ptr => { + const data = val.castTag(.elem_ptr).?.data; + return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer); + }, + .field_ptr => { + const data = val.castTag(.field_ptr).?.data; + if (data.container_ptr.pointerDecl()) |decl_index| { + const container_decl = mod.declPtr(decl_index); + const field_type = data.container_ty.structFieldType(data.field_index); + const field_val = container_decl.val.fieldValue(field_type, mod, data.field_index); + return field_val.elemValueAdvanced(mod, index, arena, buffer); + } else unreachable; + }, + + // The child type of arrays which have only one possible value need + // to have only one possible value itself. + .the_only_possible_value => return val, + + .opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.container_ptr.elemValueAdvanced(mod, index, arena, buffer), + .eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.container_ptr.elemValueAdvanced(mod, index, arena, buffer), + + .opt_payload => return val.castTag(.opt_payload).?.data.elemValueAdvanced(mod, index, arena, buffer), + .eu_payload => return val.castTag(.eu_payload).?.data.elemValueAdvanced(mod, index, arena, buffer), + + else => unreachable, }, - - .bytes => { - const byte = val.castTag(.bytes).?.data[index]; - if (arena) |a| { - return Tag.int_u64.create(a, byte); - } else { - buffer.* = .{ - .base = .{ .tag = .int_u64 }, - .data = byte, - }; - return initPayload(&buffer.base); - } - }, - .str_lit => { - const str_lit = val.castTag(.str_lit).?.data; - const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; - const byte = bytes[index]; - if (arena) |a| { - return Tag.int_u64.create(a, byte); - } else { - buffer.* = .{ - .base = .{ .tag = .int_u64 }, - .data = byte, - }; - return initPayload(&buffer.base); - } - }, - - // No matter the index; all the elements are the same! - .repeated => return val.castTag(.repeated).?.data, - - .aggregate => return val.castTag(.aggregate).?.data[index], - .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(mod, index, arena, buffer), - - .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer), - .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValueAdvanced(mod, index, arena, buffer), - .comptime_field_ptr => return val.castTag(.comptime_field_ptr).?.data.field_val.elemValueAdvanced(mod, index, arena, buffer), - .elem_ptr => { - const data = val.castTag(.elem_ptr).?.data; - return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer); - }, - .field_ptr => { - const data = val.castTag(.field_ptr).?.data; - if (data.container_ptr.pointerDecl()) |decl_index| { - const container_decl = mod.declPtr(decl_index); - const field_type = data.container_ty.structFieldType(data.field_index); - const field_val = container_decl.val.fieldValue(field_type, mod, data.field_index); - return field_val.elemValueAdvanced(mod, index, arena, buffer); - } else unreachable; - }, - - // The child type of arrays which have only one possible value need - // to have only one possible value itself. - .the_only_possible_value => return val, - - .opt_payload_ptr => return val.castTag(.opt_payload_ptr).?.data.container_ptr.elemValueAdvanced(mod, index, arena, buffer), - .eu_payload_ptr => return val.castTag(.eu_payload_ptr).?.data.container_ptr.elemValueAdvanced(mod, index, arena, buffer), - - .opt_payload => return val.castTag(.opt_payload).?.data.elemValueAdvanced(mod, index, arena, buffer), - .eu_payload => return val.castTag(.eu_payload).?.data.elemValueAdvanced(mod, index, arena, buffer), - else => unreachable, } } /// Returns true if a Value is backed by a variable pub fn isVariable(val: Value, mod: *Module) bool { - return switch (val.tag()) { - .slice => val.castTag(.slice).?.data.ptr.isVariable(mod), - .comptime_field_ptr => val.castTag(.comptime_field_ptr).?.data.field_val.isVariable(mod), - .elem_ptr => val.castTag(.elem_ptr).?.data.array_ptr.isVariable(mod), - .field_ptr => val.castTag(.field_ptr).?.data.container_ptr.isVariable(mod), - .eu_payload_ptr => val.castTag(.eu_payload_ptr).?.data.container_ptr.isVariable(mod), - .opt_payload_ptr => val.castTag(.opt_payload_ptr).?.data.container_ptr.isVariable(mod), - .decl_ref => { - const decl = mod.declPtr(val.castTag(.decl_ref).?.data); - assert(decl.has_tv); - return decl.val.isVariable(mod); - }, - .decl_ref_mut => { - const decl = mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index); - assert(decl.has_tv); - return decl.val.isVariable(mod); - }, + return switch (val.ip_index) { + .none => switch (val.tag()) { + .slice => val.castTag(.slice).?.data.ptr.isVariable(mod), + .comptime_field_ptr => val.castTag(.comptime_field_ptr).?.data.field_val.isVariable(mod), + .elem_ptr => val.castTag(.elem_ptr).?.data.array_ptr.isVariable(mod), + .field_ptr => val.castTag(.field_ptr).?.data.container_ptr.isVariable(mod), + .eu_payload_ptr => val.castTag(.eu_payload_ptr).?.data.container_ptr.isVariable(mod), + .opt_payload_ptr => val.castTag(.opt_payload_ptr).?.data.container_ptr.isVariable(mod), + .decl_ref => { + const decl = mod.declPtr(val.castTag(.decl_ref).?.data); + assert(decl.has_tv); + return decl.val.isVariable(mod); + }, + .decl_ref_mut => { + const decl = mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index); + assert(decl.has_tv); + return decl.val.isVariable(mod); + }, - .variable => true, + .variable => true, + else => false, + }, else => false, }; } @@ -2878,39 +2866,46 @@ pub const Value = struct { } pub fn fieldValue(val: Value, ty: Type, mod: *const Module, index: usize) Value { - switch (val.tag()) { - .aggregate => { - const field_values = val.castTag(.aggregate).?.data; - return field_values[index]; - }, - .@"union" => { - const payload = val.castTag(.@"union").?.data; - // TODO assert the tag is correct - return payload.val; - }, - - .the_only_possible_value => return ty.onePossibleValue(mod).?, - - .empty_struct_value => { - if (ty.isSimpleTupleOrAnonStruct()) { - const tuple = ty.tupleFields(); - return tuple.values[index]; - } - if (ty.structFieldValueComptime(mod, index)) |some| { - return some; - } - unreachable; - }, + switch (val.ip_index) { .undef => return Value.undef, + .none => switch (val.tag()) { + .aggregate => { + const field_values = val.castTag(.aggregate).?.data; + return field_values[index]; + }, + .@"union" => { + const payload = val.castTag(.@"union").?.data; + // TODO assert the tag is correct + return payload.val; + }, + .the_only_possible_value => return ty.onePossibleValue(mod).?, + + .empty_struct_value => { + if (ty.isSimpleTupleOrAnonStruct()) { + const tuple = ty.tupleFields(); + return tuple.values[index]; + } + if (ty.structFieldValueComptime(mod, index)) |some| { + return some; + } + unreachable; + }, + + else => unreachable, + }, else => unreachable, } } pub fn unionTag(val: Value) Value { - switch (val.tag()) { - .undef, .enum_field_index => return val, - .@"union" => return val.castTag(.@"union").?.data.tag, + switch (val.ip_index) { + .undef => return val, + .none => switch (val.tag()) { + .enum_field_index => return val, + .@"union" => return val.castTag(.@"union").?.data.tag, + else => unreachable, + }, else => unreachable, } } @@ -2946,15 +2941,15 @@ pub const Value = struct { }); } - pub fn isUndef(self: Value) bool { - return self.tag() == .undef; + pub fn isUndef(val: Value) bool { + return val.ip_index == .undef; } /// TODO: check for cases such as array that is not marked undef but all the element /// values are marked undef, or struct that is not marked undef but all fields are marked /// undef, etc. - pub fn isUndefDeep(self: Value) bool { - return self.isUndef(); + pub fn isUndefDeep(val: Value) bool { + return val.isUndef(); } /// Returns true if any value contained in `self` is undefined. @@ -2962,27 +2957,29 @@ pub const Value = struct { /// values are marked undef, or struct that is not marked undef but all fields are marked /// undef, etc. pub fn anyUndef(self: Value, mod: *Module) bool { - switch (self.tag()) { - .slice => { - const payload = self.castTag(.slice).?; - const len = payload.data.len.toUnsignedInt(mod); - - var elem_value_buf: ElemValueBuffer = undefined; - var i: usize = 0; - while (i < len) : (i += 1) { - const elem_val = payload.data.ptr.elemValueBuffer(mod, i, &elem_value_buf); - if (elem_val.anyUndef(mod)) return true; - } - }, - - .aggregate => { - const payload = self.castTag(.aggregate).?; - for (payload.data) |val| { - if (val.anyUndef(mod)) return true; - } - }, - + switch (self.ip_index) { .undef => return true, + .none => switch (self.tag()) { + .slice => { + const payload = self.castTag(.slice).?; + const len = payload.data.len.toUnsignedInt(mod); + + var elem_value_buf: ElemValueBuffer = undefined; + var i: usize = 0; + while (i < len) : (i += 1) { + const elem_val = payload.data.ptr.elemValueBuffer(mod, i, &elem_value_buf); + if (elem_val.anyUndef(mod)) return true; + } + }, + + .aggregate => { + const payload = self.castTag(.aggregate).?; + for (payload.data) |val| { + if (val.anyUndef(mod)) return true; + } + }, + else => {}, + }, else => {}, } @@ -2992,30 +2989,33 @@ pub const Value = struct { /// Asserts the value is not undefined and not unreachable. /// Integer value 0 is considered null because of C pointers. pub fn isNull(self: Value, mod: *const Module) bool { - return switch (self.tag()) { - .null_value => true, - .opt_payload => false, - - // If it's not one of those two tags then it must be a C pointer value, - // in which case the value 0 is null and other values are non-null. - - .zero, - .the_only_possible_value, - => true, - - .one => false, - - .int_u64, - .int_i64, - .int_big_positive, - .int_big_negative, - => self.orderAgainstZero(mod).compare(.eq), - + return switch (self.ip_index) { .undef => unreachable, .unreachable_value => unreachable, - .inferred_alloc => unreachable, - .inferred_alloc_comptime => unreachable, + .null_value => true, + .none => switch (self.tag()) { + .opt_payload => false, + // If it's not one of those two tags then it must be a C pointer value, + // in which case the value 0 is null and other values are non-null. + + .zero, + .the_only_possible_value, + => true, + + .one => false, + + .int_u64, + .int_i64, + .int_big_positive, + .int_big_negative, + => self.orderAgainstZero(mod).compare(.eq), + + .inferred_alloc => unreachable, + .inferred_alloc_comptime => unreachable, + + else => false, + }, else => false, }; } @@ -3025,18 +3025,21 @@ pub const Value = struct { /// something is an error or not because it works without having to figure out the /// string. pub fn getError(self: Value) ?[]const u8 { - return switch (self.tag()) { - .@"error" => self.castTag(.@"error").?.data.name, - .int_u64 => @panic("TODO"), - .int_i64 => @panic("TODO"), - .int_big_positive => @panic("TODO"), - .int_big_negative => @panic("TODO"), - .one => @panic("TODO"), + return switch (self.ip_index) { .undef => unreachable, .unreachable_value => unreachable, - .inferred_alloc => unreachable, - .inferred_alloc_comptime => unreachable, + .none => switch (self.tag()) { + .@"error" => self.castTag(.@"error").?.data.name, + .int_u64 => @panic("TODO"), + .int_i64 => @panic("TODO"), + .int_big_positive => @panic("TODO"), + .int_big_negative => @panic("TODO"), + .one => @panic("TODO"), + .inferred_alloc => unreachable, + .inferred_alloc_comptime => unreachable, + else => null, + }, else => null, }; } @@ -3044,13 +3047,16 @@ pub const Value = struct { /// Assumes the type is an error union. Returns true if and only if the value is /// the error union payload, not an error. pub fn errorUnionIsPayload(val: Value) bool { - return switch (val.tag()) { - .eu_payload => true, - else => false, - + return switch (val.ip_index) { .undef => unreachable, - .inferred_alloc => unreachable, - .inferred_alloc_comptime => unreachable, + .none => switch (val.tag()) { + .eu_payload => true, + else => false, + + .inferred_alloc => unreachable, + .inferred_alloc_comptime => unreachable, + }, + else => false, }; } @@ -3065,17 +3071,20 @@ pub const Value = struct { /// Valid for all types. Asserts the value is not undefined. pub fn isFloat(self: Value) bool { - return switch (self.tag()) { + return switch (self.ip_index) { .undef => unreachable, - .inferred_alloc => unreachable, - .inferred_alloc_comptime => unreachable, + .none => switch (self.tag()) { + .inferred_alloc => unreachable, + .inferred_alloc_comptime => unreachable, - .float_16, - .float_32, - .float_64, - .float_80, - .float_128, - => true, + .float_16, + .float_32, + .float_64, + .float_80, + .float_128, + => true, + else => false, + }, else => false, }; } @@ -3102,40 +3111,44 @@ pub const Value = struct { pub fn intToFloatScalar(val: Value, arena: Allocator, float_ty: Type, mod: *Module, opt_sema: ?*Sema) !Value { const target = mod.getTarget(); - switch (val.tag()) { - .undef, .zero, .one => return val, - .the_only_possible_value => return Value.initTag(.zero), // for i0, u0 - .int_u64 => { - return intToFloatInner(val.castTag(.int_u64).?.data, arena, float_ty, target); - }, - .int_i64 => { - return intToFloatInner(val.castTag(.int_i64).?.data, arena, float_ty, target); - }, - .int_big_positive => { - const limbs = val.castTag(.int_big_positive).?.data; - const float = bigIntToFloat(limbs, true); - return floatToValue(float, arena, float_ty, target); - }, - .int_big_negative => { - const limbs = val.castTag(.int_big_negative).?.data; - const float = bigIntToFloat(limbs, false); - return floatToValue(float, arena, float_ty, target); - }, - .lazy_align => { - const ty = val.castTag(.lazy_align).?.data; - if (opt_sema) |sema| { - return intToFloatInner((try ty.abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar, arena, float_ty, target); - } else { - return intToFloatInner(ty.abiAlignment(mod), arena, float_ty, target); - } - }, - .lazy_size => { - const ty = val.castTag(.lazy_size).?.data; - if (opt_sema) |sema| { - return intToFloatInner((try ty.abiSizeAdvanced(mod, .{ .sema = sema })).scalar, arena, float_ty, target); - } else { - return intToFloatInner(ty.abiSize(mod), arena, float_ty, target); - } + switch (val.ip_index) { + .undef => return val, + .none => switch (val.tag()) { + .zero, .one => return val, + .the_only_possible_value => return Value.initTag(.zero), // for i0, u0 + .int_u64 => { + return intToFloatInner(val.castTag(.int_u64).?.data, arena, float_ty, target); + }, + .int_i64 => { + return intToFloatInner(val.castTag(.int_i64).?.data, arena, float_ty, target); + }, + .int_big_positive => { + const limbs = val.castTag(.int_big_positive).?.data; + const float = bigIntToFloat(limbs, true); + return floatToValue(float, arena, float_ty, target); + }, + .int_big_negative => { + const limbs = val.castTag(.int_big_negative).?.data; + const float = bigIntToFloat(limbs, false); + return floatToValue(float, arena, float_ty, target); + }, + .lazy_align => { + const ty = val.castTag(.lazy_align).?.data; + if (opt_sema) |sema| { + return intToFloatInner((try ty.abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar, arena, float_ty, target); + } else { + return intToFloatInner(ty.abiAlignment(mod), arena, float_ty, target); + } + }, + .lazy_size => { + const ty = val.castTag(.lazy_size).?.data; + if (opt_sema) |sema| { + return intToFloatInner((try ty.abiSizeAdvanced(mod, .{ .sema = sema })).scalar, arena, float_ty, target); + } else { + return intToFloatInner(ty.abiSize(mod), arena, float_ty, target); + } + }, + else => unreachable, }, else => unreachable, } @@ -3381,7 +3394,7 @@ pub const Value = struct { arena: Allocator, mod: *Module, ) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + if (lhs.isUndef() or rhs.isUndef()) return Value.undef; if (ty.zigTypeTag(mod) == .ComptimeInt) { return intMul(lhs, rhs, ty, arena, mod); @@ -3492,7 +3505,7 @@ pub const Value = struct { /// operands must be integers; handles undefined. pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value { - if (val.isUndef()) return Value.initTag(.undef); + if (val.isUndef()) return Value.undef; const info = ty.intInfo(mod); @@ -3532,7 +3545,7 @@ pub const Value = struct { /// operands must be integers; handles undefined. pub fn bitwiseAndScalar(lhs: Value, rhs: Value, arena: Allocator, mod: *Module) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + if (lhs.isUndef() or rhs.isUndef()) return Value.undef; // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. @@ -3568,7 +3581,7 @@ pub const Value = struct { /// operands must be integers; handles undefined. pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + if (lhs.isUndef() or rhs.isUndef()) return Value.undef; const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod); @@ -3598,7 +3611,7 @@ pub const Value = struct { /// operands must be integers; handles undefined. pub fn bitwiseOrScalar(lhs: Value, rhs: Value, arena: Allocator, mod: *Module) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + if (lhs.isUndef() or rhs.isUndef()) return Value.undef; // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. @@ -3633,7 +3646,7 @@ pub const Value = struct { /// operands must be integers; handles undefined. pub fn bitwiseXorScalar(lhs: Value, rhs: Value, arena: Allocator, mod: *Module) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + if (lhs.isUndef() or rhs.isUndef()) return Value.undef; // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. @@ -5393,11 +5406,12 @@ pub const Value = struct { .ip_index = .none, .legacy = .{ .ptr_otherwise = &negative_one_payload.base }, }; - pub const undef = initTag(.undef); + pub const undef: Value = .{ .ip_index = .undef, .legacy = undefined }; pub const @"void": Value = .{ .ip_index = .void_value, .legacy = undefined }; - pub const @"null" = initTag(.null_value); + pub const @"null": Value = .{ .ip_index = .null_value, .legacy = undefined }; pub const @"false": Value = .{ .ip_index = .bool_false, .legacy = undefined }; pub const @"true": Value = .{ .ip_index = .bool_true, .legacy = undefined }; + pub const @"unreachable": Value = .{ .ip_index = .unreachable_value, .legacy = undefined }; pub const generic_poison: Value = .{ .ip_index = .generic_poison, .legacy = undefined }; pub const generic_poison_type: Value = .{ .ip_index = .generic_poison_type, .legacy = undefined };