[clang] [clang] fix transformation of SubstNonTypeTemplateParmExpr nodes from typealiases and concepts (PR #200850)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 1 08:53:43 PDT 2026
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 <mizvekov at gmail.com>
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 at -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 at -1 {{no matching function for call to 'f'}}
+} // namespace GHGH196375
More information about the cfe-commits
mailing list