forked from OSchip/llvm-project
[Concepts] Class template associated constraints
Summary: This adds associated constraints as a property of class templates. An error is produced if redeclarations are not similarly constrained. Reviewers: rsmith, faisalv, aaron.ballman Reviewed By: rsmith Subscribers: cfe-commits, nwilson Differential Revision: https://reviews.llvm.org/D25674 llvm-svn: 294697
This commit is contained in:
parent
48b4b04700
commit
5a8ec4e287
|
@ -344,6 +344,32 @@ public:
|
||||||
// Kinds of Templates
|
// Kinds of Templates
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
/// \brief Stores the template parameter list and associated constraints for
|
||||||
|
/// \c TemplateDecl objects that track associated constraints.
|
||||||
|
class ConstrainedTemplateDeclInfo {
|
||||||
|
friend TemplateDecl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConstrainedTemplateDeclInfo() : TemplateParams(), AssociatedConstraints() {}
|
||||||
|
|
||||||
|
TemplateParameterList *getTemplateParameters() const {
|
||||||
|
return TemplateParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr *getAssociatedConstraints() const { return AssociatedConstraints; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setTemplateParameters(TemplateParameterList *TParams) {
|
||||||
|
TemplateParams = TParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAssociatedConstraints(Expr *AC) { AssociatedConstraints = AC; }
|
||||||
|
|
||||||
|
TemplateParameterList *TemplateParams;
|
||||||
|
Expr *AssociatedConstraints;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// \brief The base class of all kinds of template declarations (e.g.,
|
/// \brief The base class of all kinds of template declarations (e.g.,
|
||||||
/// class, function, etc.).
|
/// class, function, etc.).
|
||||||
///
|
///
|
||||||
|
@ -352,33 +378,53 @@ public:
|
||||||
class TemplateDecl : public NamedDecl {
|
class TemplateDecl : public NamedDecl {
|
||||||
void anchor() override;
|
void anchor() override;
|
||||||
protected:
|
protected:
|
||||||
// This is probably never used.
|
|
||||||
TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name)
|
|
||||||
: NamedDecl(DK, DC, L, Name), TemplatedDecl(nullptr, false),
|
|
||||||
TemplateParams(nullptr) {}
|
|
||||||
|
|
||||||
// Construct a template decl with the given name and parameters.
|
// Construct a template decl with the given name and parameters.
|
||||||
// Used when there is not templated element (tt-params).
|
// Used when there is no templated element (e.g., for tt-params).
|
||||||
TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name,
|
TemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK, DeclContext *DC,
|
||||||
|
SourceLocation L, DeclarationName Name,
|
||||||
TemplateParameterList *Params)
|
TemplateParameterList *Params)
|
||||||
: NamedDecl(DK, DC, L, Name), TemplatedDecl(nullptr, false),
|
: NamedDecl(DK, DC, L, Name), TemplatedDecl(nullptr, false),
|
||||||
TemplateParams(Params) {}
|
TemplateParams(CTDI) {
|
||||||
|
this->setTemplateParameters(Params);
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name,
|
||||||
|
TemplateParameterList *Params)
|
||||||
|
: TemplateDecl(nullptr, DK, DC, L, Name, Params) {}
|
||||||
|
|
||||||
// Construct a template decl with name, parameters, and templated element.
|
// Construct a template decl with name, parameters, and templated element.
|
||||||
TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name,
|
TemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK, DeclContext *DC,
|
||||||
|
SourceLocation L, DeclarationName Name,
|
||||||
TemplateParameterList *Params, NamedDecl *Decl)
|
TemplateParameterList *Params, NamedDecl *Decl)
|
||||||
: NamedDecl(DK, DC, L, Name), TemplatedDecl(Decl, false),
|
: NamedDecl(DK, DC, L, Name), TemplatedDecl(Decl, false),
|
||||||
TemplateParams(Params) {}
|
TemplateParams(CTDI) {
|
||||||
|
this->setTemplateParameters(Params);
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName Name,
|
||||||
|
TemplateParameterList *Params, NamedDecl *Decl)
|
||||||
|
: TemplateDecl(nullptr, DK, DC, L, Name, Params, Decl) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Get the list of template parameters
|
/// Get the list of template parameters
|
||||||
TemplateParameterList *getTemplateParameters() const {
|
TemplateParameterList *getTemplateParameters() const {
|
||||||
return TemplateParams;
|
const auto *const CTDI =
|
||||||
|
TemplateParams.dyn_cast<ConstrainedTemplateDeclInfo *>();
|
||||||
|
return CTDI ? CTDI->getTemplateParameters()
|
||||||
|
: TemplateParams.get<TemplateParameterList *>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the constraint-expression from the associated requires-clause (if any)
|
/// Get the constraint-expression from the associated requires-clause (if any)
|
||||||
const Expr *getRequiresClause() const {
|
const Expr *getRequiresClause() const {
|
||||||
return TemplateParams ? TemplateParams->getRequiresClause() : nullptr;
|
const TemplateParameterList *const TP = getTemplateParameters();
|
||||||
|
return TP ? TP->getRequiresClause() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr *getAssociatedConstraints() const {
|
||||||
|
const TemplateDecl *const C = cast<TemplateDecl>(getCanonicalDecl());
|
||||||
|
const auto *const CTDI =
|
||||||
|
C->TemplateParams.dyn_cast<ConstrainedTemplateDeclInfo *>();
|
||||||
|
return CTDI ? CTDI->getAssociatedConstraints() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the underlying, templated declaration.
|
/// Get the underlying, templated declaration.
|
||||||
|
@ -391,7 +437,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceRange getSourceRange() const override LLVM_READONLY {
|
SourceRange getSourceRange() const override LLVM_READONLY {
|
||||||
return SourceRange(TemplateParams->getTemplateLoc(),
|
return SourceRange(getTemplateParameters()->getTemplateLoc(),
|
||||||
TemplatedDecl.getPointer()->getSourceRange().getEnd());
|
TemplatedDecl.getPointer()->getSourceRange().getEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +453,29 @@ protected:
|
||||||
/// (function or variable) is a concept.
|
/// (function or variable) is a concept.
|
||||||
llvm::PointerIntPair<NamedDecl *, 1, bool> TemplatedDecl;
|
llvm::PointerIntPair<NamedDecl *, 1, bool> TemplatedDecl;
|
||||||
|
|
||||||
TemplateParameterList* TemplateParams;
|
/// \brief The template parameter list and optional requires-clause
|
||||||
|
/// associated with this declaration; alternatively, a
|
||||||
|
/// \c ConstrainedTemplateDeclInfo if the associated constraints of the
|
||||||
|
/// template are being tracked by this particular declaration.
|
||||||
|
llvm::PointerUnion<TemplateParameterList *,
|
||||||
|
ConstrainedTemplateDeclInfo *>
|
||||||
|
TemplateParams;
|
||||||
|
|
||||||
|
void setTemplateParameters(TemplateParameterList *TParams) {
|
||||||
|
if (auto *const CTDI =
|
||||||
|
TemplateParams.dyn_cast<ConstrainedTemplateDeclInfo *>()) {
|
||||||
|
CTDI->setTemplateParameters(TParams);
|
||||||
|
} else {
|
||||||
|
TemplateParams = TParams;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAssociatedConstraints(Expr *AC) {
|
||||||
|
assert(isCanonicalDecl() &&
|
||||||
|
"Attaching associated constraints to non-canonical Decl");
|
||||||
|
TemplateParams.get<ConstrainedTemplateDeclInfo *>()
|
||||||
|
->setAssociatedConstraints(AC);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// \brief Initialize the underlying templated declaration and
|
/// \brief Initialize the underlying templated declaration and
|
||||||
|
@ -737,11 +805,17 @@ protected:
|
||||||
virtual CommonBase *newCommon(ASTContext &C) const = 0;
|
virtual CommonBase *newCommon(ASTContext &C) const = 0;
|
||||||
|
|
||||||
// Construct a template decl with name, parameters, and templated element.
|
// Construct a template decl with name, parameters, and templated element.
|
||||||
|
RedeclarableTemplateDecl(ConstrainedTemplateDeclInfo *CTDI, Kind DK,
|
||||||
|
ASTContext &C, DeclContext *DC, SourceLocation L,
|
||||||
|
DeclarationName Name, TemplateParameterList *Params,
|
||||||
|
NamedDecl *Decl)
|
||||||
|
: TemplateDecl(CTDI, DK, DC, L, Name, Params, Decl), redeclarable_base(C),
|
||||||
|
Common() {}
|
||||||
|
|
||||||
RedeclarableTemplateDecl(Kind DK, ASTContext &C, DeclContext *DC,
|
RedeclarableTemplateDecl(Kind DK, ASTContext &C, DeclContext *DC,
|
||||||
SourceLocation L, DeclarationName Name,
|
SourceLocation L, DeclarationName Name,
|
||||||
TemplateParameterList *Params, NamedDecl *Decl)
|
TemplateParameterList *Params, NamedDecl *Decl)
|
||||||
: TemplateDecl(DK, DC, L, Name, Params, Decl), redeclarable_base(C),
|
: RedeclarableTemplateDecl(nullptr, DK, C, DC, L, Name, Params, Decl) {}
|
||||||
Common() {}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <class decl_type> friend class RedeclarableTemplate;
|
template <class decl_type> friend class RedeclarableTemplate;
|
||||||
|
@ -1997,10 +2071,16 @@ protected:
|
||||||
llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &
|
llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &
|
||||||
getPartialSpecializations();
|
getPartialSpecializations();
|
||||||
|
|
||||||
|
ClassTemplateDecl(ConstrainedTemplateDeclInfo *CTDI, ASTContext &C,
|
||||||
|
DeclContext *DC, SourceLocation L, DeclarationName Name,
|
||||||
|
TemplateParameterList *Params, NamedDecl *Decl)
|
||||||
|
: RedeclarableTemplateDecl(CTDI, ClassTemplate, C, DC, L, Name, Params,
|
||||||
|
Decl) {}
|
||||||
|
|
||||||
ClassTemplateDecl(ASTContext &C, DeclContext *DC, SourceLocation L,
|
ClassTemplateDecl(ASTContext &C, DeclContext *DC, SourceLocation L,
|
||||||
DeclarationName Name, TemplateParameterList *Params,
|
DeclarationName Name, TemplateParameterList *Params,
|
||||||
NamedDecl *Decl)
|
NamedDecl *Decl)
|
||||||
: RedeclarableTemplateDecl(ClassTemplate, C, DC, L, Name, Params, Decl) {}
|
: ClassTemplateDecl(nullptr, C, DC, L, Name, Params, Decl) {}
|
||||||
|
|
||||||
CommonBase *newCommon(ASTContext &C) const override;
|
CommonBase *newCommon(ASTContext &C) const override;
|
||||||
|
|
||||||
|
@ -2023,12 +2103,14 @@ public:
|
||||||
return getTemplatedDecl()->isThisDeclarationADefinition();
|
return getTemplatedDecl()->isThisDeclarationADefinition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: remove default argument for AssociatedConstraints
|
||||||
/// \brief Create a class template node.
|
/// \brief Create a class template node.
|
||||||
static ClassTemplateDecl *Create(ASTContext &C, DeclContext *DC,
|
static ClassTemplateDecl *Create(ASTContext &C, DeclContext *DC,
|
||||||
SourceLocation L,
|
SourceLocation L,
|
||||||
DeclarationName Name,
|
DeclarationName Name,
|
||||||
TemplateParameterList *Params,
|
TemplateParameterList *Params,
|
||||||
NamedDecl *Decl);
|
NamedDecl *Decl,
|
||||||
|
Expr *AssociatedConstraints = nullptr);
|
||||||
|
|
||||||
/// \brief Create an empty class template node.
|
/// \brief Create an empty class template node.
|
||||||
static ClassTemplateDecl *CreateDeserialized(ASTContext &C, unsigned ID);
|
static ClassTemplateDecl *CreateDeserialized(ASTContext &C, unsigned ID);
|
||||||
|
|
|
@ -2287,6 +2287,9 @@ def err_concept_specialized : Error<
|
||||||
"%select{function|variable}0 concept cannot be "
|
"%select{function|variable}0 concept cannot be "
|
||||||
"%select{explicitly instantiated|explicitly specialized|partially specialized}1">;
|
"%select{explicitly instantiated|explicitly specialized|partially specialized}1">;
|
||||||
|
|
||||||
|
def err_template_different_associated_constraints : Error<
|
||||||
|
"associated constraints differ in template redeclaration">;
|
||||||
|
|
||||||
// C++11 char16_t/char32_t
|
// C++11 char16_t/char32_t
|
||||||
def warn_cxx98_compat_unicode_type : Warning<
|
def warn_cxx98_compat_unicode_type : Warning<
|
||||||
"'%0' type specifier is incompatible with C++98">,
|
"'%0' type specifier is incompatible with C++98">,
|
||||||
|
|
|
@ -297,10 +297,18 @@ ClassTemplateDecl *ClassTemplateDecl::Create(ASTContext &C,
|
||||||
SourceLocation L,
|
SourceLocation L,
|
||||||
DeclarationName Name,
|
DeclarationName Name,
|
||||||
TemplateParameterList *Params,
|
TemplateParameterList *Params,
|
||||||
NamedDecl *Decl) {
|
NamedDecl *Decl,
|
||||||
|
Expr *AssociatedConstraints) {
|
||||||
AdoptTemplateParameterList(Params, cast<DeclContext>(Decl));
|
AdoptTemplateParameterList(Params, cast<DeclContext>(Decl));
|
||||||
ClassTemplateDecl *New = new (C, DC) ClassTemplateDecl(C, DC, L, Name,
|
|
||||||
Params, Decl);
|
if (!AssociatedConstraints) {
|
||||||
|
return new (C, DC) ClassTemplateDecl(C, DC, L, Name, Params, Decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstrainedTemplateDeclInfo *const CTDI = new (C) ConstrainedTemplateDeclInfo;
|
||||||
|
ClassTemplateDecl *const New =
|
||||||
|
new (C, DC) ClassTemplateDecl(CTDI, C, DC, L, Name, Params, Decl);
|
||||||
|
New->setAssociatedConstraints(AssociatedConstraints);
|
||||||
return New;
|
return New;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,26 @@ clang::getTemplateParamsRange(TemplateParameterList const * const *Ps,
|
||||||
return SourceRange(Ps[0]->getTemplateLoc(), Ps[N-1]->getRAngleLoc());
|
return SourceRange(Ps[0]->getTemplateLoc(), Ps[N-1]->getRAngleLoc());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
/// \brief [temp.constr.decl]p2: A template's associated constraints are
|
||||||
|
/// defined as a single constraint-expression derived from the introduced
|
||||||
|
/// constraint-expressions [ ... ].
|
||||||
|
///
|
||||||
|
/// \param Params The template parameter list and optional requires-clause.
|
||||||
|
///
|
||||||
|
/// \param FD The underlying templated function declaration for a function
|
||||||
|
/// template.
|
||||||
|
static Expr *formAssociatedConstraints(TemplateParameterList *Params,
|
||||||
|
FunctionDecl *FD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Expr *clang::formAssociatedConstraints(TemplateParameterList *Params,
|
||||||
|
FunctionDecl *FD) {
|
||||||
|
// FIXME: Concepts: collect additional introduced constraint-expressions
|
||||||
|
assert(!FD && "Cannot collect constraints from function declaration yet.");
|
||||||
|
return Params->getRequiresClause();
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Determine whether the declaration found is acceptable as the name
|
/// \brief Determine whether the declaration found is acceptable as the name
|
||||||
/// of a template and, if so, return that template declaration. Otherwise,
|
/// of a template and, if so, return that template declaration. Otherwise,
|
||||||
/// returns NULL.
|
/// returns NULL.
|
||||||
|
@ -1137,6 +1157,9 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Memory management; associated constraints are not always stored.
|
||||||
|
Expr *const CurAC = formAssociatedConstraints(TemplateParams, nullptr);
|
||||||
|
|
||||||
if (PrevClassTemplate) {
|
if (PrevClassTemplate) {
|
||||||
// Ensure that the template parameter lists are compatible. Skip this check
|
// Ensure that the template parameter lists are compatible. Skip this check
|
||||||
// for a friend in a dependent context: the template parameter list itself
|
// for a friend in a dependent context: the template parameter list itself
|
||||||
|
@ -1148,6 +1171,29 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
||||||
TPL_TemplateMatch))
|
TPL_TemplateMatch))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Check for matching associated constraints on redeclarations.
|
||||||
|
const Expr *const PrevAC = PrevClassTemplate->getAssociatedConstraints();
|
||||||
|
const bool RedeclACMismatch = [&] {
|
||||||
|
if (!(CurAC || PrevAC))
|
||||||
|
return false; // Nothing to check; no mismatch.
|
||||||
|
if (CurAC && PrevAC) {
|
||||||
|
llvm::FoldingSetNodeID CurACInfo, PrevACInfo;
|
||||||
|
CurAC->Profile(CurACInfo, Context, /*Canonical=*/true);
|
||||||
|
PrevAC->Profile(PrevACInfo, Context, /*Canonical=*/true);
|
||||||
|
if (CurACInfo == PrevACInfo)
|
||||||
|
return false; // All good; no mismatch.
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (RedeclACMismatch) {
|
||||||
|
Diag(CurAC ? CurAC->getLocStart() : NameLoc,
|
||||||
|
diag::err_template_different_associated_constraints);
|
||||||
|
Diag(PrevAC ? PrevAC->getLocStart() : PrevClassTemplate->getLocation(),
|
||||||
|
diag::note_template_prev_declaration) << /*declaration*/0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// C++ [temp.class]p4:
|
// C++ [temp.class]p4:
|
||||||
// In a redeclaration, partial specialization, explicit
|
// In a redeclaration, partial specialization, explicit
|
||||||
// specialization or explicit instantiation of a class template,
|
// specialization or explicit instantiation of a class template,
|
||||||
|
@ -1250,10 +1296,15 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
||||||
AddMsStructLayoutForRecord(NewClass);
|
AddMsStructLayoutForRecord(NewClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attach the associated constraints when the declaration will not be part of
|
||||||
|
// a decl chain.
|
||||||
|
Expr *const ACtoAttach =
|
||||||
|
PrevClassTemplate && ShouldAddRedecl ? nullptr : CurAC;
|
||||||
|
|
||||||
ClassTemplateDecl *NewTemplate
|
ClassTemplateDecl *NewTemplate
|
||||||
= ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
|
= ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
|
||||||
DeclarationName(Name), TemplateParams,
|
DeclarationName(Name), TemplateParams,
|
||||||
NewClass);
|
NewClass, ACtoAttach);
|
||||||
|
|
||||||
if (ShouldAddRedecl)
|
if (ShouldAddRedecl)
|
||||||
NewTemplate->setPreviousDecl(PrevClassTemplate);
|
NewTemplate->setPreviousDecl(PrevClassTemplate);
|
||||||
|
|
|
@ -1876,6 +1876,7 @@ DeclID ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
|
||||||
DeclID PatternID = ReadDeclID();
|
DeclID PatternID = ReadDeclID();
|
||||||
NamedDecl *TemplatedDecl = cast_or_null<NamedDecl>(Reader.GetDecl(PatternID));
|
NamedDecl *TemplatedDecl = cast_or_null<NamedDecl>(Reader.GetDecl(PatternID));
|
||||||
TemplateParameterList *TemplateParams = Record.readTemplateParameterList();
|
TemplateParameterList *TemplateParams = Record.readTemplateParameterList();
|
||||||
|
// FIXME handle associated constraints
|
||||||
D->init(TemplatedDecl, TemplateParams);
|
D->init(TemplatedDecl, TemplateParams);
|
||||||
|
|
||||||
return PatternID;
|
return PatternID;
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
|
||||||
|
|
||||||
|
namespace nodiag {
|
||||||
|
|
||||||
|
template <typename T> requires bool(T())
|
||||||
|
struct A;
|
||||||
|
template <typename U> requires bool(U())
|
||||||
|
struct A;
|
||||||
|
|
||||||
|
} // end namespace nodiag
|
||||||
|
|
||||||
|
namespace diag {
|
||||||
|
|
||||||
|
template <typename T> requires true // expected-note{{previous template declaration is here}}
|
||||||
|
struct A;
|
||||||
|
template <typename T> struct A; // expected-error{{associated constraints differ in template redeclaration}}
|
||||||
|
|
||||||
|
template <typename T> struct B; // expected-note{{previous template declaration is here}}
|
||||||
|
template <typename T> requires true // expected-error{{associated constraints differ in template redeclaration}}
|
||||||
|
struct B;
|
||||||
|
|
||||||
|
template <typename T> requires true // expected-note{{previous template declaration is here}}
|
||||||
|
struct C;
|
||||||
|
template <typename T> requires !0 // expected-error{{associated constraints differ in template redeclaration}}
|
||||||
|
struct C;
|
||||||
|
|
||||||
|
} // end namespace diag
|
||||||
|
|
||||||
|
namespace nodiag {
|
||||||
|
|
||||||
|
struct AA {
|
||||||
|
template <typename T> requires someFunc(T())
|
||||||
|
struct A;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> requires someFunc(T())
|
||||||
|
struct AA::A { };
|
||||||
|
|
||||||
|
struct AAF {
|
||||||
|
template <typename T> requires someFunc(T())
|
||||||
|
friend struct AA::A;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace nodiag
|
||||||
|
|
||||||
|
namespace diag {
|
||||||
|
|
||||||
|
template <unsigned N>
|
||||||
|
struct TA {
|
||||||
|
template <template <unsigned> class TT> requires TT<N>::happy // expected-note 2{{previous template declaration is here}}
|
||||||
|
struct A;
|
||||||
|
|
||||||
|
struct AF;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <unsigned N>
|
||||||
|
template <template <unsigned> class TT> struct TA<N>::A { }; // expected-error{{associated constraints differ in template redeclaration}}
|
||||||
|
|
||||||
|
template <unsigned N>
|
||||||
|
struct TA<N>::AF {
|
||||||
|
template <template <unsigned> class TT> requires TT<N + 0>::happy // expected-error{{associated constraints differ in template redeclaration}}
|
||||||
|
friend struct TA::A;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace diag
|
Loading…
Reference in New Issue