diff --git a/src/Sema.zig b/src/Sema.zig index 91acbeeb59..cdc80f3cea 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20290,11 +20290,41 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is const zcu = pt.zcu; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); + // Generic poison means this is an untyped anonymous empty struct/array init - const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return .empty_tuple; + const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse { + if (is_byref) { + return sema.uavRef(.empty_tuple); + } else { + return .empty_tuple; + } + }; + const init_ty = if (is_byref) ty: { const ptr_ty = ty_operand.optEuBaseType(zcu); assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction + switch (ptr_ty.ptrSize(zcu)) { + // Use a zero-length array for a slice or many-ptr result + .slice, .many => break :ty try pt.arrayType(.{ + .len = 0, + .child = ptr_ty.childType(zcu).toIntern(), + .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, + }), + // Just use the child type for a single-pointer or C-pointer result + .one, .c => { + const child = ptr_ty.childType(zcu); + if (child.toIntern() == .anyopaque_type) { + // ...unless that child is anyopaque, in which case this is equivalent to an untyped init. + // `.{}` is an empty tuple. + if (is_byref) { + return sema.uavRef(.empty_tuple); + } else { + return .empty_tuple; + } + } + break :ty child; + }, + } if (!ptr_ty.isSlice(zcu)) { break :ty ptr_ty.childType(zcu); } diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 61225aa3e0..14b2a9694b 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -1094,3 +1094,44 @@ test "@splat zero-length array" { try S.doTheTest(?*anyopaque, null); try comptime S.doTheTest(?*anyopaque, null); } + +test "initialize slice with reference to empty array initializer" { + const a: []const u8 = &.{}; + comptime assert(a.len == 0); +} + +test "initialize many-pointer with reference to empty array initializer" { + const a: [*]const u8 = &.{}; + _ = a; // nothing meaningful to test; points to zero bits +} + +test "initialize sentinel-terminated slice with reference to empty array initializer" { + const a: [:0]const u8 = &.{}; + comptime assert(a.len == 0); + comptime assert(a[0] == 0); +} + +test "initialize sentinel-terminated many-pointer with reference to empty array initializer" { + const a: [*:0]const u8 = &.{}; + comptime assert(a[0] == 0); +} + +test "pass pointer to empty array initializer to anytype parameter" { + const S = struct { + fn TypeOf(x: anytype) type { + return @TypeOf(x); + } + }; + comptime assert(S.TypeOf(&.{}) == @TypeOf(&.{})); +} + +test "initialize pointer to anyopaque with reference to empty array initializer" { + const ptr: *const anyopaque = &.{}; + // The above acts like an untyped initializer, since the `.{}` has no result type. + // So, `ptr` points in memory to an empty tuple (`@TypeOf(.{})`). + const casted: *const @TypeOf(.{}) = @alignCast(@ptrCast(ptr)); + const loaded = casted.*; + // `val` should be a `@TypeOf(.{})`, as expected. + // We can't check the value, but it's zero-bit, so the type matching is good enough. + comptime assert(@TypeOf(loaded) == @TypeOf(.{})); +}