Skip to content

Commit c5f2a1f

Browse files
committed
Fix initialization with resources in structs
This was a bit tricker than I expected, but I think I came up with a reasonably clever solution. In HLSL, user-defined data types have aggregate initialization, not constructors _except_ that some of the builtin types we do model constructors for. This is actually useful! In my earlier change I updated DeclCXX so that HLSL non-implicit classes are always marged as Aggregates. This ends up being not quite right, because there are some implicit types that should be aggregates, and others that shouldn't. For example, the implicit cbuffer-layout types should be aggregates so that we can flatten them, but resources shouldn't be because we really don't want to flatten them. In the update, whether or not an HLSL type is an aggregate is keyed off having non-implicit "special" members (constructors & operators). This is more correct. Aggregate types get flattened out for casting and initialization, while non-Aggregate types (basically just resources) get left unflattened and we attempt copy-initialization on them. Next problem: I was getting some odd conflicting diagnostics when argument conversion or copy-initialization fails, so I refactored BuildInitializerList and CastInitializer to return success/failure so that we can propagate that up and fail if the argument->destination type fails without then also complaining about the number of initializers.
1 parent 2e1452d commit c5f2a1f

File tree

3 files changed

+64
-37
lines changed

3 files changed

+64
-37
lines changed

clang/lib/AST/DeclCXX.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,8 +1468,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
14681468
// relevant HLSL feature proposals that will depend on this changing:
14691469
// * 0005-strict-initializer-lists.md
14701470
// * https://github.com/microsoft/hlsl-specs/pull/325
1471-
if (getLangOpts().HLSL && !isImplicit())
1472-
data().Aggregate = true;
1471+
if (getLangOpts().HLSL)
1472+
data().Aggregate = data().UserDeclaredSpecialMembers == 0;
14731473
}
14741474

14751475
bool CXXRecordDecl::isLiteral() const {

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2589,17 +2589,20 @@ static void BuildFlattenedTypeList(QualType BaseTy,
25892589
continue;
25902590
}
25912591
if (const auto *RT = dyn_cast<RecordType>(T)) {
2592-
const RecordDecl *RD = RT->getDecl();
2593-
if (RD->isUnion()) {
2592+
const CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
2593+
assert(RD && "HLSL record types should all be CXXRecordDecls!");
2594+
2595+
if (RD->isStandardLayout())
2596+
RD = RD->getStandardLayoutBaseWithFields();
2597+
2598+
// For types that we shouldn't decompose (unios and non-aggregates), just
2599+
// add the type itself to the list.
2600+
if (RD->isUnion() || !RD->isAggregate()) {
25942601
List.push_back(T);
25952602
continue;
25962603
}
2597-
const CXXRecordDecl *CXXD = dyn_cast<CXXRecordDecl>(RD);
25982604

25992605
llvm::SmallVector<QualType, 16> FieldTypes;
2600-
if (CXXD && CXXD->isStandardLayout())
2601-
RD = CXXD->getStandardLayoutBaseWithFields();
2602-
26032606
for (const auto *FD : RD->fields())
26042607
FieldTypes.push_back(FD->getType());
26052608
// Reverse the newly added sub-range.
@@ -2608,9 +2611,9 @@ static void BuildFlattenedTypeList(QualType BaseTy,
26082611

26092612
// If this wasn't a standard layout type we may also have some base
26102613
// classes to deal with.
2611-
if (CXXD && !CXXD->isStandardLayout()) {
2614+
if (!RD->isStandardLayout()) {
26122615
FieldTypes.clear();
2613-
for (const auto &Base : CXXD->bases())
2616+
for (const auto &Base : RD->bases())
26142617
FieldTypes.push_back(Base.getType());
26152618
std::reverse(FieldTypes.begin(), FieldTypes.end());
26162619
WorkList.insert(WorkList.end(), FieldTypes.begin(), FieldTypes.end());
@@ -3062,6 +3065,8 @@ static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
30623065
llvm::SmallVectorImpl<QualType> &DestTypes) {
30633066
if (List.size() >= DestTypes.size()) {
30643067
List.push_back(E);
3068+
// This is odd, but it isn't technically a failure due to conversion, we
3069+
// handle mismatched counts of arguments differently.
30653070
return true;
30663071
}
30673072
InitializedEntity Entity = InitializedEntity::InitializeParameter(
@@ -3074,32 +3079,26 @@ static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
30743079
return true;
30753080
}
30763081

3077-
static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
3082+
static bool BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
30783083
llvm::SmallVectorImpl<Expr *> &List,
3079-
llvm::SmallVectorImpl<QualType> &DestTypes,
3080-
bool &ExcessInits) {
3081-
if (List.size() >= DestTypes.size())
3082-
ExcessInits = true;
3083-
3084+
llvm::SmallVectorImpl<QualType> &DestTypes) {
30843085
// If this is an initialization list, traverse the sub initializers.
30853086
if (auto *Init = dyn_cast<InitListExpr>(E)) {
30863087
for (auto *SubInit : Init->inits())
3087-
BuildInitializerList(S, Ctx, SubInit, List, DestTypes, ExcessInits);
3088-
return;
3088+
if (!BuildInitializerList(S, Ctx, SubInit, List, DestTypes))
3089+
return false;
3090+
return true;
30893091
}
30903092

30913093
// If this is a scalar type, just enqueue the expression.
30923094
QualType Ty = E->getType();
3093-
if (Ty->isScalarType()) {
3094-
(void)CastInitializer(S, Ctx, E, List, DestTypes);
3095-
return;
3096-
}
3095+
3096+
if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType()))
3097+
return CastInitializer(S, Ctx, E, List, DestTypes);
30973098

30983099
if (auto *ATy = Ty->getAs<VectorType>()) {
30993100
uint64_t Size = ATy->getNumElements();
31003101

3101-
if (List.size() + Size > DestTypes.size())
3102-
ExcessInits = true;
31033102
QualType SizeTy = Ctx.getSizeType();
31043103
uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
31053104
for (uint64_t I = 0; I < Size; ++I) {
@@ -3109,10 +3108,11 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
31093108
ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
31103109
E, E->getBeginLoc(), Idx, E->getEndLoc());
31113110
if (ElExpr.isInvalid())
3112-
return;
3113-
CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes);
3111+
return false;
3112+
if (!CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes))
3113+
return false;
31143114
}
3115-
return;
3115+
return true;
31163116
}
31173117

31183118
if (auto *VTy = dyn_cast<ConstantArrayType>(Ty.getTypePtr())) {
@@ -3125,10 +3125,11 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
31253125
ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
31263126
E, E->getBeginLoc(), Idx, E->getEndLoc());
31273127
if (ElExpr.isInvalid())
3128-
return;
3129-
BuildInitializerList(S, Ctx, ElExpr.get(), List, DestTypes, ExcessInits);
3128+
return false;
3129+
if (!BuildInitializerList(S, Ctx, ElExpr.get(), List, DestTypes))
3130+
return false;
31303131
}
3131-
return;
3132+
return true;
31323133
}
31333134

31343135
if (auto *RTy = Ty->getAs<RecordType>()) {
@@ -3149,16 +3150,18 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
31493150
ExprResult Res = S.BuildFieldReferenceExpr(
31503151
E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo);
31513152
if (Res.isInvalid())
3152-
return;
3153-
BuildInitializerList(S, Ctx, Res.get(), List, DestTypes, ExcessInits);
3153+
return false;
3154+
if (!BuildInitializerList(S, Ctx, Res.get(), List, DestTypes))
3155+
return false;
31543156
}
31553157
}
31563158
}
3159+
return true;
31573160
}
31583161

31593162
static Expr *GenerateInitLists(ASTContext &Ctx, QualType Ty,
31603163
llvm::SmallVectorImpl<Expr *>::iterator &It) {
3161-
if (Ty->isScalarType()) {
3164+
if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType())) {
31623165
return *(It++);
31633166
}
31643167
llvm::SmallVector<Expr *> Inits;
@@ -3216,12 +3219,12 @@ bool SemaHLSL::TransformInitList(const InitializedEntity &Entity,
32163219
BuildFlattenedTypeList(InitTy, DestTypes);
32173220

32183221
llvm::SmallVector<Expr *, 16> ArgExprs;
3219-
bool ExcessInits = false;
32203222
for (Expr *Arg : Init->inits())
3221-
BuildInitializerList(SemaRef, Ctx, Arg, ArgExprs, DestTypes, ExcessInits);
3223+
if (!BuildInitializerList(SemaRef, Ctx, Arg, ArgExprs, DestTypes))
3224+
return false;
32223225

3223-
if (DestTypes.size() != ArgExprs.size() || ExcessInits) {
3224-
int TooManyOrFew = ExcessInits ? 1 : 0;
3226+
if (DestTypes.size() != ArgExprs.size()) {
3227+
int TooManyOrFew = ArgExprs.size() > DestTypes.size() ? 1 : 0;
32253228
SemaRef.Diag(Init->getBeginLoc(), diag::err_hlsl_incorrect_num_initializers)
32263229
<< TooManyOrFew << InitTy << DestTypes.size() << ArgExprs.size();
32273230
return false;

clang/test/SemaHLSL/Language/InitLists.hlsl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ struct SlicyBits {
4444
int W : 8;
4545
};
4646

47+
struct ContainsResource { // #ContainsResource
48+
int X;
49+
RWBuffer<float4> B;
50+
};
51+
52+
struct ContainsResourceInverted {
53+
RWBuffer<float4> B;
54+
int X;
55+
};
56+
4757
void fn() {
4858
TwoFloats TF1 = {{{1.0, 2}}};
4959
TwoFloats TF2 = {1,2};
@@ -87,3 +97,17 @@ void Errs() {
8797

8898
int2 Something = {1.xxx}; // expected-error{{too many initializers in list for type 'int2' (aka 'vector<int, 2>') (expected 2 but found 3)}}
8999
}
100+
101+
void Err2(RWBuffer<float4> B) {
102+
ContainsResource RS1 = {1, B};
103+
ContainsResource RS2 = (1.xx); // expected-error{{no viable conversion from 'vector<int, 2>' (vector of 2 'int' values) to 'ContainsResource'}}
104+
ContainsResource RS3 = {B, 1}; // expected-error{{no viable conversion from 'RWBuffer<float4>' (aka 'RWBuffer<vector<float, 4>>') to 'int'}}
105+
ContainsResourceInverted IR = {RS1}; // expected-error{{no viable conversion from 'int' to 'hlsl::RWBuffer<vector<float, 4>>'}}
106+
}
107+
108+
// expected-note@#ContainsResource{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'vector<int, 2>' (vector of 2 'int' values) to 'const ContainsResource &' for 1st argument}}
109+
// expected-note@#ContainsResource{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'vector<int, 2>' (vector of 2 'int' values) to 'ContainsResource &&' for 1st argument}}
110+
111+
// These notes refer to the RWBuffer constructors that do not have source locations
112+
// expected-note@*{{candidate constructor (the implicit copy constructor) not viable}}
113+
// expected-note@*{{candidate constructor (the implicit move constructor) not viable}}

0 commit comments

Comments
 (0)