-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[HLSL] Handle incomplete array types #133508
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
6234f44
[HLSL] Handle incomplete array types
llvm-beanz 279cb38
Fix formatting
llvm-beanz 46afbd6
Remove unused parameter to SemaHLSL::transformInitList
llvm-beanz 79b270c
clang-format
llvm-beanz 0fb3605
Update based on review feedback from @spall
llvm-beanz 46cb70b
Merge remote-tracking branch 'origin/main' into
llvm-beanz f160e14
Updating based on PR feedback
llvm-beanz e517bf9
Merge remote-tracking branch 'origin/main' into
llvm-beanz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3294,164 +3294,210 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) { | |
if (!HasBinding && isResourceRecordTypeOrArrayOf(VD)) | ||
SemaRef.Diag(VD->getLocation(), diag::warn_hlsl_implicit_binding); | ||
} | ||
|
||
static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E, | ||
llvm::SmallVectorImpl<Expr *> &List, | ||
llvm::SmallVectorImpl<QualType> &DestTypes) { | ||
if (List.size() >= DestTypes.size()) { | ||
List.push_back(E); | ||
// This is odd, but it isn't technically a failure due to conversion, we | ||
// handle mismatched counts of arguments differently. | ||
namespace { | ||
class InitListTransformer { | ||
Sema &S; | ||
ASTContext &Ctx; | ||
QualType InitTy; | ||
QualType *DstIt = nullptr; | ||
Expr **ArgIt = nullptr; | ||
// Is wrapping the destination type iterator required? This is only used for | ||
// incomplete array types where we loop over the destination type since we | ||
// don't know the full number of elements from the declaration. | ||
bool Wrap; | ||
|
||
bool castInitializer(Expr *E) { | ||
assert(DstIt && "This should always be something!"); | ||
if (DstIt == DestTypes.end()) { | ||
if (!Wrap) { | ||
ArgExprs.push_back(E); | ||
// This is odd, but it isn't technically a failure due to conversion, we | ||
// handle mismatched counts of arguments differently. | ||
return true; | ||
} | ||
DstIt = DestTypes.begin(); | ||
} | ||
InitializedEntity Entity = InitializedEntity::InitializeParameter( | ||
Ctx, *DstIt, /* Consumed (ObjC) */ false); | ||
ExprResult Res = S.PerformCopyInitialization(Entity, E->getBeginLoc(), E); | ||
if (Res.isInvalid()) | ||
return false; | ||
Expr *Init = Res.get(); | ||
ArgExprs.push_back(Init); | ||
DstIt++; | ||
return true; | ||
} | ||
InitializedEntity Entity = InitializedEntity::InitializeParameter( | ||
Ctx, DestTypes[List.size()], false); | ||
ExprResult Res = S.PerformCopyInitialization(Entity, E->getBeginLoc(), E); | ||
if (Res.isInvalid()) | ||
return false; | ||
Expr *Init = Res.get(); | ||
List.push_back(Init); | ||
return true; | ||
} | ||
|
||
static bool BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E, | ||
llvm::SmallVectorImpl<Expr *> &List, | ||
llvm::SmallVectorImpl<QualType> &DestTypes) { | ||
// If this is an initialization list, traverse the sub initializers. | ||
if (auto *Init = dyn_cast<InitListExpr>(E)) { | ||
for (auto *SubInit : Init->inits()) | ||
if (!BuildInitializerList(S, Ctx, SubInit, List, DestTypes)) | ||
return false; | ||
return true; | ||
} | ||
bool buildInitializerListImpl(Expr *E) { | ||
// If this is an initialization list, traverse the sub initializers. | ||
if (auto *Init = dyn_cast<InitListExpr>(E)) { | ||
for (auto *SubInit : Init->inits()) | ||
if (!buildInitializerListImpl(SubInit)) | ||
return false; | ||
return true; | ||
} | ||
|
||
// If this is a scalar type, just enqueue the expression. | ||
QualType Ty = E->getType(); | ||
// If this is a scalar type, just enqueue the expression. | ||
QualType Ty = E->getType(); | ||
|
||
if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType())) | ||
return CastInitializer(S, Ctx, E, List, DestTypes); | ||
if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType())) | ||
return castInitializer(E); | ||
|
||
if (auto *VecTy = Ty->getAs<VectorType>()) { | ||
uint64_t Size = VecTy->getNumElements(); | ||
if (auto *VecTy = Ty->getAs<VectorType>()) { | ||
uint64_t Size = VecTy->getNumElements(); | ||
|
||
QualType SizeTy = Ctx.getSizeType(); | ||
uint64_t SizeTySize = Ctx.getTypeSize(SizeTy); | ||
for (uint64_t I = 0; I < Size; ++I) { | ||
auto *Idx = IntegerLiteral::Create(Ctx, llvm::APInt(SizeTySize, I), | ||
SizeTy, SourceLocation()); | ||
QualType SizeTy = Ctx.getSizeType(); | ||
uint64_t SizeTySize = Ctx.getTypeSize(SizeTy); | ||
for (uint64_t I = 0; I < Size; ++I) { | ||
auto *Idx = IntegerLiteral::Create(Ctx, llvm::APInt(SizeTySize, I), | ||
SizeTy, SourceLocation()); | ||
|
||
ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr( | ||
E, E->getBeginLoc(), Idx, E->getEndLoc()); | ||
if (ElExpr.isInvalid()) | ||
return false; | ||
if (!CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes)) | ||
return false; | ||
ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr( | ||
E, E->getBeginLoc(), Idx, E->getEndLoc()); | ||
if (ElExpr.isInvalid()) | ||
return false; | ||
if (!castInitializer(ElExpr.get())) | ||
return false; | ||
} | ||
return true; | ||
} | ||
return true; | ||
} | ||
|
||
if (auto *ArrTy = dyn_cast<ConstantArrayType>(Ty.getTypePtr())) { | ||
uint64_t Size = ArrTy->getZExtSize(); | ||
QualType SizeTy = Ctx.getSizeType(); | ||
uint64_t SizeTySize = Ctx.getTypeSize(SizeTy); | ||
for (uint64_t I = 0; I < Size; ++I) { | ||
auto *Idx = IntegerLiteral::Create(Ctx, llvm::APInt(SizeTySize, I), | ||
SizeTy, SourceLocation()); | ||
ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr( | ||
E, E->getBeginLoc(), Idx, E->getEndLoc()); | ||
if (ElExpr.isInvalid()) | ||
return false; | ||
if (!BuildInitializerList(S, Ctx, ElExpr.get(), List, DestTypes)) | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
if (auto *RTy = Ty->getAs<RecordType>()) { | ||
llvm::SmallVector<const RecordType *> RecordTypes; | ||
RecordTypes.push_back(RTy); | ||
while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) { | ||
CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl(); | ||
assert(D->getNumBases() == 1 && | ||
"HLSL doesn't support multiple inheritance"); | ||
RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>()); | ||
} | ||
while (!RecordTypes.empty()) { | ||
const RecordType *RT = RecordTypes.pop_back_val(); | ||
for (auto *FD : RT->getDecl()->fields()) { | ||
DeclAccessPair Found = DeclAccessPair::make(FD, FD->getAccess()); | ||
DeclarationNameInfo NameInfo(FD->getDeclName(), E->getBeginLoc()); | ||
ExprResult Res = S.BuildFieldReferenceExpr( | ||
E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo); | ||
if (Res.isInvalid()) | ||
if (auto *ArrTy = dyn_cast<ConstantArrayType>(Ty.getTypePtr())) { | ||
uint64_t Size = ArrTy->getZExtSize(); | ||
QualType SizeTy = Ctx.getSizeType(); | ||
uint64_t SizeTySize = Ctx.getTypeSize(SizeTy); | ||
for (uint64_t I = 0; I < Size; ++I) { | ||
auto *Idx = IntegerLiteral::Create(Ctx, llvm::APInt(SizeTySize, I), | ||
SizeTy, SourceLocation()); | ||
ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr( | ||
E, E->getBeginLoc(), Idx, E->getEndLoc()); | ||
if (ElExpr.isInvalid()) | ||
return false; | ||
if (!BuildInitializerList(S, Ctx, Res.get(), List, DestTypes)) | ||
if (!buildInitializerListImpl(ElExpr.get())) | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
if (auto *RTy = Ty->getAs<RecordType>()) { | ||
llvm::SmallVector<const RecordType *> RecordTypes; | ||
RecordTypes.push_back(RTy); | ||
while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) { | ||
CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl(); | ||
assert(D->getNumBases() == 1 && | ||
"HLSL doesn't support multiple inheritance"); | ||
RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>()); | ||
} | ||
while (!RecordTypes.empty()) { | ||
const RecordType *RT = RecordTypes.pop_back_val(); | ||
for (auto *FD : RT->getDecl()->fields()) { | ||
DeclAccessPair Found = DeclAccessPair::make(FD, FD->getAccess()); | ||
DeclarationNameInfo NameInfo(FD->getDeclName(), E->getBeginLoc()); | ||
ExprResult Res = S.BuildFieldReferenceExpr( | ||
E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo); | ||
if (Res.isInvalid()) | ||
return false; | ||
if (!buildInitializerListImpl(Res.get())) | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
return true; | ||
} | ||
|
||
static Expr *GenerateInitLists(ASTContext &Ctx, QualType Ty, | ||
llvm::SmallVectorImpl<Expr *>::iterator &It) { | ||
if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType())) { | ||
return *(It++); | ||
} | ||
llvm::SmallVector<Expr *> Inits; | ||
assert(!isa<MatrixType>(Ty) && "Matrix types not yet supported in HLSL"); | ||
Ty = Ty.getDesugaredType(Ctx); | ||
if (Ty->isVectorType() || Ty->isConstantArrayType()) { | ||
QualType ElTy; | ||
uint64_t Size = 0; | ||
if (auto *ATy = Ty->getAs<VectorType>()) { | ||
ElTy = ATy->getElementType(); | ||
Size = ATy->getNumElements(); | ||
} else { | ||
auto *VTy = cast<ConstantArrayType>(Ty.getTypePtr()); | ||
ElTy = VTy->getElementType(); | ||
Size = VTy->getZExtSize(); | ||
} | ||
for (uint64_t I = 0; I < Size; ++I) | ||
Inits.push_back(GenerateInitLists(Ctx, ElTy, It)); | ||
} | ||
if (auto *RTy = Ty->getAs<RecordType>()) { | ||
llvm::SmallVector<const RecordType *> RecordTypes; | ||
RecordTypes.push_back(RTy); | ||
while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) { | ||
CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl(); | ||
assert(D->getNumBases() == 1 && | ||
"HLSL doesn't support multiple inheritance"); | ||
RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>()); | ||
} | ||
while (!RecordTypes.empty()) { | ||
const RecordType *RT = RecordTypes.pop_back_val(); | ||
for (auto *FD : RT->getDecl()->fields()) { | ||
Inits.push_back(GenerateInitLists(Ctx, FD->getType(), It)); | ||
Expr *generateInitListsImpl(QualType Ty) { | ||
assert(ArgIt != ArgExprs.end() && "Something is off in iteration!"); | ||
if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType())) | ||
return *(ArgIt++); | ||
|
||
llvm::SmallVector<Expr *> Inits; | ||
assert(!isa<MatrixType>(Ty) && "Matrix types not yet supported in HLSL"); | ||
Ty = Ty.getDesugaredType(Ctx); | ||
if (Ty->isVectorType() || Ty->isConstantArrayType()) { | ||
QualType ElTy; | ||
uint64_t Size = 0; | ||
if (auto *ATy = Ty->getAs<VectorType>()) { | ||
ElTy = ATy->getElementType(); | ||
Size = ATy->getNumElements(); | ||
} else { | ||
auto *VTy = cast<ConstantArrayType>(Ty.getTypePtr()); | ||
ElTy = VTy->getElementType(); | ||
Size = VTy->getZExtSize(); | ||
} | ||
for (uint64_t I = 0; I < Size; ++I) | ||
Inits.push_back(generateInitListsImpl(ElTy)); | ||
} | ||
if (auto *RTy = Ty->getAs<RecordType>()) { | ||
llvm::SmallVector<const RecordType *> RecordTypes; | ||
RecordTypes.push_back(RTy); | ||
while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) { | ||
CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl(); | ||
assert(D->getNumBases() == 1 && | ||
"HLSL doesn't support multiple inheritance"); | ||
RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>()); | ||
} | ||
while (!RecordTypes.empty()) { | ||
const RecordType *RT = RecordTypes.pop_back_val(); | ||
for (auto *FD : RT->getDecl()->fields()) { | ||
Inits.push_back(generateInitListsImpl(FD->getType())); | ||
} | ||
} | ||
} | ||
auto *NewInit = new (Ctx) InitListExpr(Ctx, Inits.front()->getBeginLoc(), | ||
Inits, Inits.back()->getEndLoc()); | ||
NewInit->setType(Ty); | ||
return NewInit; | ||
} | ||
auto *NewInit = new (Ctx) InitListExpr(Ctx, Inits.front()->getBeginLoc(), | ||
Inits, Inits.back()->getEndLoc()); | ||
NewInit->setType(Ty); | ||
return NewInit; | ||
} | ||
|
||
bool SemaHLSL::TransformInitList(const InitializedEntity &Entity, | ||
const InitializationKind &Kind, | ||
public: | ||
llvm::SmallVector<QualType, 16> DestTypes; | ||
llvm::SmallVector<Expr *, 16> ArgExprs; | ||
InitListTransformer(Sema &SemaRef, const InitializedEntity &Entity) | ||
: S(SemaRef), Ctx(SemaRef.getASTContext()), | ||
Wrap(Entity.getType()->isIncompleteArrayType()) { | ||
InitTy = Entity.getType().getNonReferenceType(); | ||
// When we're generating initializer lists for incomplete array types we | ||
// need to wrap around both when building the initializers and when | ||
// generating the final initializer lists. | ||
if (Wrap) { | ||
assert(InitTy->isIncompleteArrayType()); | ||
const IncompleteArrayType *IAT = Ctx.getAsIncompleteArrayType(InitTy); | ||
InitTy = IAT->getElementType(); | ||
} | ||
BuildFlattenedTypeList(InitTy, DestTypes); | ||
DstIt = DestTypes.begin(); | ||
} | ||
|
||
bool buildInitializerList(Expr *E) { return buildInitializerListImpl(E); } | ||
|
||
Expr *generateInitLists() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this function is always meant to be called after 'buildInitializerList'? Should any asserts related to this be added? |
||
assert(!ArgExprs.empty() && | ||
"Call buildInitializerList to generate argument expressions."); | ||
ArgIt = ArgExprs.begin(); | ||
if (!Wrap) | ||
return generateInitListsImpl(InitTy); | ||
llvm::SmallVector<Expr *> Inits; | ||
while (ArgIt != ArgExprs.end()) | ||
Inits.push_back(generateInitListsImpl(InitTy)); | ||
|
||
auto *NewInit = new (Ctx) InitListExpr(Ctx, Inits.front()->getBeginLoc(), | ||
Inits, Inits.back()->getEndLoc()); | ||
llvm::APInt ArySize(64, Inits.size()); | ||
NewInit->setType(Ctx.getConstantArrayType(InitTy, ArySize, nullptr, | ||
ArraySizeModifier::Normal, 0)); | ||
return NewInit; | ||
} | ||
}; | ||
} // namespace | ||
|
||
bool SemaHLSL::transformInitList(const InitializedEntity &Entity, | ||
InitListExpr *Init) { | ||
// If the initializer is a scalar, just return it. | ||
if (Init->getType()->isScalarType()) | ||
return true; | ||
ASTContext &Ctx = SemaRef.getASTContext(); | ||
llvm::SmallVector<QualType, 16> DestTypes; | ||
// An initializer list might be attempting to initialize a reference or | ||
// rvalue-reference. When checking the initializer we should look through the | ||
// reference. | ||
QualType InitTy = Entity.getType().getNonReferenceType(); | ||
BuildFlattenedTypeList(InitTy, DestTypes); | ||
InitListTransformer ILT(SemaRef, Entity); | ||
|
||
llvm::SmallVector<Expr *, 16> ArgExprs; | ||
for (unsigned I = 0; I < Init->getNumInits(); ++I) { | ||
Expr *E = Init->getInit(I); | ||
if (E->HasSideEffects(Ctx)) { | ||
|
@@ -3462,21 +3508,35 @@ bool SemaHLSL::TransformInitList(const InitializedEntity &Entity, | |
E->getObjectKind(), E); | ||
Init->setInit(I, E); | ||
} | ||
if (!BuildInitializerList(SemaRef, Ctx, E, ArgExprs, DestTypes)) | ||
if (!ILT.buildInitializerList(E)) | ||
return false; | ||
} | ||
size_t ExpectedSize = ILT.DestTypes.size(); | ||
size_t ActualSize = ILT.ArgExprs.size(); | ||
// 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 | ||
// complete the final element. | ||
if (Entity.getType()->isIncompleteArrayType()) | ||
ExpectedSize = | ||
((ActualSize + ExpectedSize - 1) / ExpectedSize) * ExpectedSize; | ||
|
||
if (DestTypes.size() != ArgExprs.size()) { | ||
int TooManyOrFew = ArgExprs.size() > DestTypes.size() ? 1 : 0; | ||
// An initializer list might be attempting to initialize a reference or | ||
// rvalue-reference. When checking the initializer we should look through | ||
// the reference. | ||
QualType InitTy = Entity.getType().getNonReferenceType(); | ||
if (InitTy.hasAddressSpace()) | ||
InitTy = SemaRef.getASTContext().removeAddrSpaceQualType(InitTy); | ||
if (ExpectedSize != ActualSize) { | ||
int TooManyOrFew = ActualSize > ExpectedSize ? 1 : 0; | ||
SemaRef.Diag(Init->getBeginLoc(), diag::err_hlsl_incorrect_num_initializers) | ||
<< TooManyOrFew << InitTy << DestTypes.size() << ArgExprs.size(); | ||
<< TooManyOrFew << InitTy << ExpectedSize << ActualSize; | ||
return false; | ||
} | ||
|
||
auto It = ArgExprs.begin(); | ||
// GenerateInitLists will always return an InitListExpr here, because the | ||
// generateInitListsImpl will always return an InitListExpr here, because the | ||
// scalar case is handled above. | ||
auto *NewInit = cast<InitListExpr>(GenerateInitLists(Ctx, InitTy, It)); | ||
auto *NewInit = cast<InitListExpr>(ILT.generateInitLists()); | ||
Init->resizeInits(Ctx, NewInit->getNumInits()); | ||
for (unsigned I = 0; I < NewInit->getNumInits(); ++I) | ||
Init->updateInit(Ctx, I, NewInit->getInit(I)); | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.