diff --git a/src/Sema.zig b/src/Sema.zig index 0d387faacf..c0b78c3e1d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1881,10 +1881,10 @@ pub fn toConstString( air_inst: Air.Inst.Ref, reason: NeededComptimeReason, ) ![]u8 { - const wanted_type = Type.slice_const_u8; - const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); - const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); - return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod); + const coerced_inst = try sema.coerce(block, Type.slice_const_u8, air_inst, src); + const slice_val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); + const arr_val = try sema.derefSliceAsArray(block, src, slice_val, reason); + return arr_val.toAllocatedBytes(arr_val.typeOf(sema.mod), sema.arena, sema.mod); } pub fn resolveConstStringIntern( @@ -14498,12 +14498,16 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai else => unreachable, }) |rhs_val| { const lhs_sub_val = if (lhs_ty.isSinglePointer(mod)) - (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? + try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :rs lhs_src + else if (lhs_ty.isSlice(mod)) + try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :rs lhs_src else lhs_val; const rhs_sub_val = if (rhs_ty.isSinglePointer(mod)) - (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? + try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty) orelse break :rs rhs_src + else if (rhs_ty.isSlice(mod)) + try sema.maybeDerefSliceAsArray(block, rhs_src, rhs_val) orelse break :rs rhs_src else rhs_val; @@ -14623,10 +14627,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins .Pointer => { const ptr_info = operand_ty.ptrInfo(mod); switch (ptr_info.flags.size) { - // TODO: in the Many case here this should only work if the type - // has a sentinel, and this code should compute the length based - // on the sentinel value. - .Slice, .Many => { + .Slice => { const val = try sema.resolveConstDefinedValue(block, src, operand, .{ .needed_comptime_reason = "slice value being concatenated must be comptime-known", }); @@ -14636,7 +14637,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins .none => null, else => Value.fromInterned(ptr_info.sentinel), }, - .len = val.sliceLen(mod), + .len = try val.sliceLen(sema), }; }, .One => { @@ -14644,7 +14645,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins return Type.fromInterned(ptr_info.child).arrayInfo(mod); } }, - .C => {}, + .C, .Many => {}, } }, .Struct => { @@ -14830,9 +14831,11 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const ptr_addrspace = if (lhs_ty.zigTypeTag(mod) == .Pointer) lhs_ty.ptrAddressSpace(mod) else null; const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); - if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { + if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| ct: { const lhs_sub_val = if (lhs_ty.isSinglePointer(mod)) - (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? + try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :ct + else if (lhs_ty.isSlice(mod)) + try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :ct else lhs_val; @@ -14840,7 +14843,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // Optimization for the common pattern of a single element repeated N times, such // as zero-filling a byte array. if (lhs_len == 1 and lhs_info.sentinel == null) { - const elem_val = (try lhs_sub_val.maybeElemValueFull(sema, mod, 0)).?; + const elem_val = try lhs_sub_val.elemValue(mod, 0); break :v try mod.intern(.{ .aggregate = .{ .ty = result_ty.toIntern(), .storage = .{ .repeated_elem = elem_val.toIntern() }, @@ -14852,7 +14855,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai while (elem_i < result_len) { var lhs_i: usize = 0; while (lhs_i < lhs_len) : (lhs_i += 1) { - const elem_val = (try lhs_sub_val.maybeElemValueFull(sema, mod, lhs_i)).?; + const elem_val = try lhs_sub_val.elemValue(mod, lhs_i); element_vals[elem_i] = elem_val.toIntern(); elem_i += 1; } @@ -21124,7 +21127,9 @@ fn zirReify( .needed_comptime_reason = "operand to @Type must be comptime-known", }); const union_val = ip.indexToKey(val.toIntern()).un; - if (try sema.anyUndef(Value.fromInterned(union_val.val))) return sema.failWithUseOfUndef(block, src); + if (try sema.anyUndef(block, operand_src, Value.fromInterned(union_val.val))) { + return sema.failWithUseOfUndef(block, operand_src); + } const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), mod).?; switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { .Type => return .type_type, @@ -21365,11 +21370,15 @@ fn zirReify( const payload_val = Value.fromInterned(union_val.val).optionalValue(mod) orelse return Air.internedToRef(Type.anyerror.toIntern()); - const len = try sema.usizeCast(block, src, payload_val.sliceLen(mod)); + const names_val = try sema.derefSliceAsArray(block, src, payload_val, .{ + .needed_comptime_reason = "error set contents must be comptime-known", + }); + + const len = try sema.usizeCast(block, src, names_val.typeOf(mod).arrayLen(mod)); var names: InferredErrorSet.NameMap = .{}; try names.ensureUnusedCapacity(sema.arena, len); for (0..len) |i| { - const elem_val = (try payload_val.maybeElemValueFull(sema, mod, i)).?; + const elem_val = try names_val.elemValue(mod, i); const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( ip, @@ -21417,7 +21426,7 @@ fn zirReify( const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); // Decls - if (decls_val.sliceLen(mod) > 0) { + if (try decls_val.sliceLen(sema) > 0) { return sema.fail(block, src, "reified structs must have no decls", .{}); } @@ -21425,7 +21434,11 @@ fn zirReify( return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); } - return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool()); + const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ + .needed_comptime_reason = "struct fields must be comptime-known", + }); + + return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_arr, name_strategy, is_tuple_val.toBool()); }, .Enum => { const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); @@ -21446,11 +21459,15 @@ fn zirReify( try ip.getOrPutString(gpa, "is_exhaustive"), ).?); - if (decls_val.sliceLen(mod) > 0) { + if (try decls_val.sliceLen(sema) > 0) { return sema.fail(block, src, "reified enums must have no decls", .{}); } - return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_val, name_strategy); + const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ + .needed_comptime_reason = "enum fields must be comptime-known", + }); + + return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_arr, name_strategy); }, .Opaque => { const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); @@ -21460,7 +21477,7 @@ fn zirReify( ).?); // Decls - if (decls_val.sliceLen(mod) > 0) { + if (try decls_val.sliceLen(sema) > 0) { return sema.fail(block, src, "reified opaque must have no decls", .{}); } @@ -21505,12 +21522,16 @@ fn zirReify( try ip.getOrPutString(gpa, "decls"), ).?); - if (decls_val.sliceLen(mod) > 0) { + if (try decls_val.sliceLen(sema) > 0) { return sema.fail(block, src, "reified unions must have no decls", .{}); } const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); - return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_val, name_strategy); + const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ + .needed_comptime_reason = "union fields must be comptime-known", + }); + + return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_arr, name_strategy); }, .Fn => { const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); @@ -21530,7 +21551,7 @@ fn zirReify( ip, try ip.getOrPutString(gpa, "return_type"), ).?); - const params_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( + const params_slice_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "params"), ).?); @@ -21549,12 +21570,16 @@ fn zirReify( const return_type = return_type_val.optionalValue(mod) orelse return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); - const args_len = try sema.usizeCast(block, src, params_val.sliceLen(mod)); + const params_val = try sema.derefSliceAsArray(block, operand_src, params_slice_val, .{ + .needed_comptime_reason = "function parameters must be comptime-known", + }); + + const args_len = try sema.usizeCast(block, src, params_val.typeOf(mod).arrayLen(mod)); const param_types = try sema.arena.alloc(InternPool.Index, args_len); var noalias_bits: u32 = 0; for (param_types, 0..) |*param_type, i| { - const elem_val = (try params_val.maybeElemValueFull(sema, mod, i)).?; + const elem_val = try params_val.elemValue(mod, i); const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); const param_is_generic_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( ip, @@ -21615,7 +21640,7 @@ fn reifyEnum( // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`. - const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + const fields_len: u32 = @intCast(fields_val.typeOf(mod).arrayLen(mod)); // The validation work here is non-trivial, and it's possible the type already exists. // So in this first pass, let's just construct a hash to optimize for this case. If the @@ -21629,7 +21654,7 @@ fn reifyEnum( std.hash.autoHash(&hasher, fields_len); for (0..fields_len) |field_idx| { - const field_info = (try fields_val.maybeElemValueFull(sema, mod, field_idx)).?; + const field_info = try fields_val.elemValue(mod, field_idx); const field_name_val = try field_info.fieldValue(mod, 0); const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 1)); @@ -21674,7 +21699,7 @@ fn reifyEnum( wip_ty.setTagTy(ip, tag_ty.toIntern()); for (0..fields_len) |field_idx| { - const field_info = (try fields_val.maybeElemValueFull(sema, mod, field_idx)).?; + const field_info = try fields_val.elemValue(mod, field_idx); const field_name_val = try field_info.fieldValue(mod, 0); const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 1)); @@ -21736,7 +21761,7 @@ fn reifyUnion( // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`. - const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + const fields_len: u32 = @intCast(fields_val.typeOf(mod).arrayLen(mod)); // The validation work here is non-trivial, and it's possible the type already exists. // So in this first pass, let's just construct a hash to optimize for this case. If the @@ -21752,7 +21777,7 @@ fn reifyUnion( var any_aligns = false; for (0..fields_len) |field_idx| { - const field_info = (try fields_val.maybeElemValueFull(sema, mod, field_idx)).?; + const field_info = try fields_val.elemValue(mod, field_idx); const field_name_val = try field_info.fieldValue(mod, 0); const field_type_val = try field_info.fieldValue(mod, 1); @@ -21828,7 +21853,7 @@ fn reifyUnion( var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len); for (field_types, 0..) |*field_ty, field_idx| { - const field_info = (try fields_val.maybeElemValueFull(sema, mod, field_idx)).?; + const field_info = try fields_val.elemValue(mod, field_idx); const field_name_val = try field_info.fieldValue(mod, 0); const field_type_val = try field_info.fieldValue(mod, 1); @@ -21880,7 +21905,7 @@ fn reifyUnion( try field_names.ensureTotalCapacity(sema.arena, fields_len); for (field_types, 0..) |*field_ty, field_idx| { - const field_info = (try fields_val.maybeElemValueFull(sema, mod, field_idx)).?; + const field_info = try fields_val.elemValue(mod, field_idx); const field_name_val = try field_info.fieldValue(mod, 0); const field_type_val = try field_info.fieldValue(mod, 1); @@ -21974,7 +21999,7 @@ fn reifyStruct( // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`. - const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + const fields_len: u32 = @intCast(fields_val.typeOf(mod).arrayLen(mod)); // The validation work here is non-trivial, and it's possible the type already exists. // So in this first pass, let's just construct a hash to optimize for this case. If the @@ -21993,7 +22018,7 @@ fn reifyStruct( var any_aligned_fields = false; for (0..fields_len) |field_idx| { - const field_info = (try fields_val.maybeElemValueFull(sema, mod, field_idx)).?; + const field_info = try fields_val.elemValue(mod, field_idx); const field_name_val = try field_info.fieldValue(mod, 0); const field_type_val = try field_info.fieldValue(mod, 1); @@ -22071,7 +22096,7 @@ fn reifyStruct( const struct_type = ip.loadStructType(wip_ty.index); for (0..fields_len) |field_idx| { - const field_info = (try fields_val.maybeElemValueFull(sema, mod, field_idx)).?; + const field_info = try fields_val.elemValue(mod, field_idx); const field_name_val = try field_info.fieldValue(mod, 0); const field_type_val = try field_info.fieldValue(mod, 1); @@ -23892,11 +23917,9 @@ fn resolveExportOptions( const visibility_src = sema.maybeOptionsSrc(block, src, "visibility"); const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); - const name_val = try sema.resolveConstDefinedValue(block, name_src, name_operand, .{ + const name = try sema.toConstString(block, name_src, name_operand, .{ .needed_comptime_reason = "name of exported value must be comptime-known", }); - const name_ty = Type.slice_const_u8; - const name = try name_val.toAllocatedBytes(name_ty, sema.arena, mod); const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ @@ -23908,9 +23931,10 @@ fn resolveExportOptions( const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .needed_comptime_reason = "linksection of exported value must be comptime-known", }); - const section_ty = Type.slice_const_u8; const section = if (section_opt_val.optionalValue(mod)) |section_val| - try section_val.toAllocatedBytes(section_ty, sema.arena, mod) + try sema.toConstString(block, section_src, Air.internedToRef(section_val.toIntern()), .{ + .needed_comptime_reason = "linksection of exported value must be comptime-known", + }) else null; @@ -26028,10 +26052,9 @@ fn resolveExternOptions( const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local"); const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); - const name_val = try sema.resolveConstDefinedValue(block, name_src, name_ref, .{ + const name = try sema.toConstString(block, name_src, name_ref, .{ .needed_comptime_reason = "name of the extern symbol must be comptime-known", }); - const name = try name_val.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src); const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ @@ -26050,7 +26073,9 @@ fn resolveExternOptions( }); const library_name = if (library_name_val.optionalValue(mod)) |library_name_payload| library_name: { - const library_name = try library_name_payload.toAllocatedBytes(Type.slice_const_u8, sema.arena, mod); + const library_name = try sema.toConstString(block, library_src, Air.internedToRef(library_name_payload.toIntern()), .{ + .needed_comptime_reason = "library in which extern symbol is must be comptime-known", + }); if (library_name.len == 0) { return sema.fail(block, library_src, "library name cannot be empty", .{}); } @@ -28564,7 +28589,7 @@ fn elemValSlice( if (maybe_slice_val) |slice_val| { runtime_src = elem_index_src; - const slice_len = slice_val.sliceLen(mod); + const slice_len = try slice_val.sliceLen(sema); const slice_len_s = slice_len + @intFromBool(slice_sent); if (slice_len_s == 0) { return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); @@ -28589,7 +28614,7 @@ fn elemValSlice( try sema.requireRuntimeBlock(block, src, runtime_src); if (oob_safety and block.wantSafety()) { const len_inst = if (maybe_slice_val) |slice_val| - try mod.intRef(Type.usize, slice_val.sliceLen(mod)) + try mod.intRef(Type.usize, try slice_val.sliceLen(sema)) else try block.addTyOp(.slice_len, Type.usize, slice); const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -28626,7 +28651,7 @@ fn elemPtrSlice( if (slice_val.isUndef(mod)) { return mod.undefRef(elem_ptr_ty); } - const slice_len = slice_val.sliceLen(mod); + const slice_len = try slice_val.sliceLen(sema); const slice_len_s = slice_len + @intFromBool(slice_sent); if (slice_len_s == 0) { return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); @@ -28649,7 +28674,7 @@ fn elemPtrSlice( const len_inst = len: { if (maybe_undef_slice_val) |slice_val| if (!slice_val.isUndef(mod)) - break :len try mod.intRef(Type.usize, slice_val.sliceLen(mod)); + break :len try mod.intRef(Type.usize, try slice_val.sliceLen(sema)); break :len try block.addTyOp(.slice_len, Type.usize, slice); }; const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -31523,16 +31548,11 @@ fn coerceArrayPtrToSlice( if (try sema.resolveValue(inst)) |val| { const ptr_array_ty = sema.typeOf(inst); const array_ty = ptr_array_ty.childType(mod); + const slice_ptr_ty = dest_ty.slicePtrFieldType(mod); + const slice_ptr = try mod.getCoerced(val, slice_ptr_ty); const slice_val = try mod.intern(.{ .slice = .{ .ty = dest_ty.toIntern(), - .ptr = try mod.intern(.{ .ptr = .{ - .ty = dest_ty.slicePtrFieldType(mod).toIntern(), - .addr = switch (mod.intern_pool.indexToKey(val.toIntern())) { - .undef => .{ .int = try mod.intern(.{ .undef = .usize_type }) }, - .ptr => |ptr| ptr.addr, - else => unreachable, - }, - } }), + .ptr = slice_ptr.toIntern(), .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).toIntern(), } }); return Air.internedToRef(slice_val); @@ -32602,7 +32622,7 @@ fn analyzeSliceLen( if (slice_val.isUndef(mod)) { return mod.undefRef(Type.usize); } - return mod.intRef(Type.usize, slice_val.sliceLen(sema.mod)); + return mod.intRef(Type.usize, try slice_val.sliceLen(sema)); } try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.slice_len, Type.usize, slice_inst); @@ -33041,7 +33061,7 @@ fn analyzeSlice( return sema.fail(block, src, "slice of undefined", .{}); } const has_sentinel = slice_ty.sentinel(mod) != null; - const slice_len = slice_val.sliceLen(mod); + const slice_len = try slice_val.sliceLen(sema); const len_plus_sent = slice_len + @intFromBool(has_sentinel); const slice_len_val_with_sentinel = try mod.intValue(Type.usize, len_plus_sent); if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, Type.usize))) { @@ -33056,7 +33076,7 @@ fn analyzeSlice( "end index {} out of bounds for slice of length {d}{s}", .{ end_val.fmtValue(Type.usize, mod), - slice_val.sliceLen(mod), + try slice_val.sliceLen(sema), sentinel_label, }, ); @@ -33285,7 +33305,7 @@ fn analyzeSlice( if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { // we don't need to add one for sentinels because the // underlying value data includes the sentinel - break :blk try mod.intRef(Type.usize, slice_val.sliceLen(mod)); + break :blk try mod.intRef(Type.usize, try slice_val.sliceLen(sema)); } const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); @@ -39003,22 +39023,22 @@ fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Ai } /// Returns true if any value contained in `val` is undefined. -fn anyUndef(sema: *Sema, val: Value) !bool { +fn anyUndef(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) !bool { const mod = sema.mod; - return switch (val.toIntern()) { + return switch (mod.intern_pool.indexToKey(val.toIntern())) { .undef => true, - else => switch (mod.intern_pool.indexToKey(val.toIntern())) { - .undef => true, - .simple_value => |v| v == .undefined, - .slice => |slice| for (0..@intCast(Value.fromInterned(slice.len).toUnsignedInt(mod))) |idx| { - if (try sema.anyUndef((try val.maybeElemValueFull(sema, mod, idx)).?)) break true; - } else false, - .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| { - const elem = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i]; - if (try sema.anyUndef(Value.fromInterned(elem))) break true; - } else false, - else => false, + .simple_value => |v| v == .undefined, + .slice => { + // If the slice contents are runtime-known, reification will fail later on with a + // specific error message. + const arr = try sema.maybeDerefSliceAsArray(block, src, val) orelse return false; + return sema.anyUndef(block, src, arr); }, + .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| { + const elem = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i]; + if (try sema.anyUndef(block, src, Value.fromInterned(elem))) break true; + } else false, + else => false, }; } @@ -39050,6 +39070,20 @@ fn derefSliceAsArray( slice_val: Value, reason: NeededComptimeReason, ) CompileError!Value { + return try sema.maybeDerefSliceAsArray(block, src, slice_val) orelse { + return sema.failWithNeededComptime(block, src, reason); + }; +} + +/// Given a slice value, attempts to dereference it into a comptime-known array. +/// Returns `null` if the contents of the slice are not comptime-known. +/// Asserts that `slice_val` is a slice. +fn maybeDerefSliceAsArray( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + slice_val: Value, +) CompileError!?Value { const zcu = sema.mod; const ip = &zcu.intern_pool; assert(Type.fromInterned(ip.typeOf(slice_val.toIntern())).isSlice(zcu)); @@ -39072,7 +39106,5 @@ fn derefSliceAsArray( break :p p; }); const casted_ptr = try zcu.getCoerced(Value.fromInterned(slice.ptr), ptr_ty); - return try sema.pointerDeref(block, src, casted_ptr, ptr_ty) orelse { - return sema.failWithNeededComptime(block, src, reason); - }; + return sema.pointerDeref(block, src, casted_ptr, ptr_ty); } diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 4679ce20ee..ceef4116a2 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -1,7 +1,10 @@ const std = @import("std"); const Type = @import("type.zig").Type; const Value = @import("Value.zig"); -const Module = @import("Module.zig"); +const Zcu = @import("Module.zig"); +const Module = Zcu; +const Sema = @import("Sema.zig"); +const InternPool = @import("InternPool.zig"); const Allocator = std.mem.Allocator; const TypedValue = @This(); const Target = std.Target; @@ -61,8 +64,10 @@ pub fn format( ) !void { _ = options; comptime std.debug.assert(fmt.len == 0); - return ctx.tv.print(writer, 3, ctx.mod) catch |err| switch (err) { + return ctx.tv.print(writer, 3, ctx.mod, null) catch |err| switch (err) { error.OutOfMemory => @panic("OOM"), // We're not allowed to return this from a format function + error.ComptimeBreak, error.ComptimeReturn => unreachable, + error.AnalysisFail, error.NeededSourceLocation => unreachable, // TODO: re-evaluate when we actually pass `opt_sema` else => |e| return e, }; } @@ -73,11 +78,12 @@ pub fn print( writer: anytype, level: u8, mod: *Module, -) (@TypeOf(writer).Error || Allocator.Error)!void { - var val = tv.val; - var ty = tv.ty; + /// If this `Sema` is provided, we will recurse through pointers where possible to provide friendly output. + opt_sema: ?*Sema, +) (@TypeOf(writer).Error || Module.CompileError)!void { const ip = &mod.intern_pool; - while (true) switch (ip.indexToKey(val.toIntern())) { + const val = tv.val; + switch (ip.indexToKey(val.toIntern())) { .int_type, .ptr_type, .array_type, @@ -94,324 +100,298 @@ pub fn print( .func_type, .error_set_type, .inferred_error_set_type, - => return Type.print(val.toType(), writer, mod), - .undef => return writer.writeAll("undefined"), + => try Type.print(val.toType(), writer, mod), + .undef => try writer.writeAll("undefined"), .simple_value => |simple_value| switch (simple_value) { - .void => return writer.writeAll("{}"), - .empty_struct => return printAggregate(ty, val, writer, level, mod), - .generic_poison => return writer.writeAll("(generic poison)"), - else => return writer.writeAll(@tagName(simple_value)), + .void => try writer.writeAll("{}"), + .empty_struct => try writer.writeAll(".{}"), + .generic_poison => try writer.writeAll("(generic poison)"), + else => try writer.writeAll(@tagName(simple_value)), }, - .variable => return writer.writeAll("(variable)"), - .extern_func => |extern_func| return writer.print("(extern function '{}')", .{ + .variable => try writer.writeAll("(variable)"), + .extern_func => |extern_func| try writer.print("(extern function '{}')", .{ mod.declPtr(extern_func.decl).name.fmt(ip), }), - .func => |func| return writer.print("(function '{}')", .{ + .func => |func| try writer.print("(function '{}')", .{ mod.declPtr(func.owner_decl).name.fmt(ip), }), .int => |int| switch (int.storage) { - inline .u64, .i64, .big_int => |x| return writer.print("{}", .{x}), - .lazy_align => |lazy_ty| return writer.print("{d}", .{ - Type.fromInterned(lazy_ty).abiAlignment(mod), - }), - .lazy_size => |lazy_ty| return writer.print("{d}", .{ - Type.fromInterned(lazy_ty).abiSize(mod), - }), + inline .u64, .i64, .big_int => |x| try writer.print("{}", .{x}), + .lazy_align => |ty| if (opt_sema) |sema| { + const a = (try Type.fromInterned(ty).abiAlignmentAdvanced(mod, .{ .sema = sema })).scalar; + try writer.print("{}", .{a.toByteUnits(0)}); + } else try writer.print("@alignOf({})", .{Type.fromInterned(ty).fmt(mod)}), + .lazy_size => |ty| if (opt_sema) |sema| { + const s = (try Type.fromInterned(ty).abiSizeAdvanced(mod, .{ .sema = sema })).scalar; + try writer.print("{}", .{s}); + } else try writer.print("@sizeOf({})", .{Type.fromInterned(ty).fmt(mod)}), }, - .err => |err| return writer.print("error.{}", .{ + .err => |err| try writer.print("error.{}", .{ err.name.fmt(ip), }), .error_union => |error_union| switch (error_union.val) { - .err_name => |err_name| return writer.print("error.{}", .{ + .err_name => |err_name| try writer.print("error.{}", .{ err_name.fmt(ip), }), - .payload => |payload| { - val = Value.fromInterned(payload); - ty = ty.errorUnionPayload(mod); - }, + .payload => |payload| try print(.{ + .ty = tv.ty.errorUnionPayload(mod), + .val = Value.fromInterned(payload), + }, writer, level, mod, opt_sema), }, - .enum_literal => |enum_literal| return writer.print(".{}", .{ + .enum_literal => |enum_literal| try writer.print(".{}", .{ enum_literal.fmt(ip), }), .enum_tag => |enum_tag| { - if (level == 0) { - return writer.writeAll("(enum)"); - } - const enum_type = ip.loadEnumType(ty.toIntern()); + const enum_type = ip.loadEnumType(val.typeOf(mod).toIntern()); if (enum_type.tagValueIndex(ip, val.toIntern())) |tag_index| { try writer.print(".{i}", .{enum_type.names.get(ip)[tag_index].fmt(ip)}); return; } + if (level == 0) { + try writer.writeAll("@enumFromInt(...)"); + } try writer.writeAll("@enumFromInt("); try print(.{ .ty = Type.fromInterned(ip.typeOf(enum_tag.int)), .val = Value.fromInterned(enum_tag.int), - }, writer, level - 1, mod); + }, writer, level - 1, mod, opt_sema); try writer.writeAll(")"); - return; }, - .empty_enum_value => return writer.writeAll("(empty enum value)"), + .empty_enum_value => try writer.writeAll("(empty enum value)"), .float => |float| switch (float.storage) { - inline else => |x| return writer.print("{d}", .{@as(f64, @floatCast(x))}), + inline else => |x| try writer.print("{d}", .{@as(f64, @floatCast(x))}), }, .slice => |slice| { - const ptr_ty = switch (ip.indexToKey(slice.ptr)) { - .ptr => |ptr| ty: { - if (ptr.addr == .int) return print(.{ - .ty = Type.fromInterned(ptr.ty), - .val = Value.fromInterned(slice.ptr), - }, writer, level - 1, mod); - break :ty ip.indexToKey(ptr.ty).ptr_type; - }, - .undef => |ptr_ty| ip.indexToKey(ptr_ty).ptr_type, - else => unreachable, + const print_contents = switch (ip.getBackingAddrTag(slice.ptr).?) { + .field, .elem, .eu_payload, .opt_payload => unreachable, + .anon_decl, .comptime_alloc, .comptime_field => true, + .decl, .int => false, }; - if (level == 0) { - return writer.writeAll(".{ ... }"); + if (print_contents) { + // TODO: eventually we want to load the slice as an array with `opt_sema`, but that's + // currently not possible without e.g. triggering compile errors. } - const elem_ty = Type.fromInterned(ptr_ty.child); - const len = Value.fromInterned(slice.len).toUnsignedInt(mod); - if (elem_ty.eql(Type.u8, mod)) str: { - const max_len = @min(len, max_string_len); - var buf: [max_string_len]u8 = undefined; - for (buf[0..max_len], 0..) |*c, i| { - const maybe_elem = try val.maybeElemValue(mod, i); - const elem = maybe_elem orelse return writer.writeAll(".{ (reinterpreted data) }"); - if (elem.isUndef(mod)) break :str; - c.* = @as(u8, @intCast(elem.toUnsignedInt(mod))); - } - 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 = @min(len, max_aggregate_items); - for (0..max_len) |i| { - if (i != 0) try writer.writeAll(", "); - const maybe_elem = try val.maybeElemValue(mod, i); - const elem = maybe_elem orelse return writer.writeAll("(reinterpreted data) }"); - try print(.{ - .ty = elem_ty, - .val = elem, - }, writer, level - 1, mod); - } - if (len > max_aggregate_items) { - try writer.writeAll(", ..."); - } - return writer.writeAll(" }"); + try printPtr(slice.ptr, writer, false, false, 0, level, mod, opt_sema); + try writer.writeAll("[0.."); + try print(.{ + .ty = Type.usize, + .val = Value.fromInterned(slice.len), + }, writer, level - 1, mod, opt_sema); + try writer.writeAll("]"); }, - .ptr => |ptr| { - switch (ptr.addr) { - .decl => |decl_index| { - const decl = mod.declPtr(decl_index); - if (level == 0) return writer.print("(decl '{}')", .{decl.name.fmt(ip)}); - return print(.{ - .ty = decl.typeOf(mod), - .val = decl.val, - }, writer, level - 1, mod); - }, - .anon_decl => |anon_decl| { - const decl_val = anon_decl.val; - if (level == 0) return writer.print("(anon decl '{d}')", .{ - @intFromEnum(decl_val), - }); - return print(.{ - .ty = Type.fromInterned(ip.typeOf(decl_val)), - .val = Value.fromInterned(decl_val), - }, writer, level - 1, mod); - }, - .comptime_alloc => { - // TODO: we need a Sema to print this! - return writer.writeAll("(comptime alloc)"); - }, - .comptime_field => |field_val_ip| { - return print(.{ - .ty = Type.fromInterned(ip.typeOf(field_val_ip)), - .val = Value.fromInterned(field_val_ip), - }, writer, level - 1, mod); - }, - .int => |int_ip| { - try writer.writeAll("@ptrFromInt("); - try print(.{ - .ty = Type.usize, - .val = Value.fromInterned(int_ip), - }, writer, level - 1, mod); - try writer.writeByte(')'); - }, - .eu_payload => |eu_ip| { - try writer.writeAll("(payload of "); - try print(.{ - .ty = Type.fromInterned(ip.typeOf(eu_ip)), - .val = Value.fromInterned(eu_ip), - }, writer, level - 1, mod); - try writer.writeAll(")"); - }, - .opt_payload => |opt_ip| { - try print(.{ - .ty = Type.fromInterned(ip.typeOf(opt_ip)), - .val = Value.fromInterned(opt_ip), - }, writer, level - 1, mod); - try writer.writeAll(".?"); - }, - .elem => |elem| { - if (level == 0) { - try writer.writeAll("(...)"); - } else { - try print(.{ - .ty = Type.fromInterned(ip.typeOf(elem.base)), - .val = Value.fromInterned(elem.base), - }, writer, level - 1, mod); - } - try writer.print("[{}]", .{elem.index}); - }, - .field => |field| { - const ptr_container_ty = Type.fromInterned(ip.typeOf(field.base)); - if (level == 0) { - try writer.writeAll("(...)"); - } else { - try print(.{ - .ty = ptr_container_ty, - .val = Value.fromInterned(field.base), - }, writer, level - 1, mod); - } - - const container_ty = ptr_container_ty.childType(mod); - switch (container_ty.zigTypeTag(mod)) { - .Struct => { - if (container_ty.structFieldName(@intCast(field.index), mod).unwrap()) |field_name| { - try writer.print(".{i}", .{field_name.fmt(ip)}); - } else { - try writer.print("[{d}]", .{field.index}); - } - }, - .Union => { - const field_name = mod.typeToUnion(container_ty).?.loadTagType(ip).names.get(ip)[@intCast(field.index)]; - try writer.print(".{i}", .{field_name.fmt(ip)}); - }, - .Pointer => { - std.debug.assert(container_ty.isSlice(mod)); - try writer.writeAll(switch (field.index) { - Value.slice_ptr_index => ".ptr", - Value.slice_len_index => ".len", - else => unreachable, - }); - }, - else => unreachable, - } - }, + .ptr => { + const print_contents = switch (ip.getBackingAddrTag(val.toIntern()).?) { + .field, .elem, .eu_payload, .opt_payload => unreachable, + .anon_decl, .comptime_alloc, .comptime_field => true, + .decl, .int => false, + }; + if (print_contents) { + // TODO: eventually we want to load the pointer with `opt_sema`, but that's + // currently not possible without e.g. triggering compile errors. } - return; + try printPtr(val.toIntern(), writer, false, false, 0, level, mod, opt_sema); }, .opt => |opt| switch (opt.val) { - .none => return writer.writeAll("null"), - else => |payload| { - val = Value.fromInterned(payload); - ty = ty.optionalChild(mod); - }, - }, - .aggregate => |aggregate| switch (aggregate.storage) { - .bytes => |bytes| { - // Strip the 0 sentinel off of strings before printing - const zero_sent = blk: { - const sent = ty.sentinel(mod) orelse break :blk false; - break :blk sent.eql(Value.zero_u8, Type.u8, mod); - }; - const str = if (zero_sent) bytes[0 .. bytes.len - 1] else bytes; - return writer.print("\"{}\"", .{std.zig.fmtEscapes(str)}); - }, - .elems, .repeated_elem => return printAggregate(ty, val, writer, level, mod), + .none => try writer.writeAll("null"), + else => |payload| try print(.{ + .ty = tv.ty.childType(mod), + .val = Value.fromInterned(payload), + }, writer, level, mod, opt_sema), }, + .aggregate => |aggregate| try printAggregate(val, aggregate, writer, level, mod, opt_sema), .un => |un| { - try writer.writeAll(".{ "); - if (level > 0) { - if (un.tag != .none) { - try print(.{ - .ty = ty.unionTagTypeHypothetical(mod), - .val = Value.fromInterned(un.tag), - }, writer, level - 1, mod); - try writer.writeAll(" = "); - const field_ty = ty.unionFieldType(Value.fromInterned(un.tag), mod).?; - try print(.{ - .ty = field_ty, - .val = Value.fromInterned(un.val), - }, writer, level - 1, mod); - } else { - try writer.writeAll("(unknown tag) = "); - const backing_ty = try ty.unionBackingType(mod); - try print(.{ - .ty = backing_ty, - .val = Value.fromInterned(un.val), - }, writer, level - 1, mod); - } - } else try writer.writeAll("..."); - return writer.writeAll(" }"); + if (level == 0) { + try writer.writeAll(".{ ... }"); + return; + } + if (un.tag == .none) { + const backing_ty = try tv.ty.unionBackingType(mod); + try writer.print("@bitCast(@as({}, ", .{backing_ty.fmt(mod)}); + try print(.{ + .ty = backing_ty, + .val = Value.fromInterned(un.val), + }, writer, level - 1, mod, opt_sema); + try writer.writeAll("))"); + } else { + try writer.writeAll(".{ "); + try print(.{ + .ty = tv.ty.unionTagTypeHypothetical(mod), + .val = Value.fromInterned(un.tag), + }, writer, level - 1, mod, opt_sema); + try writer.writeAll(" = "); + const field_ty = tv.ty.unionFieldType(Value.fromInterned(un.tag), mod).?; + try print(.{ + .ty = field_ty, + .val = Value.fromInterned(un.val), + }, writer, level - 1, mod, opt_sema); + try writer.writeAll(" }"); + } }, .memoized_call => unreachable, - }; + } } fn printAggregate( - ty: Type, val: Value, + aggregate: InternPool.Key.Aggregate, writer: anytype, level: u8, - mod: *Module, -) (@TypeOf(writer).Error || Allocator.Error)!void { + zcu: *Zcu, + opt_sema: ?*Sema, +) (@TypeOf(writer).Error || Module.CompileError)!void { if (level == 0) { return writer.writeAll(".{ ... }"); } - const ip = &mod.intern_pool; - if (ty.zigTypeTag(mod) == .Struct) { - try writer.writeAll(".{"); - const max_len = @min(ty.structFieldCount(mod), max_aggregate_items); - - for (0..max_len) |i| { - if (i != 0) try writer.writeAll(", "); - - const field_name = ty.structFieldName(@intCast(i), mod); - - if (field_name.unwrap()) |name| try writer.print(".{} = ", .{name.fmt(ip)}); - try print(.{ - .ty = ty.structFieldType(i, mod), - .val = try val.fieldValue(mod, i), - }, writer, level - 1, mod); - } - if (ty.structFieldCount(mod) > 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: usize = @min(len, max_string_len); - var buf: [max_string_len]u8 = undefined; - - var i: u32 = 0; - while (i < max_len) : (i += 1) { - const elem = try val.fieldValue(mod, i); - if (elem.isUndef(mod)) break :str; - buf[i] = std.math.cast(u8, elem.toUnsignedInt(mod)) orelse break :str; + const ip = &zcu.intern_pool; + const ty = Type.fromInterned(aggregate.ty); + switch (ty.zigTypeTag(zcu)) { + .Struct => if (!ty.isTuple(zcu)) { + if (ty.structFieldCount(zcu) == 0) { + return writer.writeAll(".{}"); } + try writer.writeAll(".{ "); + const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items); + for (0..max_len) |i| { + if (i != 0) try writer.writeAll(", "); + const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?; + try writer.print(".{i} = ", .{field_name.fmt(ip)}); + try print(.{ + .ty = ty.structFieldType(i, zcu), + .val = try val.fieldValue(zcu, i), + }, writer, level - 1, zcu, opt_sema); + } + try writer.writeAll(" }"); + return; + }, + .Array => if (aggregate.storage == .bytes) { + return writer.print("\"{}\".*", .{std.zig.fmtEscapes(aggregate.storage.bytes)}); + } else if (ty.arrayLen(zcu) == 0) { + return writer.writeAll(".{}"); + }, + .Vector => if (ty.arrayLen(zcu) == 0) { + return writer.writeAll(".{}"); + }, + else => unreachable, + } - const truncated = if (len > max_string_len) " (truncated)" else ""; - return writer.print("\"{}{s}\"", .{ std.zig.fmtEscapes(buf[0..max_len]), truncated }); - } + const elem_ty = ty.childType(zcu); + const len = ty.arrayLen(zcu); - try writer.writeAll(".{ "); + try writer.writeAll(".{ "); - const max_len = @min(len, max_aggregate_items); - var i: u32 = 0; - while (i < max_len) : (i += 1) { - if (i != 0) try writer.writeAll(", "); + const max_len = @min(len, max_aggregate_items); + for (0..max_len) |i| { + if (i != 0) try writer.writeAll(", "); + try print(.{ + .ty = elem_ty, + .val = try val.fieldValue(zcu, i), + }, writer, level - 1, zcu, opt_sema); + } + if (len > max_aggregate_items) { + try writer.writeAll(", ..."); + } + return writer.writeAll(" }"); +} + +fn printPtr( + ptr_val: InternPool.Index, + writer: anytype, + force_type: bool, + force_addrof: bool, + leading_parens: u32, + level: u8, + zcu: *Zcu, + opt_sema: ?*Sema, +) (@TypeOf(writer).Error || Module.CompileError)!void { + const ip = &zcu.intern_pool; + const ptr = switch (ip.indexToKey(ptr_val)) { + .undef => |ptr_ty| { + if (force_addrof) try writer.writeAll("&"); + try writer.writeByteNTimes('(', leading_parens); + try writer.print("@as({}, undefined)", .{Type.fromInterned(ptr_ty).fmt(zcu)}); + return; + }, + .ptr => |ptr| ptr, + else => unreachable, + }; + switch (ptr.addr) { + .int => |int| { + if (force_addrof) try writer.writeAll("&"); + try writer.writeByteNTimes('(', leading_parens); + if (force_type) { + try writer.print("@as({}, @ptrFromInt(", .{Type.fromInterned(ptr.ty).fmt(zcu)}); + try print(.{ + .ty = Type.usize, + .val = Value.fromInterned(int), + }, writer, level - 1, zcu, opt_sema); + try writer.writeAll("))"); + } else { + try writer.writeAll("@ptrFromInt("); + try print(.{ + .ty = Type.usize, + .val = Value.fromInterned(int), + }, writer, level - 1, zcu, opt_sema); + try writer.writeAll(")"); + } + }, + .decl => |index| { + try writer.writeAll("&"); + try zcu.declPtr(index).renderFullyQualifiedName(zcu, writer); + }, + .comptime_alloc => try writer.writeAll("&(comptime alloc)"), + .anon_decl => |anon| { + const ty = Type.fromInterned(ip.typeOf(anon.val)); + try writer.print("&@as({}, ", .{ty.fmt(zcu)}); try print(.{ - .ty = elem_ty, - .val = try val.fieldValue(mod, i), - }, writer, level - 1, mod); - } - if (len > max_aggregate_items) { - try writer.writeAll(", ..."); - } - return writer.writeAll(" }"); + .ty = ty, + .val = Value.fromInterned(anon.val), + }, writer, level - 1, zcu, opt_sema); + try writer.writeAll(")"); + }, + .comptime_field => |val| { + const ty = Type.fromInterned(ip.typeOf(val)); + try writer.print("&@as({}, ", .{ty.fmt(zcu)}); + try print(.{ + .ty = ty, + .val = Value.fromInterned(val), + }, writer, level - 1, zcu, opt_sema); + try writer.writeAll(")"); + }, + .eu_payload => |base| { + try printPtr(base, writer, true, true, leading_parens, level, zcu, opt_sema); + try writer.writeAll(".?"); + }, + .opt_payload => |base| { + try writer.writeAll("("); + try printPtr(base, writer, true, true, leading_parens + 1, level, zcu, opt_sema); + try writer.writeAll(" catch unreachable"); + }, + .elem => |elem| { + try printPtr(elem.base, writer, true, true, leading_parens, level, zcu, opt_sema); + try writer.print("[{d}]", .{elem.index}); + }, + .field => |field| { + try printPtr(field.base, writer, true, true, leading_parens, level, zcu, opt_sema); + const base_ty = Type.fromInterned(ip.typeOf(field.base)).childType(zcu); + switch (base_ty.zigTypeTag(zcu)) { + .Struct => if (base_ty.isTuple(zcu)) { + try writer.print("[{d}]", .{field.index}); + } else { + const field_name = base_ty.structFieldName(@intCast(field.index), zcu).unwrap().?; + try writer.print(".{i}", .{field_name.fmt(ip)}); + }, + .Union => { + const tag_ty = base_ty.unionTagTypeHypothetical(zcu); + const field_name = tag_ty.enumFieldName(@intCast(field.index), zcu); + try writer.print(".{i}", .{field_name.fmt(ip)}); + }, + .Pointer => switch (field.index) { + Value.slice_ptr_index => try writer.writeAll(".ptr"), + Value.slice_len_index => try writer.writeAll(".len"), + else => unreachable, + }, + else => unreachable, + } + }, } } diff --git a/src/Value.zig b/src/Value.zig index 040961fa38..290f38d117 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -305,16 +305,16 @@ pub fn toBool(val: Value) bool { }; } -fn isDeclRef(val: Value, mod: *Module) bool { +fn ptrHasIntAddr(val: Value, mod: *Module) bool { var check = val; while (true) switch (mod.intern_pool.indexToKey(check.toIntern())) { .ptr => |ptr| switch (ptr.addr) { - .decl, .comptime_alloc, .comptime_field, .anon_decl => return true, + .decl, .comptime_alloc, .comptime_field, .anon_decl => return false, + .int => return true, .eu_payload, .opt_payload => |base| check = Value.fromInterned(base), .elem, .field => |base_index| check = Value.fromInterned(base_index.base), - .int => return false, }, - else => return false, + else => unreachable, }; } @@ -439,7 +439,7 @@ pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{ }, .Pointer => { if (ty.isSlice(mod)) return error.IllDefinedMemoryLayout; - if (val.isDeclRef(mod)) return error.ReinterpretDeclRef; + if (!val.ptrHasIntAddr(mod)) return error.ReinterpretDeclRef; return val.writeToMemory(Type.usize, mod, buffer); }, .Optional => { @@ -566,7 +566,7 @@ pub fn writeToPackedMemory( }, .Pointer => { assert(!ty.isSlice(mod)); // No well defined layout. - if (val.isDeclRef(mod)) return error.ReinterpretDeclRef; + if (!val.ptrHasIntAddr(mod)) return error.ReinterpretDeclRef; return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset); }, .Optional => { @@ -1261,62 +1261,23 @@ pub fn slicePtr(val: Value, mod: *Module) Value { return Value.fromInterned(mod.intern_pool.slicePtr(val.toIntern())); } -pub fn sliceLen(val: Value, mod: *Module) u64 { - const ip = &mod.intern_pool; - return switch (ip.indexToKey(val.toIntern())) { - .ptr => |ptr| switch (ip.indexToKey(switch (ptr.addr) { - .decl => |decl| mod.declPtr(decl).typeOf(mod).toIntern(), - .comptime_alloc => @panic("TODO"), - .anon_decl => |anon_decl| ip.typeOf(anon_decl.val), - .comptime_field => |comptime_field| ip.typeOf(comptime_field), - else => unreachable, - })) { - .array_type => |array_type| array_type.len, - else => 1, +/// Gets the `len` field of a slice value as a `u64`. +/// Resolves the length using the provided `Sema` if necessary. +pub fn sliceLen(val: Value, sema: *Sema) !u64 { + return Value.fromInterned(sema.mod.intern_pool.sliceLen(val.toIntern())).toUnsignedIntAdvanced(sema); +} + +/// Asserts the value is an aggregate, and returns the element value at the given index. +pub fn elemValue(val: Value, zcu: *Zcu, index: usize) Allocator.Error!Value { + const ip = &zcu.intern_pool; + switch (zcu.intern_pool.indexToKey(val.toIntern())) { + .undef => |ty| { + return Value.fromInterned(try zcu.intern(.{ .undef = Type.fromInterned(ty).childType(zcu).toIntern() })); }, - .slice => |slice| Value.fromInterned(slice.len).toUnsignedInt(mod), - else => unreachable, - }; -} - -/// Asserts the value is a single-item pointer to an array, or an array, -/// or an unknown-length pointer, and returns the element value at the index. -pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value { - return (try val.maybeElemValue(mod, index)).?; -} - -/// Like `elemValue`, but returns `null` instead of asserting on failure. -pub fn maybeElemValue(val: Value, mod: *Module, index: usize) Allocator.Error!?Value { - return val.maybeElemValueFull(null, mod, index); -} - -pub fn maybeElemValueFull(val: Value, sema: ?*Sema, mod: *Module, index: usize) Allocator.Error!?Value { - return switch (mod.intern_pool.indexToKey(val.toIntern())) { - .undef => |ty| Value.fromInterned((try mod.intern(.{ - .undef = Type.fromInterned(ty).elemType2(mod).toIntern(), - }))), - .slice => |slice| return Value.fromInterned(slice.ptr).maybeElemValueFull(sema, mod, index), - .ptr => |ptr| switch (ptr.addr) { - .decl => |decl| mod.declPtr(decl).val.maybeElemValueFull(sema, mod, index), - .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).maybeElemValueFull(sema, mod, index), - .comptime_alloc => |idx| if (sema) |s| Value.fromInterned( - try s.getComptimeAlloc(idx).val.intern(mod, s.arena), - ).maybeElemValueFull(sema, mod, index) else null, - .int, .eu_payload => null, - .opt_payload => |base| Value.fromInterned(base).maybeElemValueFull(sema, mod, index), - .comptime_field => |field_val| Value.fromInterned(field_val).maybeElemValueFull(sema, mod, index), - .elem => |elem| Value.fromInterned(elem.base).maybeElemValueFull(sema, mod, index + @as(usize, @intCast(elem.index))), - .field => |field| if (Value.fromInterned(field.base).pointerDecl(mod)) |decl_index| { - const base_decl = mod.declPtr(decl_index); - const field_val = try base_decl.val.fieldValue(mod, @as(usize, @intCast(field.index))); - return field_val.maybeElemValueFull(sema, mod, index); - } else null, - }, - .opt => |opt| Value.fromInterned(opt.val).maybeElemValueFull(sema, mod, index), .aggregate => |aggregate| { - const len = mod.intern_pool.aggregateTypeLen(aggregate.ty); + const len = ip.aggregateTypeLen(aggregate.ty); if (index < len) return Value.fromInterned(switch (aggregate.storage) { - .bytes => |bytes| try mod.intern(.{ .int = .{ + .bytes => |bytes| try zcu.intern(.{ .int = .{ .ty = .u8_type, .storage = .{ .u64 = bytes[index] }, } }), @@ -1324,10 +1285,10 @@ pub fn maybeElemValueFull(val: Value, sema: ?*Sema, mod: *Module, index: usize) .repeated_elem => |elem| elem, }); assert(index == len); - return Value.fromInterned(mod.intern_pool.indexToKey(aggregate.ty).array_type.sentinel); + return Type.fromInterned(aggregate.ty).sentinel(zcu).?; }, - else => null, - }; + else => unreachable, + } } pub fn isLazyAlign(val: Value, mod: *Module) bool { @@ -1359,39 +1320,26 @@ pub fn sliceArray( ) error{OutOfMemory}!Value { // TODO: write something like getCoercedInts to avoid needing to dupe const mod = sema.mod; - return switch (mod.intern_pool.indexToKey(val.toIntern())) { - .ptr => |ptr| switch (ptr.addr) { - .decl => |decl| try mod.declPtr(decl).val.sliceArray(sema, start, end), - .comptime_alloc => |idx| try Value.fromInterned( - try sema.getComptimeAlloc(idx).val.intern(mod, sema.arena), - ).sliceArray(sema, start, end), - .comptime_field => |comptime_field| Value.fromInterned(comptime_field) - .sliceArray(sema, start, end), - .elem => |elem| Value.fromInterned(elem.base) - .sliceArray(sema, start + @as(usize, @intCast(elem.index)), end + @as(usize, @intCast(elem.index))), + const aggregate = mod.intern_pool.indexToKey(val.toIntern()).aggregate; + return Value.fromInterned(try mod.intern(.{ .aggregate = .{ + .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) { + .array_type => |array_type| try mod.arrayType(.{ + .len = @as(u32, @intCast(end - start)), + .child = array_type.child, + .sentinel = if (end == array_type.len) array_type.sentinel else .none, + }), + .vector_type => |vector_type| try mod.vectorType(.{ + .len = @as(u32, @intCast(end - start)), + .child = vector_type.child, + }), else => unreachable, + }.toIntern(), + .storage = switch (aggregate.storage) { + .bytes => .{ .bytes = try sema.arena.dupe(u8, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.bytes[start..end]) }, + .elems => .{ .elems = try sema.arena.dupe(InternPool.Index, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.elems[start..end]) }, + .repeated_elem => |elem| .{ .repeated_elem = elem }, }, - .aggregate => |aggregate| Value.fromInterned((try mod.intern(.{ .aggregate = .{ - .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) { - .array_type => |array_type| try mod.arrayType(.{ - .len = @as(u32, @intCast(end - start)), - .child = array_type.child, - .sentinel = if (end == array_type.len) array_type.sentinel else .none, - }), - .vector_type => |vector_type| try mod.vectorType(.{ - .len = @as(u32, @intCast(end - start)), - .child = vector_type.child, - }), - else => unreachable, - }.toIntern(), - .storage = switch (aggregate.storage) { - .bytes => .{ .bytes = try sema.arena.dupe(u8, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.bytes[start..end]) }, - .elems => .{ .elems = try sema.arena.dupe(InternPool.Index, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.elems[start..end]) }, - .repeated_elem => |elem| .{ .repeated_elem = elem }, - }, - } }))), - else => unreachable, - }; + } })); } pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value { @@ -3586,6 +3534,10 @@ pub fn isGenericPoison(val: Value) bool { return val.toIntern() == .generic_poison; } +pub fn typeOf(val: Value, zcu: *const Zcu) Type { + return Type.fromInterned(zcu.intern_pool.typeOf(val.toIntern())); +} + /// For an integer (comptime or fixed-width) `val`, returns the comptime-known bounds of the value. /// If `val` is not undef, the bounds are both `val`. /// If `val` is undef and has a fixed-width type, the bounds are the bounds of the type. diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index e76416ddb0..c9abaf9c8b 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -693,31 +693,6 @@ test "string concatenation" { try expect(b[len] == 0); } -fn manyptrConcat(comptime s: [*:0]const u8) [*:0]const u8 { - return "very " ++ s; -} - -test "comptime manyptr concatenation" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - - const s = "epic"; - const actual = manyptrConcat(s); - const expected = "very epic"; - - const len = mem.len(actual); - const len_with_null = len + 1; - { - var i: u32 = 0; - while (i < len_with_null) : (i += 1) { - try expect(actual[i] == expected[i]); - } - } - try expect(actual[len] == 0); - try expect(expected[len] == 0); -} - test "result location is optional inside error union" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO