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

Reply via email to