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
16 changes: 9 additions & 7 deletions clang/include/clang/AST/ASTConcept.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
namespace clang {

class ConceptDecl;
class TemplateDecl;
class Expr;
class NamedDecl;
struct PrintingPolicy;
Expand Down Expand Up @@ -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;

Expand All @@ -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


/// \brief The template argument list source info used to specialize the
/// concept.
const ASTTemplateArgumentListInfo *ArgsAsWritten;

ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
ConceptDecl *NamedConcept,
TemplateDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten)
: NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
ConceptName(ConceptNameInfo), FoundDecl(FoundDecl),
Expand All @@ -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 {
Expand Down Expand Up @@ -197,9 +199,7 @@ class ConceptReference {
return FoundDecl;
}

ConceptDecl *getNamedConcept() const {
return NamedConcept;
}
TemplateDecl *getNamedConcept() const { return NamedConcept; }

const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
return ArgsAsWritten;
Expand Down Expand Up @@ -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();
Expand Down
11 changes: 6 additions & 5 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
46 changes: 34 additions & 12 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
///
Expand All @@ -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);

Expand All @@ -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,
Expand Down Expand Up @@ -1746,6 +1754,16 @@ class TemplateTemplateParmDecl final
return SourceRange(getTemplateParameters()->getTemplateLoc(), End);
}

TemplateNameKind templateParameterKind() const {
return static_cast<TemplateNameKind>(ParameterKind);
}

bool isTypeConceptTemplateParam() const {
return templateParameterKind() == 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; }
Expand Down Expand Up @@ -3341,7 +3359,11 @@ inline TemplateDecl *getAsTypeTemplateDecl(Decl *D) {
return TD && (isa<ClassTemplateDecl>(TD) ||
isa<ClassTemplatePartialSpecializationDecl>(TD) ||
isa<TypeAliasTemplateDecl>(TD) ||
isa<TemplateTemplateParmDecl>(TD))
[&]() {
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TD))
return TTP->templateParameterKind() == TNK_Type_template;
return false;
}())
? TD
: nullptr;
}
Expand Down
47 changes: 45 additions & 2 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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->templateParameterKind() == 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->templateParameterKind() == 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())
Expand Down Expand Up @@ -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(); }

Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/AST/ExprConcepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/TemplateBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
8 changes: 4 additions & 4 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -6774,7 +6774,7 @@ class AutoType : public DeducedType {
AutoTypeBits.NumArgs};
}

ConceptDecl *getTypeConstraintConcept() const {
TemplateDecl *getTypeConstraintConcept() const {
return TypeConstraintConcept;
}

Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2284,7 +2284,7 @@ class AutoTypeLoc
return nullptr;
}

ConceptDecl *getNamedConcept() const {
TemplateDecl *getNamedConcept() const {
if (const auto *CR = getConceptReference())
return CR->getNamedConcept();
return nullptr;
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -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() }];
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
19 changes: 12 additions & 7 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand All @@ -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">;
Expand Down
9 changes: 5 additions & 4 deletions clang/include/clang/Sema/Initialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ class alignas(8) InitializedEntity {
};

struct VD {
/// The VarDecl, FieldDecl, or BindingDecl being initialized.
ValueDecl *VariableOrMember;
/// The VarDecl, FieldDecl, TemplateParmDecl, or BindingDecl being
/// initialized.
NamedDecl *VariableOrMember;

/// When Kind == EK_Member, whether this is an implicit member
/// initialization in a copy or move constructor. These can perform array
Expand Down Expand Up @@ -291,8 +292,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;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/Ownership.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading
Loading