Skip to content

[Clang] Initial support for P2841 (Variable template and concept template parameters) #150823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Aug 4, 2025

Conversation

cor3ntin
Copy link
Contributor

@cor3ntin cor3ntin commented Jul 27, 2025

This is a first pass at implementing P2841R7.

The implementation is far from complete; however, I'm aiming to do that in chunks, to make our lives easier.

In particular, this does not implement

FTM, release notes, status page, etc, will be updated once the feature is more mature. Given the state of the feature, it is not yet allowed in older language modes.

Of note:

@cor3ntin cor3ntin changed the title [WIP] Variable template and concept template parameters Variable template and concept template parameters Jul 28, 2025
@cor3ntin cor3ntin marked this pull request as ready for review July 28, 2025 14:55
@cor3ntin cor3ntin requested a review from JDevlieghere as a code owner July 28, 2025 14:55
@llvmbot llvmbot added clang Clang issues not falling into any other category lldb clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules labels Jul 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-lldb

Author: Corentin Jabot (cor3ntin)

Changes

This is a first pass at implementing P2841R7.

The implementation is far from complete; however, I'm aiming to do that in chunks, to make our lives easier.

In particular, this does not implement

  • Subsumption
  • Mangling
  • Satisfaction checking is minimal as we should focus on #141776 first (note that I'm currently very stuck)

FTM, release notes, status page, etc, will be updated once the feature is more mature. Given the state of the feature, it is not yet allowed in older language modes.

Of note:

  • Mismatches between template template arguments and template template parameters are a bit wonky. This is addressed by #130603
  • We use UnresolvedLookupExpr to model template-id. While this is pre-existing, I have been wondering if we want to introduce a different OverloadExpr subclass for that. I did not make the change in this patch.

Patch is 148.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/150823.diff

51 Files Affected:

  • (modified) clang/include/clang/AST/ASTConcept.h (+9-7)
  • (modified) clang/include/clang/AST/ASTContext.h (+6-5)
  • (modified) clang/include/clang/AST/DeclTemplate.h (+35-12)
  • (modified) clang/include/clang/AST/ExprCXX.h (+45-2)
  • (modified) clang/include/clang/AST/ExprConcepts.h (+3-1)
  • (modified) clang/include/clang/AST/TemplateBase.h (+2)
  • (modified) clang/include/clang/AST/Type.h (+4-4)
  • (modified) clang/include/clang/AST/TypeLoc.h (+1-1)
  • (modified) clang/include/clang/AST/TypeProperties.td (+2-2)
  • (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+4)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+12-7)
  • (modified) clang/include/clang/Sema/Initialization.h (+3-3)
  • (modified) clang/include/clang/Sema/Ownership.h (+1)
  • (modified) clang/include/clang/Sema/ParsedTemplate.h (+107-105)
  • (modified) clang/include/clang/Sema/Sema.h (+15-5)
  • (modified) clang/include/clang/Sema/SemaInternal.h (+1)
  • (modified) clang/lib/AST/ASTConcept.cpp (+1-1)
  • (modified) clang/lib/AST/ASTContext.cpp (+15-11)
  • (modified) clang/lib/AST/ASTImporter.cpp (+2-2)
  • (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+2-1)
  • (modified) clang/lib/AST/ComputeDependence.cpp (+1-1)
  • (modified) clang/lib/AST/Decl.cpp (+3)
  • (modified) clang/lib/AST/DeclTemplate.cpp (+23-18)
  • (modified) clang/lib/AST/ExprCXX.cpp (+26-10)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+2-2)
  • (modified) clang/lib/AST/TemplateBase.cpp (+12)
  • (modified) clang/lib/AST/Type.cpp (+5-2)
  • (modified) clang/lib/Parse/ParseDecl.cpp (+1-1)
  • (modified) clang/lib/Parse/ParseTemplate.cpp (+60-25)
  • (modified) clang/lib/Parse/ParseTentative.cpp (+1-1)
  • (modified) clang/lib/Parse/Parser.cpp (+4-3)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+2-1)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+3-2)
  • (modified) clang/lib/Sema/SemaInit.cpp (+1-1)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+288-97)
  • (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+154-55)
  • (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+42-14)
  • (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (+10)
  • (modified) clang/lib/Sema/SemaType.cpp (+4-6)
  • (modified) clang/lib/Sema/TreeTransform.h (+74-5)
  • (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+1)
  • (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+5-3)
  • (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+1)
  • (modified) clang/test/CodeGenCXX/mangle-concept.cpp (+2-1)
  • (modified) clang/test/Parser/cxx-template-template-recovery.cpp (+6-6)
  • (modified) clang/test/Parser/cxx2a-concept-declaration.cpp (-5)
  • (added) clang/test/Parser/cxx2c-template-template-param.cpp (+79)
  • (added) clang/test/SemaCXX/cxx2c-template-template-param.cpp (+352)
  • (modified) clang/utils/TableGen/ClangBuiltinTemplatesEmitter.cpp (+2-1)
  • (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp (+6-5)
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index c8f6330a73bb1..7ccac4481b14c 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -27,6 +27,7 @@
 namespace clang {
 
 class ConceptDecl;
+class TemplateDecl;
 class Expr;
 class NamedDecl;
 struct PrintingPolicy;
@@ -123,6 +124,7 @@ struct ASTConstraintSatisfaction final :
 ///   template <std::derives_from<Expr> T> void dump();
 ///             ~~~~~~~~~~~~~~~~~~~~~~~ (in TemplateTypeParmDecl)
 class ConceptReference {
+protected:
   // \brief The optional nested name specifier used when naming the concept.
   NestedNameSpecifierLoc NestedNameSpec;
 
@@ -140,7 +142,7 @@ class ConceptReference {
   NamedDecl *FoundDecl;
 
   /// \brief The concept named.
-  ConceptDecl *NamedConcept;
+  TemplateDecl *NamedConcept;
 
   /// \brief The template argument list source info used to specialize the
   /// concept.
@@ -148,7 +150,7 @@ class ConceptReference {
 
   ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
                    DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
-                   ConceptDecl *NamedConcept,
+                   TemplateDecl *NamedConcept,
                    const ASTTemplateArgumentListInfo *ArgsAsWritten)
       : NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
         ConceptName(ConceptNameInfo), FoundDecl(FoundDecl),
@@ -158,7 +160,7 @@ class ConceptReference {
   static ConceptReference *
   Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
          SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
-         NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+         NamedDecl *FoundDecl, TemplateDecl *NamedConcept,
          const ASTTemplateArgumentListInfo *ArgsAsWritten);
 
   const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
@@ -197,9 +199,7 @@ class ConceptReference {
     return FoundDecl;
   }
 
-  ConceptDecl *getNamedConcept() const {
-    return NamedConcept;
-  }
+  TemplateDecl *getNamedConcept() const { return NamedConcept; }
 
   const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
     return ArgsAsWritten;
@@ -252,7 +252,9 @@ class TypeConstraint {
 
   // FIXME: Instead of using these concept related functions the callers should
   // directly work with the corresponding ConceptReference.
-  ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
+  TemplateDecl *getNamedConcept() const {
+    return ConceptRef->getNamedConcept();
+  }
 
   SourceLocation getConceptNameLoc() const {
     return ConceptRef->getConceptNameLoc();
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 0273109f8a698..8bdcff8b17fda 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1753,7 +1753,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   QualType
   getAutoTypeInternal(QualType DeducedType, AutoTypeKeyword Keyword,
                       bool IsDependent, bool IsPack = false,
-                      ConceptDecl *TypeConstraintConcept = nullptr,
+                      TemplateDecl *TypeConstraintConcept = nullptr,
                       ArrayRef<TemplateArgument> TypeConstraintArgs = {},
                       bool IsCanon = false) const;
 
@@ -1973,10 +1973,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                  UnaryTransformType::UTTKind UKind) const;
 
   /// C++11 deduced auto type.
-  QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
-                       bool IsDependent, bool IsPack = false,
-                       ConceptDecl *TypeConstraintConcept = nullptr,
-                       ArrayRef<TemplateArgument> TypeConstraintArgs ={}) const;
+  QualType
+  getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent,
+              bool IsPack = false,
+              TemplateDecl *TypeConstraintConcept = nullptr,
+              ArrayRef<TemplateArgument> TypeConstraintArgs = {}) const;
 
   /// C++11 deduction pattern for 'auto' type.
   QualType getAutoDeductType() const;
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 1ff6cc6fcb7d1..8e25f239fb97f 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -26,6 +26,7 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerIntPair.h"
@@ -1585,6 +1586,9 @@ class TemplateTemplateParmDecl final
       DefaultArgStorage<TemplateTemplateParmDecl, TemplateArgumentLoc *>;
   DefArgStorage DefaultArgument;
 
+  LLVM_PREFERRED_TYPE(TemplateNameKind)
+  unsigned ParameterKind : 3;
+
   /// Whether this template template parameter was declaration with
   /// the 'typename' keyword.
   ///
@@ -1607,13 +1611,16 @@ class TemplateTemplateParmDecl final
 
   TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D,
                            unsigned P, bool ParameterPack, IdentifierInfo *Id,
-                           bool Typename, TemplateParameterList *Params)
+                           TemplateNameKind ParameterKind, bool Typename,
+                           TemplateParameterList *Params)
       : TemplateDecl(TemplateTemplateParm, DC, L, Id, Params),
-        TemplateParmPosition(D, P), Typename(Typename),
-        ParameterPack(ParameterPack), ExpandedParameterPack(false) {}
+        TemplateParmPosition(D, P), ParameterKind(ParameterKind),
+        Typename(Typename), ParameterPack(ParameterPack),
+        ExpandedParameterPack(false) {}
 
   TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D,
-                           unsigned P, IdentifierInfo *Id, bool Typename,
+                           unsigned P, IdentifierInfo *Id,
+                           TemplateNameKind ParameterKind, bool Typename,
                            TemplateParameterList *Params,
                            ArrayRef<TemplateParameterList *> Expansions);
 
@@ -1624,15 +1631,16 @@ class TemplateTemplateParmDecl final
   friend class ASTDeclWriter;
   friend TrailingObjects;
 
-  static TemplateTemplateParmDecl *Create(const ASTContext &C, DeclContext *DC,
-                                          SourceLocation L, unsigned D,
-                                          unsigned P, bool ParameterPack,
-                                          IdentifierInfo *Id, bool Typename,
-                                          TemplateParameterList *Params);
   static TemplateTemplateParmDecl *
   Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
-         unsigned P, IdentifierInfo *Id, bool Typename,
-         TemplateParameterList *Params,
+         unsigned P, bool ParameterPack, IdentifierInfo *Id,
+         TemplateNameKind ParameterKind, bool Typename,
+         TemplateParameterList *Params);
+
+  static TemplateTemplateParmDecl *
+  Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
+         unsigned P, IdentifierInfo *Id, TemplateNameKind ParameterKind,
+         bool Typename, TemplateParameterList *Params,
          ArrayRef<TemplateParameterList *> Expansions);
 
   static TemplateTemplateParmDecl *CreateDeserialized(ASTContext &C,
@@ -1746,6 +1754,16 @@ class TemplateTemplateParmDecl final
     return SourceRange(getTemplateParameters()->getTemplateLoc(), End);
   }
 
+  TemplateNameKind kind() const {
+    return static_cast<TemplateNameKind>(ParameterKind);
+  }
+
+  bool isTypeConceptTemplateParam() const {
+    return kind() == TemplateNameKind::TNK_Concept_template &&
+           getTemplateParameters()->size() > 0 &&
+           isa<TemplateTypeParmDecl>(getTemplateParameters()->getParam(0));
+  }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == TemplateTemplateParm; }
@@ -3341,7 +3359,12 @@ inline TemplateDecl *getAsTypeTemplateDecl(Decl *D) {
   return TD && (isa<ClassTemplateDecl>(TD) ||
                 isa<ClassTemplatePartialSpecializationDecl>(TD) ||
                 isa<TypeAliasTemplateDecl>(TD) ||
-                isa<TemplateTemplateParmDecl>(TD))
+                [&]() {
+                  if (TemplateTemplateParmDecl *TTP =
+                          dyn_cast<TemplateTemplateParmDecl>(TD))
+                    return TTP->kind() == TNK_Type_template;
+                  return false;
+                }())
              ? TD
              : nullptr;
 }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a22c32241ac61..1979aa8a97b26 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -38,6 +38,7 @@
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "clang/Basic/TypeTraits.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/PointerUnion.h"
@@ -3257,7 +3258,49 @@ class OverloadExpr : public Expr {
   bool hasTemplateKeyword() const { return getTemplateKeywordLoc().isValid(); }
 
   /// Determines whether this expression had explicit template arguments.
-  bool hasExplicitTemplateArgs() const { return getLAngleLoc().isValid(); }
+  bool hasExplicitTemplateArgs() const {
+    if (!hasTemplateKWAndArgsInfo())
+      return false;
+    // FIXME: deduced function types can have "hidden" args and no <
+    // investigate that further, but ultimately maybe we want to model concepts
+    // reference with another kind of expression.
+    return (isConceptReference() || isVarDeclReference())
+               ? getTrailingASTTemplateKWAndArgsInfo()->NumTemplateArgs
+               : getLAngleLoc().isValid();
+  }
+
+  bool isConceptReference() const {
+    return getNumDecls() == 1 && [&]() {
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              getTrailingResults()->getDecl()))
+        return TTP->kind() == TNK_Concept_template;
+      if (isa<ConceptDecl>(getTrailingResults()->getDecl()))
+        return true;
+      return false;
+    }();
+  }
+
+  bool isVarDeclReference() const {
+    return getNumDecls() == 1 && [&]() {
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              getTrailingResults()->getDecl()))
+        return TTP->kind() == TNK_Var_template;
+      if (isa<VarTemplateDecl>(getTrailingResults()->getDecl()))
+        return true;
+      return false;
+    }();
+  }
+
+  TemplateDecl *getTemplateDecl() const {
+    assert(getNumDecls() == 1);
+    return dyn_cast_or_null<TemplateDecl>(getTrailingResults()->getDecl());
+  }
+
+  TemplateTemplateParmDecl *getTemplateTemplateDecl() const {
+    assert(getNumDecls() == 1);
+    return dyn_cast_or_null<TemplateTemplateParmDecl>(
+        getTrailingResults()->getDecl());
+  }
 
   TemplateArgumentLoc const *getTemplateArgs() const {
     if (!hasExplicitTemplateArgs())
@@ -4658,7 +4701,7 @@ class SubstNonTypeTemplateParmExpr : public Expr {
   // sugared: it doesn't need to be resugared later.
   bool getFinal() const { return Final; }
 
-  NonTypeTemplateParmDecl *getParameter() const;
+  NamedDecl *getParameter() const;
 
   bool isReferenceParameter() const { return AssociatedDeclAndRef.getInt(); }
 
diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h
index 7ab0c3e0b2769..4f162b6ffc8af 100644
--- a/clang/include/clang/AST/ExprConcepts.h
+++ b/clang/include/clang/AST/ExprConcepts.h
@@ -84,7 +84,9 @@ class ConceptSpecializationExpr final : public Expr {
 
   ConceptReference *getConceptReference() const { return ConceptRef; }
 
-  ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
+  ConceptDecl *getNamedConcept() const {
+    return cast<ConceptDecl>(ConceptRef->getNamedConcept());
+  }
 
   // FIXME: Several of the following functions can be removed. Instead the
   // caller can directly work with the ConceptReference.
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index b67036cae4261..eb384eae3faa7 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -316,6 +316,8 @@ class TemplateArgument {
   /// Determine whether this template argument is a pack expansion.
   bool isPackExpansion() const;
 
+  bool isConceptOrConceptTemplateParameter() const;
+
   /// Retrieve the type for a type template argument.
   QualType getAsType() const {
     assert(getKind() == Type && "Unexpected kind");
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 98810fbea726e..12dce309127e5 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -6762,10 +6762,10 @@ class DeducedType : public Type {
 class AutoType : public DeducedType {
   friend class ASTContext; // ASTContext creates these
 
-  ConceptDecl *TypeConstraintConcept;
+  TemplateDecl *TypeConstraintConcept;
 
   AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
-           TypeDependence ExtraDependence, QualType Canon, ConceptDecl *CD,
+           TypeDependence ExtraDependence, QualType Canon, TemplateDecl *CD,
            ArrayRef<TemplateArgument> TypeConstraintArgs);
 
 public:
@@ -6774,7 +6774,7 @@ class AutoType : public DeducedType {
             AutoTypeBits.NumArgs};
   }
 
-  ConceptDecl *getTypeConstraintConcept() const {
+  TemplateDecl *getTypeConstraintConcept() const {
     return TypeConstraintConcept;
   }
 
@@ -6797,7 +6797,7 @@ class AutoType : public DeducedType {
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context);
   static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                       QualType Deduced, AutoTypeKeyword Keyword,
-                      bool IsDependent, ConceptDecl *CD,
+                      bool IsDependent, TemplateDecl *CD,
                       ArrayRef<TemplateArgument> Arguments);
 
   static bool classof(const Type *T) {
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index be0bc896de3ea..52ef7ac54145e 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -2284,7 +2284,7 @@ class AutoTypeLoc
     return nullptr;
   }
 
-  ConceptDecl *getNamedConcept() const {
+  TemplateDecl *getNamedConcept() const {
     if (const auto *CR = getConceptReference())
       return CR->getNamedConcept();
     return nullptr;
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 3114d1180319a..3373e963038f1 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -495,9 +495,9 @@ let Class = AutoType in {
   def : Property<"keyword", AutoTypeKeyword> {
     let Read = [{ node->getKeyword() }];
   }
-  def : Property<"typeConstraintConcept", Optional<ConceptDeclRef>> {
+  def : Property<"typeConstraintConcept", Optional<TemplateDeclRef>> {
     let Read = [{ makeOptionalFromPointer(
-        const_cast<const ConceptDecl*>(node->getTypeConstraintConcept())) }];
+        node->getTypeConstraintConcept()) }];
   }
   def : Property<"typeConstraintArguments", Array<TemplateArgument>> {
     let Read = [{ node->getTypeConstraintArguments() }];
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 165f01514e2b1..0042afccba2c8 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -931,6 +931,10 @@ def err_missing_dependent_template_keyword : Error<
 def warn_missing_dependent_template_keyword : ExtWarn<
   "use 'template' keyword to treat '%0' as a dependent template name">;
 
+def err_cxx26_template_template_params
+    : Error<"%select{variable template|concept}0 template parameter is a C++2c "
+            "extension">;
+
 def ext_extern_template : Extension<
   "extern templates are a C++11 extension">, InGroup<CXX11>;
 def warn_cxx98_compat_extern_template : Warning<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 81cbd38d5cd24..7cfe145120ea4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5441,8 +5441,10 @@ def err_template_arg_must_be_expr : Error<
   "template argument for non-type template parameter must be an expression">;
 def err_template_arg_nontype_ambig : Error<
   "template argument for non-type template parameter is treated as function type %0">;
-def err_template_arg_must_be_template : Error<
-  "template argument for template template parameter must be a class template%select{| or type alias template}0">;
+def err_template_arg_must_be_template
+    : Error<"template argument for template template parameter must be a "
+            "%select{class template%select{| or type alias template}1|variable "
+            "template|concept}0">;
 def ext_template_arg_local_type : ExtWarn<
   "template argument uses local type %0">, InGroup<LocalTypeTemplateArgs>;
 def ext_template_arg_unnamed_type : ExtWarn<
@@ -5457,11 +5459,14 @@ def note_template_unnamed_type_here : Note<
   "unnamed type used in template argument was declared here">;
 def err_template_arg_overload_type : Error<
   "template argument is the type of an unresolved overloaded function">;
-def err_template_arg_not_valid_template : Error<
-  "template argument does not refer to a class or alias template, or template "
-  "template parameter">;
-def note_template_arg_refers_here_func : Note<
-  "template argument refers to function template %0, here">;
+def err_template_arg_not_valid_template
+    : Error<"template argument does not refer to a %select{class or alias "
+            "template|variable template|concept}0, or template "
+            "template parameter">;
+
+def note_template_arg_refers_to_template_here
+    : Note<"template argument refers to a %select{function template|class "
+           "template|variable template|concept}0 %1, here">;
 def err_template_arg_template_params_mismatch : Error<
   "template template argument has different template parameters than its "
   "corresponding template template parameter">;
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index a1c156eed5394..e5ee4b4e48ccc 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -160,7 +160,7 @@ class alignas(8) InitializedEntity {
 
   struct VD {
     /// The VarDecl, FieldDecl, or BindingDecl being initialized.
-    ValueDecl *VariableOrMember;
+    NamedDecl *VariableOrMember;
 
     /// When Kind == EK_Member, whether this is an implicit member
     /// initialization in a copy or move constructor. These can perform array
@@ -291,8 +291,8 @@ class alignas(8) InitializedEntity {
   }
 
   /// Create the initialization entity for a template parameter.
-  static InitializedEntity
-  InitializeTemplateParameter(QualType T, NonTypeTemplateParmDecl *Param) {
+  static InitializedEntity InitializeTemplateParameter(QualType T,
+                                                       NamedDecl *Param) {
     InitializedEntity Entity;
     Entity.Kind = EK_TemplateParameter;
     Entity.Type = T;
diff --git a/clang/include/clang/Sema/Ownership.h b/clang/include/clang/Sema/Ownership.h
index 0752f5de7e334..b1520837f2f9d 100644
--- a/clang/include/clang/Sema/Ownership.h
+++ b/clang/include/clang/Sema/Ownership.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_SEMA_OWNERSHIP_H
 #define LLVM_CLANG_SEMA_OWNERSHIP_H
 
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/ArrayRef.h"
diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h
index 3a8050f9a0a3d..6628bb88a81b9 100644
--- a/clang/include/clang/Sema/ParsedTemplate.h
+++ b/clang/include/clang/Sema/ParsedTemplate.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 #define LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 
+#include "clang/AST/DeclT...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-clang

Author: Corentin Jabot (cor3ntin)

Changes

This is a first pass at implementing P2841R7.

The implementation is far from complete; however, I'm aiming to do that in chunks, to make our lives easier.

In particular, this does not implement

  • Subsumption
  • Mangling
  • Satisfaction checking is minimal as we should focus on #141776 first (note that I'm currently very stuck)

FTM, release notes, status page, etc, will be updated once the feature is more mature. Given the state of the feature, it is not yet allowed in older language modes.

Of note:

  • Mismatches between template template arguments and template template parameters are a bit wonky. This is addressed by #130603
  • We use UnresolvedLookupExpr to model template-id. While this is pre-existing, I have been wondering if we want to introduce a different OverloadExpr subclass for that. I did not make the change in this patch.

Patch is 148.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/150823.diff

51 Files Affected:

  • (modified) clang/include/clang/AST/ASTConcept.h (+9-7)
  • (modified) clang/include/clang/AST/ASTContext.h (+6-5)
  • (modified) clang/include/clang/AST/DeclTemplate.h (+35-12)
  • (modified) clang/include/clang/AST/ExprCXX.h (+45-2)
  • (modified) clang/include/clang/AST/ExprConcepts.h (+3-1)
  • (modified) clang/include/clang/AST/TemplateBase.h (+2)
  • (modified) clang/include/clang/AST/Type.h (+4-4)
  • (modified) clang/include/clang/AST/TypeLoc.h (+1-1)
  • (modified) clang/include/clang/AST/TypeProperties.td (+2-2)
  • (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+4)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+12-7)
  • (modified) clang/include/clang/Sema/Initialization.h (+3-3)
  • (modified) clang/include/clang/Sema/Ownership.h (+1)
  • (modified) clang/include/clang/Sema/ParsedTemplate.h (+107-105)
  • (modified) clang/include/clang/Sema/Sema.h (+15-5)
  • (modified) clang/include/clang/Sema/SemaInternal.h (+1)
  • (modified) clang/lib/AST/ASTConcept.cpp (+1-1)
  • (modified) clang/lib/AST/ASTContext.cpp (+15-11)
  • (modified) clang/lib/AST/ASTImporter.cpp (+2-2)
  • (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+2-1)
  • (modified) clang/lib/AST/ComputeDependence.cpp (+1-1)
  • (modified) clang/lib/AST/Decl.cpp (+3)
  • (modified) clang/lib/AST/DeclTemplate.cpp (+23-18)
  • (modified) clang/lib/AST/ExprCXX.cpp (+26-10)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+2-2)
  • (modified) clang/lib/AST/TemplateBase.cpp (+12)
  • (modified) clang/lib/AST/Type.cpp (+5-2)
  • (modified) clang/lib/Parse/ParseDecl.cpp (+1-1)
  • (modified) clang/lib/Parse/ParseTemplate.cpp (+60-25)
  • (modified) clang/lib/Parse/ParseTentative.cpp (+1-1)
  • (modified) clang/lib/Parse/Parser.cpp (+4-3)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+2-1)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+3-2)
  • (modified) clang/lib/Sema/SemaInit.cpp (+1-1)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+288-97)
  • (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+154-55)
  • (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+42-14)
  • (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (+10)
  • (modified) clang/lib/Sema/SemaType.cpp (+4-6)
  • (modified) clang/lib/Sema/TreeTransform.h (+74-5)
  • (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+1)
  • (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+5-3)
  • (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+1)
  • (modified) clang/test/CodeGenCXX/mangle-concept.cpp (+2-1)
  • (modified) clang/test/Parser/cxx-template-template-recovery.cpp (+6-6)
  • (modified) clang/test/Parser/cxx2a-concept-declaration.cpp (-5)
  • (added) clang/test/Parser/cxx2c-template-template-param.cpp (+79)
  • (added) clang/test/SemaCXX/cxx2c-template-template-param.cpp (+352)
  • (modified) clang/utils/TableGen/ClangBuiltinTemplatesEmitter.cpp (+2-1)
  • (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp (+6-5)
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index c8f6330a73bb1..7ccac4481b14c 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -27,6 +27,7 @@
 namespace clang {
 
 class ConceptDecl;
+class TemplateDecl;
 class Expr;
 class NamedDecl;
 struct PrintingPolicy;
@@ -123,6 +124,7 @@ struct ASTConstraintSatisfaction final :
 ///   template <std::derives_from<Expr> T> void dump();
 ///             ~~~~~~~~~~~~~~~~~~~~~~~ (in TemplateTypeParmDecl)
 class ConceptReference {
+protected:
   // \brief The optional nested name specifier used when naming the concept.
   NestedNameSpecifierLoc NestedNameSpec;
 
@@ -140,7 +142,7 @@ class ConceptReference {
   NamedDecl *FoundDecl;
 
   /// \brief The concept named.
-  ConceptDecl *NamedConcept;
+  TemplateDecl *NamedConcept;
 
   /// \brief The template argument list source info used to specialize the
   /// concept.
@@ -148,7 +150,7 @@ class ConceptReference {
 
   ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
                    DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
-                   ConceptDecl *NamedConcept,
+                   TemplateDecl *NamedConcept,
                    const ASTTemplateArgumentListInfo *ArgsAsWritten)
       : NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
         ConceptName(ConceptNameInfo), FoundDecl(FoundDecl),
@@ -158,7 +160,7 @@ class ConceptReference {
   static ConceptReference *
   Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
          SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
-         NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+         NamedDecl *FoundDecl, TemplateDecl *NamedConcept,
          const ASTTemplateArgumentListInfo *ArgsAsWritten);
 
   const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
@@ -197,9 +199,7 @@ class ConceptReference {
     return FoundDecl;
   }
 
-  ConceptDecl *getNamedConcept() const {
-    return NamedConcept;
-  }
+  TemplateDecl *getNamedConcept() const { return NamedConcept; }
 
   const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
     return ArgsAsWritten;
@@ -252,7 +252,9 @@ class TypeConstraint {
 
   // FIXME: Instead of using these concept related functions the callers should
   // directly work with the corresponding ConceptReference.
-  ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
+  TemplateDecl *getNamedConcept() const {
+    return ConceptRef->getNamedConcept();
+  }
 
   SourceLocation getConceptNameLoc() const {
     return ConceptRef->getConceptNameLoc();
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 0273109f8a698..8bdcff8b17fda 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1753,7 +1753,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   QualType
   getAutoTypeInternal(QualType DeducedType, AutoTypeKeyword Keyword,
                       bool IsDependent, bool IsPack = false,
-                      ConceptDecl *TypeConstraintConcept = nullptr,
+                      TemplateDecl *TypeConstraintConcept = nullptr,
                       ArrayRef<TemplateArgument> TypeConstraintArgs = {},
                       bool IsCanon = false) const;
 
@@ -1973,10 +1973,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                  UnaryTransformType::UTTKind UKind) const;
 
   /// C++11 deduced auto type.
-  QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
-                       bool IsDependent, bool IsPack = false,
-                       ConceptDecl *TypeConstraintConcept = nullptr,
-                       ArrayRef<TemplateArgument> TypeConstraintArgs ={}) const;
+  QualType
+  getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent,
+              bool IsPack = false,
+              TemplateDecl *TypeConstraintConcept = nullptr,
+              ArrayRef<TemplateArgument> TypeConstraintArgs = {}) const;
 
   /// C++11 deduction pattern for 'auto' type.
   QualType getAutoDeductType() const;
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 1ff6cc6fcb7d1..8e25f239fb97f 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -26,6 +26,7 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerIntPair.h"
@@ -1585,6 +1586,9 @@ class TemplateTemplateParmDecl final
       DefaultArgStorage<TemplateTemplateParmDecl, TemplateArgumentLoc *>;
   DefArgStorage DefaultArgument;
 
+  LLVM_PREFERRED_TYPE(TemplateNameKind)
+  unsigned ParameterKind : 3;
+
   /// Whether this template template parameter was declaration with
   /// the 'typename' keyword.
   ///
@@ -1607,13 +1611,16 @@ class TemplateTemplateParmDecl final
 
   TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D,
                            unsigned P, bool ParameterPack, IdentifierInfo *Id,
-                           bool Typename, TemplateParameterList *Params)
+                           TemplateNameKind ParameterKind, bool Typename,
+                           TemplateParameterList *Params)
       : TemplateDecl(TemplateTemplateParm, DC, L, Id, Params),
-        TemplateParmPosition(D, P), Typename(Typename),
-        ParameterPack(ParameterPack), ExpandedParameterPack(false) {}
+        TemplateParmPosition(D, P), ParameterKind(ParameterKind),
+        Typename(Typename), ParameterPack(ParameterPack),
+        ExpandedParameterPack(false) {}
 
   TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D,
-                           unsigned P, IdentifierInfo *Id, bool Typename,
+                           unsigned P, IdentifierInfo *Id,
+                           TemplateNameKind ParameterKind, bool Typename,
                            TemplateParameterList *Params,
                            ArrayRef<TemplateParameterList *> Expansions);
 
@@ -1624,15 +1631,16 @@ class TemplateTemplateParmDecl final
   friend class ASTDeclWriter;
   friend TrailingObjects;
 
-  static TemplateTemplateParmDecl *Create(const ASTContext &C, DeclContext *DC,
-                                          SourceLocation L, unsigned D,
-                                          unsigned P, bool ParameterPack,
-                                          IdentifierInfo *Id, bool Typename,
-                                          TemplateParameterList *Params);
   static TemplateTemplateParmDecl *
   Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
-         unsigned P, IdentifierInfo *Id, bool Typename,
-         TemplateParameterList *Params,
+         unsigned P, bool ParameterPack, IdentifierInfo *Id,
+         TemplateNameKind ParameterKind, bool Typename,
+         TemplateParameterList *Params);
+
+  static TemplateTemplateParmDecl *
+  Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
+         unsigned P, IdentifierInfo *Id, TemplateNameKind ParameterKind,
+         bool Typename, TemplateParameterList *Params,
          ArrayRef<TemplateParameterList *> Expansions);
 
   static TemplateTemplateParmDecl *CreateDeserialized(ASTContext &C,
@@ -1746,6 +1754,16 @@ class TemplateTemplateParmDecl final
     return SourceRange(getTemplateParameters()->getTemplateLoc(), End);
   }
 
+  TemplateNameKind kind() const {
+    return static_cast<TemplateNameKind>(ParameterKind);
+  }
+
+  bool isTypeConceptTemplateParam() const {
+    return kind() == TemplateNameKind::TNK_Concept_template &&
+           getTemplateParameters()->size() > 0 &&
+           isa<TemplateTypeParmDecl>(getTemplateParameters()->getParam(0));
+  }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == TemplateTemplateParm; }
@@ -3341,7 +3359,12 @@ inline TemplateDecl *getAsTypeTemplateDecl(Decl *D) {
   return TD && (isa<ClassTemplateDecl>(TD) ||
                 isa<ClassTemplatePartialSpecializationDecl>(TD) ||
                 isa<TypeAliasTemplateDecl>(TD) ||
-                isa<TemplateTemplateParmDecl>(TD))
+                [&]() {
+                  if (TemplateTemplateParmDecl *TTP =
+                          dyn_cast<TemplateTemplateParmDecl>(TD))
+                    return TTP->kind() == TNK_Type_template;
+                  return false;
+                }())
              ? TD
              : nullptr;
 }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a22c32241ac61..1979aa8a97b26 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -38,6 +38,7 @@
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "clang/Basic/TypeTraits.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/PointerUnion.h"
@@ -3257,7 +3258,49 @@ class OverloadExpr : public Expr {
   bool hasTemplateKeyword() const { return getTemplateKeywordLoc().isValid(); }
 
   /// Determines whether this expression had explicit template arguments.
-  bool hasExplicitTemplateArgs() const { return getLAngleLoc().isValid(); }
+  bool hasExplicitTemplateArgs() const {
+    if (!hasTemplateKWAndArgsInfo())
+      return false;
+    // FIXME: deduced function types can have "hidden" args and no <
+    // investigate that further, but ultimately maybe we want to model concepts
+    // reference with another kind of expression.
+    return (isConceptReference() || isVarDeclReference())
+               ? getTrailingASTTemplateKWAndArgsInfo()->NumTemplateArgs
+               : getLAngleLoc().isValid();
+  }
+
+  bool isConceptReference() const {
+    return getNumDecls() == 1 && [&]() {
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              getTrailingResults()->getDecl()))
+        return TTP->kind() == TNK_Concept_template;
+      if (isa<ConceptDecl>(getTrailingResults()->getDecl()))
+        return true;
+      return false;
+    }();
+  }
+
+  bool isVarDeclReference() const {
+    return getNumDecls() == 1 && [&]() {
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              getTrailingResults()->getDecl()))
+        return TTP->kind() == TNK_Var_template;
+      if (isa<VarTemplateDecl>(getTrailingResults()->getDecl()))
+        return true;
+      return false;
+    }();
+  }
+
+  TemplateDecl *getTemplateDecl() const {
+    assert(getNumDecls() == 1);
+    return dyn_cast_or_null<TemplateDecl>(getTrailingResults()->getDecl());
+  }
+
+  TemplateTemplateParmDecl *getTemplateTemplateDecl() const {
+    assert(getNumDecls() == 1);
+    return dyn_cast_or_null<TemplateTemplateParmDecl>(
+        getTrailingResults()->getDecl());
+  }
 
   TemplateArgumentLoc const *getTemplateArgs() const {
     if (!hasExplicitTemplateArgs())
@@ -4658,7 +4701,7 @@ class SubstNonTypeTemplateParmExpr : public Expr {
   // sugared: it doesn't need to be resugared later.
   bool getFinal() const { return Final; }
 
-  NonTypeTemplateParmDecl *getParameter() const;
+  NamedDecl *getParameter() const;
 
   bool isReferenceParameter() const { return AssociatedDeclAndRef.getInt(); }
 
diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h
index 7ab0c3e0b2769..4f162b6ffc8af 100644
--- a/clang/include/clang/AST/ExprConcepts.h
+++ b/clang/include/clang/AST/ExprConcepts.h
@@ -84,7 +84,9 @@ class ConceptSpecializationExpr final : public Expr {
 
   ConceptReference *getConceptReference() const { return ConceptRef; }
 
-  ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
+  ConceptDecl *getNamedConcept() const {
+    return cast<ConceptDecl>(ConceptRef->getNamedConcept());
+  }
 
   // FIXME: Several of the following functions can be removed. Instead the
   // caller can directly work with the ConceptReference.
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index b67036cae4261..eb384eae3faa7 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -316,6 +316,8 @@ class TemplateArgument {
   /// Determine whether this template argument is a pack expansion.
   bool isPackExpansion() const;
 
+  bool isConceptOrConceptTemplateParameter() const;
+
   /// Retrieve the type for a type template argument.
   QualType getAsType() const {
     assert(getKind() == Type && "Unexpected kind");
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 98810fbea726e..12dce309127e5 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -6762,10 +6762,10 @@ class DeducedType : public Type {
 class AutoType : public DeducedType {
   friend class ASTContext; // ASTContext creates these
 
-  ConceptDecl *TypeConstraintConcept;
+  TemplateDecl *TypeConstraintConcept;
 
   AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
-           TypeDependence ExtraDependence, QualType Canon, ConceptDecl *CD,
+           TypeDependence ExtraDependence, QualType Canon, TemplateDecl *CD,
            ArrayRef<TemplateArgument> TypeConstraintArgs);
 
 public:
@@ -6774,7 +6774,7 @@ class AutoType : public DeducedType {
             AutoTypeBits.NumArgs};
   }
 
-  ConceptDecl *getTypeConstraintConcept() const {
+  TemplateDecl *getTypeConstraintConcept() const {
     return TypeConstraintConcept;
   }
 
@@ -6797,7 +6797,7 @@ class AutoType : public DeducedType {
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context);
   static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                       QualType Deduced, AutoTypeKeyword Keyword,
-                      bool IsDependent, ConceptDecl *CD,
+                      bool IsDependent, TemplateDecl *CD,
                       ArrayRef<TemplateArgument> Arguments);
 
   static bool classof(const Type *T) {
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index be0bc896de3ea..52ef7ac54145e 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -2284,7 +2284,7 @@ class AutoTypeLoc
     return nullptr;
   }
 
-  ConceptDecl *getNamedConcept() const {
+  TemplateDecl *getNamedConcept() const {
     if (const auto *CR = getConceptReference())
       return CR->getNamedConcept();
     return nullptr;
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 3114d1180319a..3373e963038f1 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -495,9 +495,9 @@ let Class = AutoType in {
   def : Property<"keyword", AutoTypeKeyword> {
     let Read = [{ node->getKeyword() }];
   }
-  def : Property<"typeConstraintConcept", Optional<ConceptDeclRef>> {
+  def : Property<"typeConstraintConcept", Optional<TemplateDeclRef>> {
     let Read = [{ makeOptionalFromPointer(
-        const_cast<const ConceptDecl*>(node->getTypeConstraintConcept())) }];
+        node->getTypeConstraintConcept()) }];
   }
   def : Property<"typeConstraintArguments", Array<TemplateArgument>> {
     let Read = [{ node->getTypeConstraintArguments() }];
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 165f01514e2b1..0042afccba2c8 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -931,6 +931,10 @@ def err_missing_dependent_template_keyword : Error<
 def warn_missing_dependent_template_keyword : ExtWarn<
   "use 'template' keyword to treat '%0' as a dependent template name">;
 
+def err_cxx26_template_template_params
+    : Error<"%select{variable template|concept}0 template parameter is a C++2c "
+            "extension">;
+
 def ext_extern_template : Extension<
   "extern templates are a C++11 extension">, InGroup<CXX11>;
 def warn_cxx98_compat_extern_template : Warning<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 81cbd38d5cd24..7cfe145120ea4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5441,8 +5441,10 @@ def err_template_arg_must_be_expr : Error<
   "template argument for non-type template parameter must be an expression">;
 def err_template_arg_nontype_ambig : Error<
   "template argument for non-type template parameter is treated as function type %0">;
-def err_template_arg_must_be_template : Error<
-  "template argument for template template parameter must be a class template%select{| or type alias template}0">;
+def err_template_arg_must_be_template
+    : Error<"template argument for template template parameter must be a "
+            "%select{class template%select{| or type alias template}1|variable "
+            "template|concept}0">;
 def ext_template_arg_local_type : ExtWarn<
   "template argument uses local type %0">, InGroup<LocalTypeTemplateArgs>;
 def ext_template_arg_unnamed_type : ExtWarn<
@@ -5457,11 +5459,14 @@ def note_template_unnamed_type_here : Note<
   "unnamed type used in template argument was declared here">;
 def err_template_arg_overload_type : Error<
   "template argument is the type of an unresolved overloaded function">;
-def err_template_arg_not_valid_template : Error<
-  "template argument does not refer to a class or alias template, or template "
-  "template parameter">;
-def note_template_arg_refers_here_func : Note<
-  "template argument refers to function template %0, here">;
+def err_template_arg_not_valid_template
+    : Error<"template argument does not refer to a %select{class or alias "
+            "template|variable template|concept}0, or template "
+            "template parameter">;
+
+def note_template_arg_refers_to_template_here
+    : Note<"template argument refers to a %select{function template|class "
+           "template|variable template|concept}0 %1, here">;
 def err_template_arg_template_params_mismatch : Error<
   "template template argument has different template parameters than its "
   "corresponding template template parameter">;
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index a1c156eed5394..e5ee4b4e48ccc 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -160,7 +160,7 @@ class alignas(8) InitializedEntity {
 
   struct VD {
     /// The VarDecl, FieldDecl, or BindingDecl being initialized.
-    ValueDecl *VariableOrMember;
+    NamedDecl *VariableOrMember;
 
     /// When Kind == EK_Member, whether this is an implicit member
     /// initialization in a copy or move constructor. These can perform array
@@ -291,8 +291,8 @@ class alignas(8) InitializedEntity {
   }
 
   /// Create the initialization entity for a template parameter.
-  static InitializedEntity
-  InitializeTemplateParameter(QualType T, NonTypeTemplateParmDecl *Param) {
+  static InitializedEntity InitializeTemplateParameter(QualType T,
+                                                       NamedDecl *Param) {
     InitializedEntity Entity;
     Entity.Kind = EK_TemplateParameter;
     Entity.Type = T;
diff --git a/clang/include/clang/Sema/Ownership.h b/clang/include/clang/Sema/Ownership.h
index 0752f5de7e334..b1520837f2f9d 100644
--- a/clang/include/clang/Sema/Ownership.h
+++ b/clang/include/clang/Sema/Ownership.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_SEMA_OWNERSHIP_H
 #define LLVM_CLANG_SEMA_OWNERSHIP_H
 
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/ArrayRef.h"
diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h
index 3a8050f9a0a3d..6628bb88a81b9 100644
--- a/clang/include/clang/Sema/ParsedTemplate.h
+++ b/clang/include/clang/Sema/ParsedTemplate.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 #define LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 
+#include "clang/AST/DeclT...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-clang-modules

Author: Corentin Jabot (cor3ntin)

Changes

This is a first pass at implementing P2841R7.

The implementation is far from complete; however, I'm aiming to do that in chunks, to make our lives easier.

In particular, this does not implement

  • Subsumption
  • Mangling
  • Satisfaction checking is minimal as we should focus on #141776 first (note that I'm currently very stuck)

FTM, release notes, status page, etc, will be updated once the feature is more mature. Given the state of the feature, it is not yet allowed in older language modes.

Of note:

  • Mismatches between template template arguments and template template parameters are a bit wonky. This is addressed by #130603
  • We use UnresolvedLookupExpr to model template-id. While this is pre-existing, I have been wondering if we want to introduce a different OverloadExpr subclass for that. I did not make the change in this patch.

Patch is 148.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/150823.diff

51 Files Affected:

  • (modified) clang/include/clang/AST/ASTConcept.h (+9-7)
  • (modified) clang/include/clang/AST/ASTContext.h (+6-5)
  • (modified) clang/include/clang/AST/DeclTemplate.h (+35-12)
  • (modified) clang/include/clang/AST/ExprCXX.h (+45-2)
  • (modified) clang/include/clang/AST/ExprConcepts.h (+3-1)
  • (modified) clang/include/clang/AST/TemplateBase.h (+2)
  • (modified) clang/include/clang/AST/Type.h (+4-4)
  • (modified) clang/include/clang/AST/TypeLoc.h (+1-1)
  • (modified) clang/include/clang/AST/TypeProperties.td (+2-2)
  • (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+4)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+12-7)
  • (modified) clang/include/clang/Sema/Initialization.h (+3-3)
  • (modified) clang/include/clang/Sema/Ownership.h (+1)
  • (modified) clang/include/clang/Sema/ParsedTemplate.h (+107-105)
  • (modified) clang/include/clang/Sema/Sema.h (+15-5)
  • (modified) clang/include/clang/Sema/SemaInternal.h (+1)
  • (modified) clang/lib/AST/ASTConcept.cpp (+1-1)
  • (modified) clang/lib/AST/ASTContext.cpp (+15-11)
  • (modified) clang/lib/AST/ASTImporter.cpp (+2-2)
  • (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+2-1)
  • (modified) clang/lib/AST/ComputeDependence.cpp (+1-1)
  • (modified) clang/lib/AST/Decl.cpp (+3)
  • (modified) clang/lib/AST/DeclTemplate.cpp (+23-18)
  • (modified) clang/lib/AST/ExprCXX.cpp (+26-10)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+2-2)
  • (modified) clang/lib/AST/TemplateBase.cpp (+12)
  • (modified) clang/lib/AST/Type.cpp (+5-2)
  • (modified) clang/lib/Parse/ParseDecl.cpp (+1-1)
  • (modified) clang/lib/Parse/ParseTemplate.cpp (+60-25)
  • (modified) clang/lib/Parse/ParseTentative.cpp (+1-1)
  • (modified) clang/lib/Parse/Parser.cpp (+4-3)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+2-1)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+3-2)
  • (modified) clang/lib/Sema/SemaInit.cpp (+1-1)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+288-97)
  • (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+154-55)
  • (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+42-14)
  • (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+3-3)
  • (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (+10)
  • (modified) clang/lib/Sema/SemaType.cpp (+4-6)
  • (modified) clang/lib/Sema/TreeTransform.h (+74-5)
  • (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+1)
  • (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+5-3)
  • (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+1)
  • (modified) clang/test/CodeGenCXX/mangle-concept.cpp (+2-1)
  • (modified) clang/test/Parser/cxx-template-template-recovery.cpp (+6-6)
  • (modified) clang/test/Parser/cxx2a-concept-declaration.cpp (-5)
  • (added) clang/test/Parser/cxx2c-template-template-param.cpp (+79)
  • (added) clang/test/SemaCXX/cxx2c-template-template-param.cpp (+352)
  • (modified) clang/utils/TableGen/ClangBuiltinTemplatesEmitter.cpp (+2-1)
  • (modified) lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp (+6-5)
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index c8f6330a73bb1..7ccac4481b14c 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -27,6 +27,7 @@
 namespace clang {
 
 class ConceptDecl;
+class TemplateDecl;
 class Expr;
 class NamedDecl;
 struct PrintingPolicy;
@@ -123,6 +124,7 @@ struct ASTConstraintSatisfaction final :
 ///   template <std::derives_from<Expr> T> void dump();
 ///             ~~~~~~~~~~~~~~~~~~~~~~~ (in TemplateTypeParmDecl)
 class ConceptReference {
+protected:
   // \brief The optional nested name specifier used when naming the concept.
   NestedNameSpecifierLoc NestedNameSpec;
 
@@ -140,7 +142,7 @@ class ConceptReference {
   NamedDecl *FoundDecl;
 
   /// \brief The concept named.
-  ConceptDecl *NamedConcept;
+  TemplateDecl *NamedConcept;
 
   /// \brief The template argument list source info used to specialize the
   /// concept.
@@ -148,7 +150,7 @@ class ConceptReference {
 
   ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
                    DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
-                   ConceptDecl *NamedConcept,
+                   TemplateDecl *NamedConcept,
                    const ASTTemplateArgumentListInfo *ArgsAsWritten)
       : NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
         ConceptName(ConceptNameInfo), FoundDecl(FoundDecl),
@@ -158,7 +160,7 @@ class ConceptReference {
   static ConceptReference *
   Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
          SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
-         NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+         NamedDecl *FoundDecl, TemplateDecl *NamedConcept,
          const ASTTemplateArgumentListInfo *ArgsAsWritten);
 
   const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
@@ -197,9 +199,7 @@ class ConceptReference {
     return FoundDecl;
   }
 
-  ConceptDecl *getNamedConcept() const {
-    return NamedConcept;
-  }
+  TemplateDecl *getNamedConcept() const { return NamedConcept; }
 
   const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
     return ArgsAsWritten;
@@ -252,7 +252,9 @@ class TypeConstraint {
 
   // FIXME: Instead of using these concept related functions the callers should
   // directly work with the corresponding ConceptReference.
-  ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
+  TemplateDecl *getNamedConcept() const {
+    return ConceptRef->getNamedConcept();
+  }
 
   SourceLocation getConceptNameLoc() const {
     return ConceptRef->getConceptNameLoc();
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 0273109f8a698..8bdcff8b17fda 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1753,7 +1753,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   QualType
   getAutoTypeInternal(QualType DeducedType, AutoTypeKeyword Keyword,
                       bool IsDependent, bool IsPack = false,
-                      ConceptDecl *TypeConstraintConcept = nullptr,
+                      TemplateDecl *TypeConstraintConcept = nullptr,
                       ArrayRef<TemplateArgument> TypeConstraintArgs = {},
                       bool IsCanon = false) const;
 
@@ -1973,10 +1973,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                  UnaryTransformType::UTTKind UKind) const;
 
   /// C++11 deduced auto type.
-  QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
-                       bool IsDependent, bool IsPack = false,
-                       ConceptDecl *TypeConstraintConcept = nullptr,
-                       ArrayRef<TemplateArgument> TypeConstraintArgs ={}) const;
+  QualType
+  getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent,
+              bool IsPack = false,
+              TemplateDecl *TypeConstraintConcept = nullptr,
+              ArrayRef<TemplateArgument> TypeConstraintArgs = {}) const;
 
   /// C++11 deduction pattern for 'auto' type.
   QualType getAutoDeductType() const;
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 1ff6cc6fcb7d1..8e25f239fb97f 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -26,6 +26,7 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerIntPair.h"
@@ -1585,6 +1586,9 @@ class TemplateTemplateParmDecl final
       DefaultArgStorage<TemplateTemplateParmDecl, TemplateArgumentLoc *>;
   DefArgStorage DefaultArgument;
 
+  LLVM_PREFERRED_TYPE(TemplateNameKind)
+  unsigned ParameterKind : 3;
+
   /// Whether this template template parameter was declaration with
   /// the 'typename' keyword.
   ///
@@ -1607,13 +1611,16 @@ class TemplateTemplateParmDecl final
 
   TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D,
                            unsigned P, bool ParameterPack, IdentifierInfo *Id,
-                           bool Typename, TemplateParameterList *Params)
+                           TemplateNameKind ParameterKind, bool Typename,
+                           TemplateParameterList *Params)
       : TemplateDecl(TemplateTemplateParm, DC, L, Id, Params),
-        TemplateParmPosition(D, P), Typename(Typename),
-        ParameterPack(ParameterPack), ExpandedParameterPack(false) {}
+        TemplateParmPosition(D, P), ParameterKind(ParameterKind),
+        Typename(Typename), ParameterPack(ParameterPack),
+        ExpandedParameterPack(false) {}
 
   TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D,
-                           unsigned P, IdentifierInfo *Id, bool Typename,
+                           unsigned P, IdentifierInfo *Id,
+                           TemplateNameKind ParameterKind, bool Typename,
                            TemplateParameterList *Params,
                            ArrayRef<TemplateParameterList *> Expansions);
 
@@ -1624,15 +1631,16 @@ class TemplateTemplateParmDecl final
   friend class ASTDeclWriter;
   friend TrailingObjects;
 
-  static TemplateTemplateParmDecl *Create(const ASTContext &C, DeclContext *DC,
-                                          SourceLocation L, unsigned D,
-                                          unsigned P, bool ParameterPack,
-                                          IdentifierInfo *Id, bool Typename,
-                                          TemplateParameterList *Params);
   static TemplateTemplateParmDecl *
   Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
-         unsigned P, IdentifierInfo *Id, bool Typename,
-         TemplateParameterList *Params,
+         unsigned P, bool ParameterPack, IdentifierInfo *Id,
+         TemplateNameKind ParameterKind, bool Typename,
+         TemplateParameterList *Params);
+
+  static TemplateTemplateParmDecl *
+  Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
+         unsigned P, IdentifierInfo *Id, TemplateNameKind ParameterKind,
+         bool Typename, TemplateParameterList *Params,
          ArrayRef<TemplateParameterList *> Expansions);
 
   static TemplateTemplateParmDecl *CreateDeserialized(ASTContext &C,
@@ -1746,6 +1754,16 @@ class TemplateTemplateParmDecl final
     return SourceRange(getTemplateParameters()->getTemplateLoc(), End);
   }
 
+  TemplateNameKind kind() const {
+    return static_cast<TemplateNameKind>(ParameterKind);
+  }
+
+  bool isTypeConceptTemplateParam() const {
+    return kind() == TemplateNameKind::TNK_Concept_template &&
+           getTemplateParameters()->size() > 0 &&
+           isa<TemplateTypeParmDecl>(getTemplateParameters()->getParam(0));
+  }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == TemplateTemplateParm; }
@@ -3341,7 +3359,12 @@ inline TemplateDecl *getAsTypeTemplateDecl(Decl *D) {
   return TD && (isa<ClassTemplateDecl>(TD) ||
                 isa<ClassTemplatePartialSpecializationDecl>(TD) ||
                 isa<TypeAliasTemplateDecl>(TD) ||
-                isa<TemplateTemplateParmDecl>(TD))
+                [&]() {
+                  if (TemplateTemplateParmDecl *TTP =
+                          dyn_cast<TemplateTemplateParmDecl>(TD))
+                    return TTP->kind() == TNK_Type_template;
+                  return false;
+                }())
              ? TD
              : nullptr;
 }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a22c32241ac61..1979aa8a97b26 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -38,6 +38,7 @@
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "clang/Basic/TypeTraits.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/PointerUnion.h"
@@ -3257,7 +3258,49 @@ class OverloadExpr : public Expr {
   bool hasTemplateKeyword() const { return getTemplateKeywordLoc().isValid(); }
 
   /// Determines whether this expression had explicit template arguments.
-  bool hasExplicitTemplateArgs() const { return getLAngleLoc().isValid(); }
+  bool hasExplicitTemplateArgs() const {
+    if (!hasTemplateKWAndArgsInfo())
+      return false;
+    // FIXME: deduced function types can have "hidden" args and no <
+    // investigate that further, but ultimately maybe we want to model concepts
+    // reference with another kind of expression.
+    return (isConceptReference() || isVarDeclReference())
+               ? getTrailingASTTemplateKWAndArgsInfo()->NumTemplateArgs
+               : getLAngleLoc().isValid();
+  }
+
+  bool isConceptReference() const {
+    return getNumDecls() == 1 && [&]() {
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              getTrailingResults()->getDecl()))
+        return TTP->kind() == TNK_Concept_template;
+      if (isa<ConceptDecl>(getTrailingResults()->getDecl()))
+        return true;
+      return false;
+    }();
+  }
+
+  bool isVarDeclReference() const {
+    return getNumDecls() == 1 && [&]() {
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              getTrailingResults()->getDecl()))
+        return TTP->kind() == TNK_Var_template;
+      if (isa<VarTemplateDecl>(getTrailingResults()->getDecl()))
+        return true;
+      return false;
+    }();
+  }
+
+  TemplateDecl *getTemplateDecl() const {
+    assert(getNumDecls() == 1);
+    return dyn_cast_or_null<TemplateDecl>(getTrailingResults()->getDecl());
+  }
+
+  TemplateTemplateParmDecl *getTemplateTemplateDecl() const {
+    assert(getNumDecls() == 1);
+    return dyn_cast_or_null<TemplateTemplateParmDecl>(
+        getTrailingResults()->getDecl());
+  }
 
   TemplateArgumentLoc const *getTemplateArgs() const {
     if (!hasExplicitTemplateArgs())
@@ -4658,7 +4701,7 @@ class SubstNonTypeTemplateParmExpr : public Expr {
   // sugared: it doesn't need to be resugared later.
   bool getFinal() const { return Final; }
 
-  NonTypeTemplateParmDecl *getParameter() const;
+  NamedDecl *getParameter() const;
 
   bool isReferenceParameter() const { return AssociatedDeclAndRef.getInt(); }
 
diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h
index 7ab0c3e0b2769..4f162b6ffc8af 100644
--- a/clang/include/clang/AST/ExprConcepts.h
+++ b/clang/include/clang/AST/ExprConcepts.h
@@ -84,7 +84,9 @@ class ConceptSpecializationExpr final : public Expr {
 
   ConceptReference *getConceptReference() const { return ConceptRef; }
 
-  ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
+  ConceptDecl *getNamedConcept() const {
+    return cast<ConceptDecl>(ConceptRef->getNamedConcept());
+  }
 
   // FIXME: Several of the following functions can be removed. Instead the
   // caller can directly work with the ConceptReference.
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index b67036cae4261..eb384eae3faa7 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -316,6 +316,8 @@ class TemplateArgument {
   /// Determine whether this template argument is a pack expansion.
   bool isPackExpansion() const;
 
+  bool isConceptOrConceptTemplateParameter() const;
+
   /// Retrieve the type for a type template argument.
   QualType getAsType() const {
     assert(getKind() == Type && "Unexpected kind");
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 98810fbea726e..12dce309127e5 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -6762,10 +6762,10 @@ class DeducedType : public Type {
 class AutoType : public DeducedType {
   friend class ASTContext; // ASTContext creates these
 
-  ConceptDecl *TypeConstraintConcept;
+  TemplateDecl *TypeConstraintConcept;
 
   AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
-           TypeDependence ExtraDependence, QualType Canon, ConceptDecl *CD,
+           TypeDependence ExtraDependence, QualType Canon, TemplateDecl *CD,
            ArrayRef<TemplateArgument> TypeConstraintArgs);
 
 public:
@@ -6774,7 +6774,7 @@ class AutoType : public DeducedType {
             AutoTypeBits.NumArgs};
   }
 
-  ConceptDecl *getTypeConstraintConcept() const {
+  TemplateDecl *getTypeConstraintConcept() const {
     return TypeConstraintConcept;
   }
 
@@ -6797,7 +6797,7 @@ class AutoType : public DeducedType {
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context);
   static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                       QualType Deduced, AutoTypeKeyword Keyword,
-                      bool IsDependent, ConceptDecl *CD,
+                      bool IsDependent, TemplateDecl *CD,
                       ArrayRef<TemplateArgument> Arguments);
 
   static bool classof(const Type *T) {
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index be0bc896de3ea..52ef7ac54145e 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -2284,7 +2284,7 @@ class AutoTypeLoc
     return nullptr;
   }
 
-  ConceptDecl *getNamedConcept() const {
+  TemplateDecl *getNamedConcept() const {
     if (const auto *CR = getConceptReference())
       return CR->getNamedConcept();
     return nullptr;
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 3114d1180319a..3373e963038f1 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -495,9 +495,9 @@ let Class = AutoType in {
   def : Property<"keyword", AutoTypeKeyword> {
     let Read = [{ node->getKeyword() }];
   }
-  def : Property<"typeConstraintConcept", Optional<ConceptDeclRef>> {
+  def : Property<"typeConstraintConcept", Optional<TemplateDeclRef>> {
     let Read = [{ makeOptionalFromPointer(
-        const_cast<const ConceptDecl*>(node->getTypeConstraintConcept())) }];
+        node->getTypeConstraintConcept()) }];
   }
   def : Property<"typeConstraintArguments", Array<TemplateArgument>> {
     let Read = [{ node->getTypeConstraintArguments() }];
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 165f01514e2b1..0042afccba2c8 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -931,6 +931,10 @@ def err_missing_dependent_template_keyword : Error<
 def warn_missing_dependent_template_keyword : ExtWarn<
   "use 'template' keyword to treat '%0' as a dependent template name">;
 
+def err_cxx26_template_template_params
+    : Error<"%select{variable template|concept}0 template parameter is a C++2c "
+            "extension">;
+
 def ext_extern_template : Extension<
   "extern templates are a C++11 extension">, InGroup<CXX11>;
 def warn_cxx98_compat_extern_template : Warning<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 81cbd38d5cd24..7cfe145120ea4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5441,8 +5441,10 @@ def err_template_arg_must_be_expr : Error<
   "template argument for non-type template parameter must be an expression">;
 def err_template_arg_nontype_ambig : Error<
   "template argument for non-type template parameter is treated as function type %0">;
-def err_template_arg_must_be_template : Error<
-  "template argument for template template parameter must be a class template%select{| or type alias template}0">;
+def err_template_arg_must_be_template
+    : Error<"template argument for template template parameter must be a "
+            "%select{class template%select{| or type alias template}1|variable "
+            "template|concept}0">;
 def ext_template_arg_local_type : ExtWarn<
   "template argument uses local type %0">, InGroup<LocalTypeTemplateArgs>;
 def ext_template_arg_unnamed_type : ExtWarn<
@@ -5457,11 +5459,14 @@ def note_template_unnamed_type_here : Note<
   "unnamed type used in template argument was declared here">;
 def err_template_arg_overload_type : Error<
   "template argument is the type of an unresolved overloaded function">;
-def err_template_arg_not_valid_template : Error<
-  "template argument does not refer to a class or alias template, or template "
-  "template parameter">;
-def note_template_arg_refers_here_func : Note<
-  "template argument refers to function template %0, here">;
+def err_template_arg_not_valid_template
+    : Error<"template argument does not refer to a %select{class or alias "
+            "template|variable template|concept}0, or template "
+            "template parameter">;
+
+def note_template_arg_refers_to_template_here
+    : Note<"template argument refers to a %select{function template|class "
+           "template|variable template|concept}0 %1, here">;
 def err_template_arg_template_params_mismatch : Error<
   "template template argument has different template parameters than its "
   "corresponding template template parameter">;
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index a1c156eed5394..e5ee4b4e48ccc 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -160,7 +160,7 @@ class alignas(8) InitializedEntity {
 
   struct VD {
     /// The VarDecl, FieldDecl, or BindingDecl being initialized.
-    ValueDecl *VariableOrMember;
+    NamedDecl *VariableOrMember;
 
     /// When Kind == EK_Member, whether this is an implicit member
     /// initialization in a copy or move constructor. These can perform array
@@ -291,8 +291,8 @@ class alignas(8) InitializedEntity {
   }
 
   /// Create the initialization entity for a template parameter.
-  static InitializedEntity
-  InitializeTemplateParameter(QualType T, NonTypeTemplateParmDecl *Param) {
+  static InitializedEntity InitializeTemplateParameter(QualType T,
+                                                       NamedDecl *Param) {
     InitializedEntity Entity;
     Entity.Kind = EK_TemplateParameter;
     Entity.Type = T;
diff --git a/clang/include/clang/Sema/Ownership.h b/clang/include/clang/Sema/Ownership.h
index 0752f5de7e334..b1520837f2f9d 100644
--- a/clang/include/clang/Sema/Ownership.h
+++ b/clang/include/clang/Sema/Ownership.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_SEMA_OWNERSHIP_H
 #define LLVM_CLANG_SEMA_OWNERSHIP_H
 
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/ArrayRef.h"
diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h
index 3a8050f9a0a3d..6628bb88a81b9 100644
--- a/clang/include/clang/Sema/ParsedTemplate.h
+++ b/clang/include/clang/Sema/ParsedTemplate.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 #define LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 
+#include "clang/AST/DeclT...
[truncated]

@cor3ntin cor3ntin changed the title Variable template and concept template parameters [Clang] Initial support for P2841 (Variable template and concept template parameters) Jul 28, 2025
@cor3ntin cor3ntin added the c++26 label Jul 28, 2025
Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite get what is happening here yet, can you give a summary of what is being accomplished, and how you're doing so?

@@ -140,15 +142,15 @@ class ConceptReference {
NamedDecl *FoundDecl;

/// \brief The concept named.
ConceptDecl *NamedConcept;
TemplateDecl *NamedConcept;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems weird for a type named ConceptReference to not refer to a ConceptDecl anymore

@cor3ntin
Copy link
Contributor Author

I don't quite get what is happening here yet, can you give a summary of what is being accomplished, and how you're doing so?

Let me try to see if I can enlighten you a bit.

P2841 very simply extends the notion of template template parameters to variable templates and concepts (which are templates). Most of the wording complexity in P2841 deals with subsumption which is not implemented in this patch.

So, what does having new kindd of template template parameters imply?

  • TemplateTemplateParmDecl gains a kind ( modeled with TemplateNameKind)
  • The decl a ConceptReference references can be a TemplateTemplateParmDecl - more generally, all the places a ConceptDecl can be used, so can a TemplateTemplateParmDecl. - so all these places now take a NamedDecl.
  • A template-id used as template argument can be a variable template or a concept, so OverloadExpr and UnresolvedLookupExpr have some new queries to deal with that ( in particular because concept template parameter can be used as type constraints)
  • Because concepts are not fully substituted, there is new code to handle concept template parameter instantiation, although some/all of that should be replaced by [Clang][WIP] Normalize constraints before checking for satisfaction #141776
  • Because these new types of template can be deduced in the same way non-type (constant using the new terminology) template parameters can, NonTypeOrVarTemplateParmDecl is used in SemaTemplateDeduction to model either of these things.

I'm happy to answer more specific questions!

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments, else looks reasonable. Thanks for the explaination!

/// an \c Sema::TypeTy* (for a type), an Expr* (for an
/// expression), or an Sema::TemplateTy (for a template).
void *Arg;
/// Represents the parsed form of a C++ template argument.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff on this file is a mess! Get I get a 'TL;DR' as to what actually changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... nothing, apparently. I reverted the formatting changes

@@ -1746,6 +1754,16 @@ class TemplateTemplateParmDecl final
return SourceRange(getTemplateParameters()->getTemplateLoc(), End);
}

TemplateNameKind kind() const {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mild preference for templateKind or something like that (perhaps something more accurate? nameKind seems wrong toot hough)? We use kind to mean sooooo many different things, it makes reading later not particularly descriptive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TemplateNameKind is pre-existing. It's the kind of a TemplateName.
Note that I did consider using a new enum type, as we don't need all the values of TemplateNameKind, but reusing the same enums avoids having to convert back and forth.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, i wasn't suggesting changing the num name, just that kind isn't really descriptive here. and nameKind is a bad alternative, and templateKind is perhaps not a great name either. So if you have a better name than kind for the function, it would be appreciated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I somehow completely misread your point. I will think about it.
Note that core failed to come up with something better https://eel.is/c++draft/temp#param-2

Copy link
Contributor

@zyn0217 zyn0217 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on it!

I did a very cursory review and I have not dived into the details yet.

Comment on lines 1198 to 1204
UnresolvedSet<1> R;
R.addDecl(CDT);
ImmediatelyDeclaredConstraint = UnresolvedLookupExpr::Create(
S.getASTContext(), nullptr, SS.getWithLocInContext(S.getASTContext()),
SourceLocation(), NameInfo, false, &ConstraintArgs, R.begin(), R.end(),
/*KnownDependent=*/false,
/*KnownInstantiationDependent=*/false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we want a FIXME suggesting there's a better model than UnresolvedLookupExpr + UnresolvedSet

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added that in CheckVarOrConceptTemplateTemplateId (and moved the creation of the UnresolvedLookupExpr there)

Comment on lines 1205 to 1207
if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
return ImmediatelyDeclaredConstraint;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: combine & move the if 1188-1189 and 1205-1206 after the brace at line 1207

Comment on lines 4762 to 4765
ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
const CXXScopeSpec &, const DeclarationNameInfo &,
TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
const TemplateArgumentListInfo *TemplateArgs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't return anything other than an empty Expr or true.

Maybe bool Sema::CheckVarOrConceptTemplateTemplateId?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified the function to return an Expr, it removes a bit of duplication

CheckTemplateArgumentInfo CTAI;
if (CheckTemplateArgumentList(
Template, TemplateLoc,
const_cast<TemplateArgumentListInfo &>(*TemplateArgs),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we somehow ensure the correctness of consts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not easily, I left a comment

Comment on lines +9228 to +9232
bool Sema::CheckConceptUseInDefinition(NamedDecl *Concept, SourceLocation Loc) {
if (auto *CE = llvm::dyn_cast<ConceptDecl>(Concept);
CE && !CE->isInvalidDecl() && !CE->hasDefinition()) {
Diag(Loc, diag::err_recursive_concept) << CE;
Diag(CE->getLocation(), diag::note_declared_at);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want a handling of TemplateTemplateParmDecl?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, concept template parameters don't have a definition. I am adding a comment.

TemplateArgument TemplateInstantiator::TransformNamedTemplateTemplateArgument(
CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc) {
if (TemplateTemplateParmDecl *TTP =
dyn_cast_or_null<TemplateTemplateParmDecl>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_if_present

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zyn0217 zyn0217 requested a review from mizvekov July 28, 2025 16:45
@@ -3341,7 +3359,12 @@ inline TemplateDecl *getAsTypeTemplateDecl(Decl *D) {
return TD && (isa<ClassTemplateDecl>(TD) ||
isa<ClassTemplatePartialSpecializationDecl>(TD) ||
isa<TypeAliasTemplateDecl>(TD) ||
isa<TemplateTemplateParmDecl>(TD))
[&]() {
if (TemplateTemplateParmDecl *TTP =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (TemplateTemplateParmDecl *TTP =
if (const auto *TTP =

@@ -6665,7 +6668,8 @@ QualType ASTContext::getAutoTypeInternal(
Canon = DeducedType.getCanonicalType();
} else if (TypeConstraintConcept) {
bool AnyNonCanonArgs = false;
ConceptDecl *CanonicalConcept = TypeConstraintConcept->getCanonicalDecl();
TemplateDecl *CanonicalConcept =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TemplateDecl *CanonicalConcept =
auto *CanonicalConcept =

if (std::distance(Begin, End) != 1)
return false;
NamedDecl *ND = *Begin;
if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(ND))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(ND))
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND))

if (parm->isParameterPack() && isa<PackExpansionType>(T))
T = cast<PackExpansionType>(T)->getPattern();
return SemaRef.SubstType(T, TemplateArgs, loc, parm->getDeclName());
if (auto NTTP = dyn_cast<NonTypeTemplateParmDecl>(parm)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto NTTP = dyn_cast<NonTypeTemplateParmDecl>(parm)) {
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(parm)) {

... right?

Comment on lines 1080 to 1081
if (TemplateTemplateParmDecl *TTP =
llvm::dyn_cast<TemplateTemplateParmDecl>(TN.getAsTemplateDecl())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (TemplateTemplateParmDecl *TTP =
llvm::dyn_cast<TemplateTemplateParmDecl>(TN.getAsTemplateDecl())) {
if (auto *TTP =
dyn_cast<TemplateTemplateParmDecl>(TN.getAsTemplateDecl())) {

bool TraverseUnresolvedLookupExpr(UnresolvedLookupExpr *E) override {
if (E->getNumDecls() == 1) {
NamedDecl *ND = *E->decls_begin();
if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(ND);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(ND);
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND);

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm good with this. No real other comments here.

@cor3ntin cor3ntin merged commit 28ed57e into llvm:main Aug 4, 2025
9 checks passed
@cor3ntin cor3ntin deleted the corentin/merge_var_temp branch August 4, 2025 06:51
@llvm-ci
Copy link
Collaborator

llvm-ci commented Aug 4, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-sie-win running on sie-win-worker while building clang,lldb at step 7 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/46/builds/21161

Here is the relevant piece of the build log for the reference
Step 7 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'lld :: COFF/import_weak_alias.test' FAILED ********************
Exit Code: 3221225477

Command Output (stdout):
--
# RUN: at line 3
split-file Z:\b\llvm-clang-x86_64-sie-win\llvm-project\lld\test\COFF\import_weak_alias.test Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.dir
# executed command: split-file 'Z:\b\llvm-clang-x86_64-sie-win\llvm-project\lld\test\COFF\import_weak_alias.test' 'Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.dir'
# note: command had no output on stdout or stderr
# RUN: at line 4
z:\b\llvm-clang-x86_64-sie-win\build\bin\llvm-mc.exe --filetype=obj -triple=x86_64-windows-msvc Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.dir/foo.s -o Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.foo.obj
# executed command: 'z:\b\llvm-clang-x86_64-sie-win\build\bin\llvm-mc.exe' --filetype=obj -triple=x86_64-windows-msvc 'Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.dir/foo.s' -o 'Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.foo.obj'
# note: command had no output on stdout or stderr
# RUN: at line 5
z:\b\llvm-clang-x86_64-sie-win\build\bin\llvm-mc.exe --filetype=obj -triple=x86_64-windows-msvc Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.dir/qux.s -o Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.qux.obj
# executed command: 'z:\b\llvm-clang-x86_64-sie-win\build\bin\llvm-mc.exe' --filetype=obj -triple=x86_64-windows-msvc 'Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.dir/qux.s' -o 'Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.qux.obj'
# note: command had no output on stdout or stderr
# RUN: at line 6
z:\b\llvm-clang-x86_64-sie-win\build\bin\lld-link.exe Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.qux.obj Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.foo.obj -out:Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.dll -dll
# executed command: 'z:\b\llvm-clang-x86_64-sie-win\build\bin\lld-link.exe' 'Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.qux.obj' 'Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.foo.obj' '-out:Z:\b\llvm-clang-x86_64-sie-win\build\tools\lld\test\COFF\Output\import_weak_alias.test.tmp.dll' -dll
# .---command stderr------------
# | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
# | Stack dump:
# | 0.	Program arguments: z:\\b\\llvm-clang-x86_64-sie-win\\build\\bin\\lld-link.exe Z:\\b\\llvm-clang-x86_64-sie-win\\build\\tools\\lld\\test\\COFF\\Output\\import_weak_alias.test.tmp.qux.obj Z:\\b\\llvm-clang-x86_64-sie-win\\build\\tools\\lld\\test\\COFF\\Output\\import_weak_alias.test.tmp.foo.obj -out:Z:\\b\\llvm-clang-x86_64-sie-win\\build\\tools\\lld\\test\\COFF\\Output\\import_weak_alias.test.tmp.dll -dll
# | Exception Code: 0xC0000005
# | #0 0x00007ff8b39c1b39 (C:\Windows\System32\KERNELBASE.dll+0x41b39)
# | #1 0x00007ff70d3dbb58 (z:\b\llvm-clang-x86_64-sie-win\build\bin\lld-link.exe+0xcbb58)
# | #2 0x00007ff70d46331b (z:\b\llvm-clang-x86_64-sie-win\build\bin\lld-link.exe+0x15331b)
# | #3 0x00007ff70d3bd9ea (z:\b\llvm-clang-x86_64-sie-win\build\bin\lld-link.exe+0xad9ea)
# | #4 0x00007ff70d3bda54 (z:\b\llvm-clang-x86_64-sie-win\build\bin\lld-link.exe+0xada54)
# | #5 0x00007ff70fb309c4 (z:\b\llvm-clang-x86_64-sie-win\build\bin\lld-link.exe+0x28209c4)
# | #6 0x00007ff8b6ad7ac4 (C:\Windows\System32\KERNEL32.DLL+0x17ac4)
# | #7 0x00007ff8b6ffa8c1 (C:\Windows\SYSTEM32\ntdll.dll+0x5a8c1)
# `-----------------------------
# error: command failed with exit status: 0xc0000005

--

********************


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++26 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category lldb
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants