https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/200850
This makes sure SubstNonTypeTemplateParmExpr produced from non-specialization decls (Type alias templates and concepts) are correctly transformed. This makes the SubstNonTypeTemplateParmExpr store the parameter type directly, and uses that instead of relying on the AssociatedDecl. Fixes #191738 Fixes #196375 >From 5cbd57d30944e5c5c064cff5bc69a8ae624821ba Mon Sep 17 00:00:00 2001 From: Matheus Izvekov <[email protected]> Date: Fri, 29 May 2026 00:38:36 -0300 Subject: [PATCH] [clang] fix transformation of SubstNonTypeTemplateParmExpr nodes from typealiases and concepts This makes sure SubstNonTypeTemplateParmExpr produced from non-specialization decls (Type alias templates and concepts) are correctly transformed. This makes the SubstNonTypeTemplateParmExpr store the parameter type directly, and uses that instead of relying on the AssociatedDecl. Fixes #191738 Fixes #196375 --- clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/AST/Expr.h | 6 +++ clang/include/clang/AST/ExprCXX.h | 26 +++++----- clang/include/clang/AST/Stmt.h | 2 + clang/include/clang/Sema/Sema.h | 19 ++++---- clang/lib/AST/ASTImporter.cpp | 5 +- clang/lib/AST/Expr.cpp | 1 + clang/lib/AST/ExprCXX.cpp | 12 ----- clang/lib/AST/ItaniumMangle.cpp | 4 +- clang/lib/Sema/HLSLExternalSemaSource.cpp | 9 ++-- clang/lib/Sema/SemaTemplate.cpp | 53 +++++++-------------- clang/lib/Sema/SemaTemplateDeduction.cpp | 10 ++-- clang/lib/Sema/SemaTemplateInstantiate.cpp | 24 ++++++---- clang/lib/Sema/SemaType.cpp | 5 +- clang/lib/Sema/TreeTransform.h | 49 +++++++------------ clang/lib/Serialization/ASTReaderStmt.cpp | 9 ++-- clang/lib/Serialization/ASTWriterDecl.cpp | 4 +- clang/lib/Serialization/ASTWriterStmt.cpp | 5 +- clang/test/SemaTemplate/alias-templates.cpp | 11 +++++ clang/test/SemaTemplate/concepts.cpp | 8 ++++ 20 files changed, 127 insertions(+), 137 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 11cce36a0906c..a8c4726e57f58 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -623,6 +623,8 @@ Bug Fixes in This Version - Fixed an assertion failure in the serialized diagnostic printer when it is destroyed without calling ``finish()``. (#GH140433) - Fixed an assertion failure caused by error recovery while extending a nested name specifier with results from ordinary lookup. (#GH181470) - Fixed a crash when parsing ``#pragma clang attribute`` arguments for attributes that forbid arguments. (#GH182122) +- Fixed a bug in how Clang re-transforms expressions produced from substititions + from type aliases and concept specializations. (#GH191738) (#GH196375) - Fixed a bug with multiple-include optimization (MIOpt) state not being preserved in some cases during lexing, which could suppress header-guard mismatch diagnostics and interfere with include-guard optimization. (#GH180155) - Fixed a crash when normalizing constraints involving concept template parameters whose index coincided with non-concept template parameters in the same parameter mapping. - Fixed a crash caused by accessing dependent diagnostics of a non-dependent context. diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index b91bf4a5375fb..03612f14f28bb 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1496,6 +1496,12 @@ class DeclRefExpr final setDependence(computeDependence(this, Context)); } + /// This DeclRefExpr is parenthesized as if it were written in parentheses, + /// e.g., "(x)". This is used in replacement expressions produced from + /// canonical template arguments. + bool isParenthesized() const { return DeclRefExprBits.IsParenthesized; } + void setParenthesized(bool P) { DeclRefExprBits.IsParenthesized = P; } + static bool classof(const Stmt *T) { return T->getStmtClass() == DeclRefExprClass; } diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 0287797370397..36e5f8940228e 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4671,12 +4671,12 @@ class SubstNonTypeTemplateParmExpr : public Expr { /// The associated declaration and a flag indicating if it was a reference /// parameter. For class NTTPs, we can't determine that based on the value /// category alone. - llvm::PointerIntPair<Decl *, 1, bool> AssociatedDeclAndRef; + llvm::PointerIntPair<Decl *, 1, bool> AssociatedDeclAndFinal; + + QualType ParamType; unsigned Index : 15; unsigned PackIndex : 15; - LLVM_PREFERRED_TYPE(bool) - unsigned Final : 1; explicit SubstNonTypeTemplateParmExpr(EmptyShell Empty) : Expr(SubstNonTypeTemplateParmExprClass, Empty) {} @@ -4684,13 +4684,13 @@ class SubstNonTypeTemplateParmExpr : public Expr { public: SubstNonTypeTemplateParmExpr(QualType Ty, ExprValueKind ValueKind, SourceLocation Loc, Expr *Replacement, - Decl *AssociatedDecl, unsigned Index, - UnsignedOrNone PackIndex, bool RefParam, + Decl *AssociatedDecl, QualType ParamType, + unsigned Index, UnsignedOrNone PackIndex, bool Final) : Expr(SubstNonTypeTemplateParmExprClass, Ty, ValueKind, OK_Ordinary), - Replacement(Replacement), - AssociatedDeclAndRef(AssociatedDecl, RefParam), Index(Index), - PackIndex(PackIndex.toInternalRepresentation()), Final(Final) { + Replacement(Replacement), AssociatedDeclAndFinal(AssociatedDecl, Final), + ParamType(ParamType), Index(Index), + PackIndex(PackIndex.toInternalRepresentation()) { assert(AssociatedDecl != nullptr); SubstNonTypeTemplateParmExprBits.NameLoc = Loc; setDependence(computeDependence(this)); @@ -4706,7 +4706,9 @@ class SubstNonTypeTemplateParmExpr : public Expr { /// A template-like entity which owns the whole pattern being substituted. /// This will own a set of template parameters. - Decl *getAssociatedDecl() const { return AssociatedDeclAndRef.getPointer(); } + Decl *getAssociatedDecl() const { + return AssociatedDeclAndFinal.getPointer(); + } /// Returns the index of the replaced parameter in the associated declaration. /// This should match the result of `getParameter()->getIndex()`. @@ -4718,14 +4720,12 @@ class SubstNonTypeTemplateParmExpr : public Expr { // This substitution is Final, which means the substitution is fully // sugared: it doesn't need to be resugared later. - bool getFinal() const { return Final; } + bool getFinal() const { return AssociatedDeclAndFinal.getInt(); } NonTypeTemplateParmDecl *getParameter() const; - bool isReferenceParameter() const { return AssociatedDeclAndRef.getInt(); } - /// Determine the substituted type of the template parameter. - QualType getParameterType(const ASTContext &Ctx) const; + QualType getParameterType() const { return ParamType; } static bool classof(const Stmt *s) { return s->getStmtClass() == SubstNonTypeTemplateParmExprClass; diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index f07ba9205661b..151cf47ac6727 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -450,6 +450,8 @@ class alignas(void *) Stmt { unsigned NonOdrUseReason : 2; LLVM_PREFERRED_TYPE(bool) unsigned IsImmediateEscalating : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned IsParenthesized : 1; /// The location of the declaration name itself. SourceLocation Loc; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 10d489dc96a34..b8d760e7e0975 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11872,10 +11872,11 @@ class Sema final : public SemaBase { return Arg; } - ExprResult BuildSubstNonTypeTemplateParmExpr( - Decl *AssociatedDecl, const NonTypeTemplateParmDecl *NTTP, - SourceLocation loc, TemplateArgument Replacement, - UnsignedOrNone PackIndex, bool Final); + ExprResult + BuildSubstNonTypeTemplateParmExpr(Decl *AssociatedDecl, unsigned Index, + QualType ParamType, SourceLocation loc, + TemplateArgument Replacement, + UnsignedOrNone PackIndex, bool Final); /// Form a template name from a name that is syntactically required to name a /// template, either due to use of the 'template' keyword or because a name in @@ -12231,8 +12232,7 @@ class Sema final : public SemaBase { /// doesn't need to live too long. It would be useful if this function /// could return a temporary expression. ExprResult BuildExpressionFromDeclTemplateArgument( - const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc, - NamedDecl *TemplateParam = nullptr); + const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc); ExprResult BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, SourceLocation Loc); @@ -12644,10 +12644,9 @@ class Sema final : public SemaBase { /// /// \param Loc The source location to use for the resulting template /// argument. - TemplateArgumentLoc - getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, QualType NTTPType, - SourceLocation Loc, - NamedDecl *TemplateParam = nullptr); + TemplateArgumentLoc getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, + QualType NTTPType, + SourceLocation Loc); /// Get a template argument mapping the given template parameter to itself, /// e.g. for X in \c template<int X>, this would return an expression template diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 0d8243a6bd74b..13a0f25c10aaa 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -7803,6 +7803,7 @@ ExpectedStmt ASTNodeImporter::VisitDeclRefExpr(DeclRefExpr *E) { if (E->hadMultipleCandidates()) ToE->setHadMultipleCandidates(true); ToE->setIsImmediateEscalating(E->isImmediateEscalating()); + ToE->setParenthesized(E->isParenthesized()); return ToE; } @@ -9215,14 +9216,14 @@ ExpectedStmt ASTNodeImporter::VisitSubstNonTypeTemplateParmExpr( auto ToType = importChecked(Err, E->getType()); auto ToNameLoc = importChecked(Err, E->getNameLoc()); auto ToAssociatedDecl = importChecked(Err, E->getAssociatedDecl()); + auto ToParamType = importChecked(Err, E->getParameterType()); auto ToReplacement = importChecked(Err, E->getReplacement()); if (Err) return std::move(Err); return new (Importer.getToContext()) SubstNonTypeTemplateParmExpr( ToType, E->getValueKind(), ToNameLoc, ToReplacement, ToAssociatedDecl, - E->getIndex(), E->getPackIndex(), E->isReferenceParameter(), - E->getFinal()); + ToParamType, E->getIndex(), E->getPackIndex(), E->getFinal()); } ExpectedStmt ASTNodeImporter::VisitTypeTraitExpr(TypeTraitExpr *E) { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 90747be4208e1..406de77321cbc 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -486,6 +486,7 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx, TemplateKWLoc); } DeclRefExprBits.IsImmediateEscalating = false; + DeclRefExprBits.IsParenthesized = false; DeclRefExprBits.HadMultipleCandidates = 0; setDependence(computeDependence(this, Ctx)); } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 40e129d03dcea..960691219be6d 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1770,18 +1770,6 @@ PackIndexingExpr::CreateDeserialized(ASTContext &Context, return new (Storage) PackIndexingExpr(EmptyShell{}); } -QualType SubstNonTypeTemplateParmExpr::getParameterType( - const ASTContext &Context) const { - // Note that, for a class type NTTP, we will have an lvalue of type 'const - // T', so we can't just compute this from the type and value category. - - QualType Type = getType(); - - if (isReferenceParameter()) - return Context.getLValueReferenceType(Type); - return Type.getUnqualifiedType(); -} - SubstNonTypeTemplateParmPackExpr::SubstNonTypeTemplateParmPackExpr( QualType T, ExprValueKind ValueKind, SourceLocation NameLoc, const TemplateArgument &ArgPack, Decl *AssociatedDecl, unsigned Index, diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 1cb6fa05f22ac..85a01c6f4f727 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -5141,9 +5141,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, auto *SNTTPE = cast<SubstNonTypeTemplateParmExpr>(E); if (auto *CE = dyn_cast<ConstantExpr>(SNTTPE->getReplacement())) { // Pull out the constant value and mangle it as a template argument. - QualType ParamType = SNTTPE->getParameterType(Context.getASTContext()); assert(CE->hasAPValueResult() && "expected the NTTP to have an APValue"); - mangleValueInTemplateArg(ParamType, CE->getAPValueResult(), false, + mangleValueInTemplateArg(SNTTPE->getParameterType(), + CE->getAPValueResult(), false, /*NeedExactType=*/true); break; } diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index ae61b590a1f71..f9fb7745721fe 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -89,9 +89,8 @@ void HLSLExternalSemaSource::defineHLSLVectorAlias() { llvm::APInt Val(AST.getIntWidth(AST.IntTy), 4); TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy, /*IsDefaulted=*/true); - SizeParam->setDefaultArgument( - AST, SemaPtr->getTrivialTemplateArgumentLoc(Default, AST.IntTy, - SourceLocation(), SizeParam)); + SizeParam->setDefaultArgument(AST, SemaPtr->getTrivialTemplateArgumentLoc( + Default, AST.IntTy, SourceLocation())); TemplateParams.emplace_back(SizeParam); auto *ParamList = @@ -146,7 +145,7 @@ void HLSLExternalSemaSource::defineHLSLMatrixAlias() { /*IsDefaulted=*/true); RowsParam->setDefaultArgument( AST, SemaPtr->getTrivialTemplateArgumentLoc(RDefault, AST.IntTy, - SourceLocation(), RowsParam)); + SourceLocation())); TemplateParams.emplace_back(RowsParam); auto *ColsParam = NonTypeTemplateParmDecl::Create( @@ -158,7 +157,7 @@ void HLSLExternalSemaSource::defineHLSLMatrixAlias() { /*IsDefaulted=*/true); ColsParam->setDefaultArgument( AST, SemaPtr->getTrivialTemplateArgumentLoc(CDefault, AST.IntTy, - SourceLocation(), ColsParam)); + SourceLocation())); TemplateParams.emplace_back(ColsParam); const unsigned MaxMatDim = SemaPtr->getLangOpts().MaxMatrixDimension; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index d478b723f6e66..1e58184f7b8a7 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -773,38 +773,28 @@ Sema::BuildDependentDeclRefExpr(const CXXScopeSpec &SS, TemplateArgs); } -ExprResult Sema::BuildSubstNonTypeTemplateParmExpr( - Decl *AssociatedDecl, const NonTypeTemplateParmDecl *NTTP, - SourceLocation Loc, TemplateArgument Arg, UnsignedOrNone PackIndex, - bool Final) { +ExprResult +Sema::BuildSubstNonTypeTemplateParmExpr(Decl *AssociatedDecl, unsigned Index, + QualType ParamType, SourceLocation Loc, + TemplateArgument Arg, + UnsignedOrNone PackIndex, bool Final) { // The template argument itself might be an expression, in which case we just // return that expression. This happens when substituting into an alias // template. Expr *Replacement; - bool refParam = true; if (Arg.getKind() == TemplateArgument::Expression) { Replacement = Arg.getAsExpr(); - refParam = Replacement->isLValue(); - if (refParam && Replacement->getType()->isRecordType()) { - QualType ParamType = - NTTP->isExpandedParameterPack() - ? NTTP->getExpansionType(*SemaRef.ArgPackSubstIndex) - : NTTP->getType(); - if (const auto *PET = dyn_cast<PackExpansionType>(ParamType)) - ParamType = PET->getPattern(); - refParam = ParamType->isReferenceType(); - } } else { ExprResult result = SemaRef.BuildExpressionFromNonTypeTemplateArgument(Arg, Loc); + // FIXME: Should this ever fail? if (result.isInvalid()) return ExprError(); Replacement = result.get(); - refParam = Arg.getNonTypeTemplateArgumentType()->isReferenceType(); } return new (SemaRef.Context) SubstNonTypeTemplateParmExpr( Replacement->getType(), Replacement->getValueKind(), Loc, Replacement, - AssociatedDecl, NTTP->getIndex(), PackIndex, refParam, Final); + AssociatedDecl, ParamType, Index, PackIndex, Final); } bool Sema::DiagnoseUninstantiableTemplate(SourceLocation PointOfInstantiation, @@ -7939,8 +7929,7 @@ void Sema::NoteTemplateParameterLocation(const NamedDecl &Decl) { /// parameter, produce an expression that properly refers to that /// declaration. ExprResult Sema::BuildExpressionFromDeclTemplateArgument( - const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc, - NamedDecl *TemplateParam) { + const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc) { // C++ [temp.param]p8: // // A non-type template-parameter of type "array of T" or @@ -8005,18 +7994,10 @@ ExprResult Sema::BuildExpressionFromDeclTemplateArgument( } else { assert(ParamType->isReferenceType() && "unexpected type for decl template argument"); - if (NonTypeTemplateParmDecl *NTTP = - dyn_cast_if_present<NonTypeTemplateParmDecl>(TemplateParam)) { - QualType TemplateParamType = NTTP->getType(); - const AutoType *AT = TemplateParamType->getAs<AutoType>(); - if (AT && AT->isDecltypeAuto()) { - RefExpr = new (getASTContext()) SubstNonTypeTemplateParmExpr( - ParamType->getPointeeType(), RefExpr.get()->getValueKind(), - RefExpr.get()->getExprLoc(), RefExpr.get(), VD, NTTP->getIndex(), - /*PackIndex=*/std::nullopt, - /*RefParam=*/true, /*Final=*/true); - } - } + // If the parameter has reference type, wrap it in paretheses so that this + // expression will have the correct type under `decltype`. Avoid using a + // ParenExpr here, since that's not ignored on standards pre C++17. + cast<DeclRefExpr>(RefExpr.get())->setParenthesized(true); } // At this point we should have the right value category. @@ -8026,13 +8007,13 @@ ExprResult Sema::BuildExpressionFromDeclTemplateArgument( // The type of the template parameter can differ from the type of the // argument in various ways; convert it now if necessary. QualType DestExprType = ParamType.getNonLValueExprType(Context); - if (!Context.hasSameType(RefExpr.get()->getType(), DestExprType)) { + QualType SrcExprType = RefExpr.get()->getType(); + if (!Context.hasSameType(SrcExprType, DestExprType)) { CastKind CK; - if (Context.hasSimilarType(RefExpr.get()->getType(), DestExprType) || - IsFunctionConversion(RefExpr.get()->getType(), DestExprType)) { + if (Context.hasSimilarType(SrcExprType, DestExprType) || + IsFunctionConversion(SrcExprType, DestExprType)) { CK = CK_NoOp; - } else if (ParamType->isVoidPointerType() && - RefExpr.get()->getType()->isPointerType()) { + } else if (ParamType->isVoidPointerType() && SrcExprType->isPointerType()) { CK = CK_BitCast; } else { // FIXME: Pointers to members can need conversion derived-to-base or diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 1544fd56b82ae..26c397afdd6ef 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2860,8 +2860,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( TemplateArgumentLoc Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, - QualType NTTPType, SourceLocation Loc, - NamedDecl *TemplateParam) { + QualType NTTPType, SourceLocation Loc) { switch (Arg.getKind()) { case TemplateArgument::Null: llvm_unreachable("Can't get a NULL template argument here"); @@ -2873,8 +2872,7 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, case TemplateArgument::Declaration: { if (NTTPType.isNull()) NTTPType = Arg.getParamTypeForDecl(); - Expr *E = BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc, - TemplateParam) + Expr *E = BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc) .getAs<Expr>(); return TemplateArgumentLoc(TemplateArgument(E, /*IsCanonical=*/false), E); } @@ -2935,8 +2933,8 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, // Convert the deduced template argument into a template // argument that we can check, almost as if the user had written // the template argument explicitly. - TemplateArgumentLoc ArgLoc = S.getTrivialTemplateArgumentLoc( - Arg, QualType(), Info.getLocation(), Param); + TemplateArgumentLoc ArgLoc = + S.getTrivialTemplateArgumentLoc(Arg, QualType(), Info.getLocation()); SaveAndRestore _1(CTAI.MatchingTTP, false); SaveAndRestore _2(CTAI.StrictPackMatch, false); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 6e34261c3e61e..5309ec4430e9e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2176,6 +2176,15 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, return Arg.getAsExpr(); } + QualType ParamType = NTTP->isExpandedParameterPack() + ? NTTP->getExpansionType(*SemaRef.ArgPackSubstIndex) + : NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex + ? NTTP->getType().getNonPackExpansionType() + : NTTP->getType(); + ParamType = SemaRef.SubstType(ParamType, TemplateArgs, E->getLocation(), + NTTP->getDeclName()); + assert(!ParamType.isNull() && "Shouldn't substitute to an invalid type"); + auto [AssociatedDecl, Final] = TemplateArgs.getAssociatedDecl(NTTP->getDepth()); UnsignedOrNone PackIndex = std::nullopt; @@ -2187,24 +2196,19 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, // We have an argument pack, but we can't select a particular argument // out of it yet. Therefore, we'll build an expression to hold on to that // argument pack. - QualType TargetType = SemaRef.SubstType(NTTP->getType(), TemplateArgs, - E->getLocation(), - NTTP->getDeclName()); - if (TargetType.isNull()) - return ExprError(); - - QualType ExprType = TargetType.getNonLValueExprType(SemaRef.Context); - if (TargetType->isRecordType()) + QualType ExprType = ParamType.getNonLValueExprType(SemaRef.Context); + if (ParamType->isRecordType()) ExprType.addConst(); return new (SemaRef.Context) SubstNonTypeTemplateParmPackExpr( - ExprType, TargetType->isReferenceType() ? VK_LValue : VK_PRValue, + ExprType, ParamType->isReferenceType() ? VK_LValue : VK_PRValue, E->getLocation(), Arg, AssociatedDecl, NTTP->getPosition(), Final); } PackIndex = SemaRef.getPackIndex(Arg); Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg); } return SemaRef.BuildSubstNonTypeTemplateParmExpr( - AssociatedDecl, NTTP, E->getLocation(), Arg, PackIndex, Final); + AssociatedDecl, NTTP->getPosition(), ParamType, E->getLocation(), Arg, + PackIndex, Final); } const AnnotateAttr * diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 44ac4f6630690..da23d23e08161 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -10016,7 +10016,7 @@ QualType Sema::getDecltypeForExpr(Expr *E) { // parameter object. This rule makes no difference before C++20 so we apply // it unconditionally. if (const auto *SNTTPE = dyn_cast<SubstNonTypeTemplateParmExpr>(IDExpr)) - return SNTTPE->getParameterType(Context); + IDExpr = SNTTPE->getReplacement(); // - if e is an unparenthesized id-expression or an unparenthesized class // member access (5.2.5), decltype(e) is the type of the entity named @@ -10024,7 +10024,8 @@ QualType Sema::getDecltypeForExpr(Expr *E) { // functions, the program is ill-formed; // // We apply the same rules for Objective-C ivar and property references. - if (const auto *DRE = dyn_cast<DeclRefExpr>(IDExpr)) { + if (const auto *DRE = dyn_cast<DeclRefExpr>(IDExpr); + DRE && !DRE->isParenthesized()) { const ValueDecl *VD = DRE->getDecl(); QualType T = VD->getType(); return isa<TemplateParamObjectDecl>(VD) ? T.getUnqualifiedType() : T; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index c3bf71dbf10df..d77f4ecc54f5c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4332,12 +4332,12 @@ class TreeTransform { } ExprResult - RebuildSubstNonTypeTemplateParmExpr(Decl *AssociatedDecl, - const NonTypeTemplateParmDecl *NTTP, - SourceLocation Loc, TemplateArgument Arg, + RebuildSubstNonTypeTemplateParmExpr(Decl *AssociatedDecl, unsigned Index, + QualType ParamType, SourceLocation Loc, + TemplateArgument Arg, UnsignedOrNone PackIndex, bool Final) { return getSema().BuildSubstNonTypeTemplateParmExpr( - AssociatedDecl, NTTP, Loc, Arg, PackIndex, Final); + AssociatedDecl, Index, ParamType, Loc, Arg, PackIndex, Final); } OMPClause *RebuildOpenMPTransparentClause(Expr *ImpexType, @@ -16692,9 +16692,9 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmPackExpr( TemplateArgument Pack = E->getArgumentPack(); TemplateArgument Arg = SemaRef.getPackSubstitutedTemplateArgument(Pack); return getDerived().RebuildSubstNonTypeTemplateParmExpr( - E->getAssociatedDecl(), E->getParameterPack(), - E->getParameterPackLocation(), Arg, SemaRef.getPackIndex(Pack), - E->getFinal()); + E->getAssociatedDecl(), E->getParameterPack()->getPosition(), + E->getParameterPack()->getType(), E->getParameterPackLocation(), Arg, + SemaRef.getPackIndex(Pack), E->getFinal()); } template <typename Derived> @@ -16710,29 +16710,19 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr( if (!AssociatedDecl) return true; + QualType ParamType = TransformType(E->getParameterType()); + if (ParamType.isNull()) + return true; + if (Replacement.get() == OrigReplacement && - AssociatedDecl == E->getAssociatedDecl()) + AssociatedDecl == E->getAssociatedDecl() && + ParamType == E->getParameterType()) return E; - auto getParamAndType = [E](Decl *AssociatedDecl) - -> std::tuple<NonTypeTemplateParmDecl *, QualType> { - auto [PDecl, Arg] = - getReplacedTemplateParameter(AssociatedDecl, E->getIndex()); - auto *Param = cast<NonTypeTemplateParmDecl>(PDecl); - if (Arg.isNull()) - return {Param, Param->getType()}; - if (UnsignedOrNone PackIndex = E->getPackIndex()) - Arg = Arg.getPackAsArray()[*PackIndex]; - return {Param, Arg.getNonTypeTemplateArgumentType()}; - }; - - // If the replacement expression did not change, and the parameter type - // did not change, we can skip the semantic action because it would - // produce the same result anyway. - if (auto [Param, ParamType] = getParamAndType(AssociatedDecl); - !SemaRef.Context.hasSameType( - ParamType, std::get<1>(getParamAndType(E->getAssociatedDecl()))) || - Replacement.get() != OrigReplacement) { + if (Replacement.get() != OrigReplacement || + ParamType != E->getParameterType()) { + auto *Param = cast<NonTypeTemplateParmDecl>(std::get<0>( + getReplacedTemplateParameter(AssociatedDecl, E->getIndex()))); // When transforming the replacement expression previously, all Sema // specific annotations, such as implicit casts, are discarded. Calling the // corresponding sema action is necessary to recover those. Otherwise, @@ -16744,13 +16734,10 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr( /*StrictCheck=*/false, Sema::CTAK_Specified); if (Replacement.isInvalid()) return true; - } else { - // Otherwise, the same expression would have been produced. - Replacement = E->getReplacement(); } return getDerived().RebuildSubstNonTypeTemplateParmExpr( - AssociatedDecl, E->getParameter(), E->getNameLoc(), + AssociatedDecl, E->getIndex(), ParamType, E->getNameLoc(), TemplateArgument(Replacement.get(), /*IsCanonical=*/false), E->getPackIndex(), E->getFinal()); } diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 7e51ce8c0aca2..c794e964e516f 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -642,6 +642,7 @@ void ASTStmtReader::VisitDeclRefExpr(DeclRefExpr *E) { E->DeclRefExprBits.NonOdrUseReason = CurrentUnpackingBits->getNextBits(/*Width=*/2); E->DeclRefExprBits.IsImmediateEscalating = CurrentUnpackingBits->getNextBit(); + E->DeclRefExprBits.IsParenthesized = CurrentUnpackingBits->getNextBit(); E->DeclRefExprBits.HasFoundDecl = CurrentUnpackingBits->getNextBit(); E->DeclRefExprBits.HasQualifier = CurrentUnpackingBits->getNextBit(); E->DeclRefExprBits.HasTemplateKWAndArgsInfo = @@ -2286,11 +2287,11 @@ void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) { void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { VisitExpr(E); - E->AssociatedDeclAndRef.setPointer(readDeclAs<Decl>()); - E->AssociatedDeclAndRef.setInt(CurrentUnpackingBits->getNextBit()); + E->AssociatedDeclAndFinal.setPointer(readDeclAs<Decl>()); + E->AssociatedDeclAndFinal.setInt(CurrentUnpackingBits->getNextBit()); E->Index = CurrentUnpackingBits->getNextBits(/*Width=*/12); E->PackIndex = Record.readUnsignedOrNone().toInternalRepresentation(); - E->Final = CurrentUnpackingBits->getNextBit(); + E->ParamType = Record.readType(); E->SubstNonTypeTemplateParmExprBits.NameLoc = readSourceLocation(); E->Replacement = Record.readSubExpr(); } @@ -3259,7 +3260,7 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_DECL_REF: { BitsUnpacker DeclRefExprBits(Record[ASTStmtReader::NumExprFields]); - DeclRefExprBits.advance(5); + DeclRefExprBits.advance(6); bool HasFoundDecl = DeclRefExprBits.getNextBit(); bool HasQualifier = DeclRefExprBits.getNextBit(); bool HasTemplateKWAndArgsInfo = DeclRefExprBits.getNextBit(); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 7f5005aa666c7..5df71d9997376 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -2849,9 +2849,9 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Type // DeclRefExpr // Packing Bits: , HadMultipleCandidates, RefersToEnclosingVariableOrCapture, - // IsImmediateEscalating, NonOdrUseReason. + // IsImmediateEscalating, IsParenthesized, NonOdrUseReason. // GetDeclFound, HasQualifier and ExplicitTemplateArgs should be 0. - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 5)); + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 6)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // DeclRef Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Location DeclRefExprAbbrev = Stream.EmitAbbrev(std::move(Abv)); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index a7e815a1ef438..b93ca11c931f6 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -730,6 +730,7 @@ void ASTStmtWriter::VisitDeclRefExpr(DeclRefExpr *E) { CurrentPackingBits.addBit(E->refersToEnclosingVariableOrCapture()); CurrentPackingBits.addBits(E->isNonOdrUse(), /*Width=*/2); CurrentPackingBits.addBit(E->isImmediateEscalating()); + CurrentPackingBits.addBit(E->isParenthesized()); CurrentPackingBits.addBit(E->getDecl() != E->getFoundDecl()); CurrentPackingBits.addBit(E->hasQualifier()); CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo()); @@ -2298,10 +2299,10 @@ void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr( SubstNonTypeTemplateParmExpr *E) { VisitExpr(E); Record.AddDeclRef(E->getAssociatedDecl()); - CurrentPackingBits.addBit(E->isReferenceParameter()); + CurrentPackingBits.addBit(E->getFinal()); CurrentPackingBits.addBits(E->getIndex(), /*Width=*/12); Record.writeUnsignedOrNone(E->getPackIndex()); - CurrentPackingBits.addBit(E->getFinal()); + Record.AddTypeRef(E->getParameterType()); Record.AddSourceLocation(E->getNameLoc()); Record.AddStmt(E->getReplacement()); diff --git a/clang/test/SemaTemplate/alias-templates.cpp b/clang/test/SemaTemplate/alias-templates.cpp index 6f71a8c794723..31a2e530a0c7b 100644 --- a/clang/test/SemaTemplate/alias-templates.cpp +++ b/clang/test/SemaTemplate/alias-templates.cpp @@ -327,3 +327,14 @@ namespace ExtraneousTemplateParameterLists1 { template <class T> template <> using B = T; // expected-error@-1 {{extraneous template parameter list}} } // namespace ExtraneousTemplateParameterLists1 + +namespace GH191738 { + template <class T, T N> using A = decltype(N); + template <class...> struct B; + template <class... Ts> using C = B<A<int, sizeof...(Ts)>, Ts...>; + + template <class> struct D; + template <class T, class... Ts> struct D<C<T, Ts...>>; + template <class T> struct D<C<T>> {}; + template struct D<C<int>>; +} // namespace GH191738 diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 1a0834452cbdb..b427e6a4d2fa5 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1994,3 +1994,11 @@ template <> struct StorageTraits<int> { }; View zbi(GetVmo(string(""))); } + +namespace GH196375 { + template <class T, T V> concept Small = V <= 2; // expected-note {{because '4 <= 2' (4 <= 2) evaluated to false}} + template <int V> consteval bool f() // expected-note {{candidate template ignored: constraints not satisfied}} + requires(Small<int, V>) { return true; } // expected-note {{because 'Small<int, 4>' evaluated to false}} + static_assert(f<4>()); + // expected-error@-1 {{no matching function for call to 'f'}} +} // namespace GHGH196375 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
