From 40676e4d17e9d56395f5f4ea0804b409fde6fbb7 Mon Sep 17 00:00:00 2001 From: Sarah Spall Date: Tue, 23 Sep 2025 11:16:26 -0700 Subject: [PATCH 1/2] ok to init struct with zero members --- clang/lib/Sema/SemaHLSL.cpp | 2 + .../CodeGenHLSL/BasicFeatures/InitLists.hlsl | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 7a61474e2a25c..7f85886f72366 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4284,6 +4284,8 @@ bool SemaHLSL::transformInitList(const InitializedEntity &Entity, } size_t ExpectedSize = ILT.DestTypes.size(); size_t ActualSize = ILT.ArgExprs.size(); + if (ExpectedSize == 0 && ActualSize == 0) + return true; // For incomplete arrays it is completely arbitrary to choose whether we think // the user intended fewer or more elements. This implementation assumes that // the user intended more, and errors that there are too few initializers to diff --git a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl index 5a5c45f686956..5fe69806b61eb 100644 --- a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl +++ b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl @@ -50,6 +50,16 @@ struct Unnamed { int : 8; }; +struct Empty { +}; + +struct UnnamedOnly { + int : 8; +}; + +// CHECK-DAG: [[ConstE:@.*]] = private unnamed_addr constant %struct.Empty undef, align 1 +// CHECK-DAG: [[ConstUO:@.*]] = private unnamed_addr constant %struct.UnnamedOnly undef, align 1 + // Case 1: Extraneous braces get ignored in literal instantiation. // CHECK-LABEL: define hidden void @_Z5case1v( // CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOFLOATS:%.*]]) align 1 [[AGG_RESULT:%.*]]) #[[ATTR0:[0-9]+]] { @@ -985,3 +995,35 @@ void case18() { void case19(Unnamed U) { TwoInts TI = {U, 1}; } + +// InitList with Empty Struct on LHS +// CHECK-LABEL: case20 +// CHECK: [[E:%.*]] = alloca %struct.Empty, align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[E]], ptr align 1 [[ConstE]], i32 1, i1 false) +void case20() { + Empty E = {}; +} + +// InitList with Empty Struct on RHS +// CHECK-LABEL: case21 +// CHECK: [[TI:%.*]] = alloca %struct.TwoInts, align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 %TI, ptr align 1 {{.*}}, i32 8, i1 false) +void case21(Empty E) { + TwoInts TI = {E, 1, 2}; +} + +// InitList with Struct with only unnamed bitfield on LHS +// CHECK-LABEL: case22 +// CHECK: [[UO:%.*]] = alloca %struct.UnnamedOnly, align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[UO]], ptr align 1 [[ConstUO]], i32 1, i1 false) +void case22() { + UnnamedOnly UO = {}; +} + +// InitList with Struct with only unnamed bitfield on RHS +// CHECK-LABEL: case23 +// CHECK: [[TI:%.*]] = alloca %struct.TwoInts, align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TI]], ptr align 1 {{.*}}, i32 8, i1 false) +void case23(UnnamedOnly UO) { + TwoInts TI = {UO, 1, 2}; +} From 1f5f4b4bf57c3f1b008835ba39f528bedd4d9861 Mon Sep 17 00:00:00 2001 From: Sarah Spall Date: Tue, 23 Sep 2025 13:30:33 -0700 Subject: [PATCH 2/2] respond to pr comments --- clang/lib/Sema/SemaHLSL.cpp | 1 + .../CodeGenHLSL/BasicFeatures/InitLists.hlsl | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 7f85886f72366..2e9706e076182 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4286,6 +4286,7 @@ bool SemaHLSL::transformInitList(const InitializedEntity &Entity, size_t ActualSize = ILT.ArgExprs.size(); if (ExpectedSize == 0 && ActualSize == 0) return true; + // For incomplete arrays it is completely arbitrary to choose whether we think // the user intended fewer or more elements. This implementation assumes that // the user intended more, and errors that there are too few initializers to diff --git a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl index 5fe69806b61eb..7e83e5f168538 100644 --- a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl +++ b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl @@ -57,8 +57,14 @@ struct UnnamedOnly { int : 8; }; +struct EmptyDerived : Empty {}; + +struct UnnamedDerived : UnnamedOnly {}; + // CHECK-DAG: [[ConstE:@.*]] = private unnamed_addr constant %struct.Empty undef, align 1 // CHECK-DAG: [[ConstUO:@.*]] = private unnamed_addr constant %struct.UnnamedOnly undef, align 1 +// CHECK-DAG: [[ConstED:@.*]] = private unnamed_addr constant %struct.EmptyDerived undef, align 1 +// CHECK-DAG: [[ConstUD:@.*]] = private unnamed_addr constant %struct.UnnamedDerived undef, align 1 // Case 1: Extraneous braces get ignored in literal instantiation. // CHECK-LABEL: define hidden void @_Z5case1v( @@ -1027,3 +1033,25 @@ void case22() { void case23(UnnamedOnly UO) { TwoInts TI = {UO, 1, 2}; } + +// InitList with Derived empty struct on LHS +// InitList with Derived unnamed bitfield on LHS +// CHECK-LABEL: case24 +// CHECK: [[ED:%.*]] = alloca %struct.EmptyDerived, align 1 +// CHECK-NEXT: [[UD:%.*]] = alloca %struct.UnnamedDerived, align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 %ED, ptr align 1 [[ConstED]], i32 1, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 %UD, ptr align 1 [[ConstUD]], i32 1, i1 false) +void case24() { + EmptyDerived ED = {}; + UnnamedDerived UD = {}; +} + +// CHECK-LABEL: case25 +// CHECK: [[TI1:%.*]] = alloca %struct.TwoInts, align 1 +// CHECK-NEXT: [[TI2:%.*]] = alloca %struct.TwoInts, align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 %TI1, ptr align 1 {{.*}}, i32 8, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 %TI2, ptr align 1 {{.*}}, i32 8, i1 false) +void case25(EmptyDerived ED, UnnamedDerived UD) { + TwoInts TI1 = {ED, 1, 2}; + TwoInts TI2 = {UD, 1, 2}; +}