Re-apply "Deferred Concept Instantiation Implementation"

This reverts commit 95d94a6775.

This implements the deferred concepts instantiation, which should allow
the libstdc++ ranges to properly compile, and for the CRTP to work for
constrained functions.

Since the last attempt, this has fixed the issues from @wlei and
@mordante.

Differential Revision: https://reviews.llvm.org/D126907
This commit is contained in:
Erich Keane 2022-08-19 13:57:45 -07:00
parent e0cdafe8d4
commit babdef27c5
31 changed files with 1813 additions and 240 deletions

View File

@ -345,6 +345,10 @@ C++20 Feature Support
`Issue 50455 <https://github.com/llvm/llvm-project/issues/50455>`_,
`Issue 54872 <https://github.com/llvm/llvm-project/issues/54872>`_,
`Issue 54587 <https://github.com/llvm/llvm-project/issues/54587>`_.
- Clang now correctly delays the instantiation of function constraints until
the time of checking, which should now allow the libstdc++ ranges implementation
to work for at least trivial examples. This fixes
`Issue 44178 <https://github.com/llvm/llvm-project/issues/44178>`_.
C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^

View File

@ -2669,6 +2669,11 @@ public:
/// template.
bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y) const;
/// Determine whether two Friend functions are different because constraints
/// that refer to an enclosing template, according to [temp.friend] p9.
bool FriendsDifferByConstraints(const FunctionDecl *X,
const FunctionDecl *Y) const;
/// Determine whether the two declarations refer to the same entity.
bool isSameEntity(const NamedDecl *X, const NamedDecl *Y) const;

View File

@ -2474,6 +2474,19 @@ public:
getCanonicalDecl()->FunctionDeclBits.IsMultiVersion = V;
}
// Sets that this is a constrained friend where the constraint refers to an
// enclosing template.
void setFriendConstraintRefersToEnclosingTemplate(bool V = true) {
getCanonicalDecl()
->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = V;
}
// Indicates this function is a constrained friend, where the constraint
// refers to an enclosing template for hte purposes of [temp.friend]p9.
bool FriendConstraintRefersToEnclosingTemplate() const {
return getCanonicalDecl()
->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate;
}
/// Gets the kind of multiversioning attribute this declaration has. Note that
/// this can return a value even if the function is not multiversion, such as
/// the case of 'target'.

View File

@ -1664,10 +1664,14 @@ class DeclContext {
/// Indicates if the function uses Floating Point Constrained Intrinsics
uint64_t UsesFPIntrin : 1;
// Indicates this function is a constrained friend, where the constraint
// refers to an enclosing template for hte purposes of [temp.friend]p9.
uint64_t FriendConstraintRefersToEnclosingTemplate : 1;
};
/// Number of non-inherited bits in FunctionDeclBitfields.
enum { NumFunctionDeclBits = 28 };
enum { NumFunctionDeclBits = 29 };
/// Stores the bits used by CXXConstructorDecl. If modified
/// NumCXXConstructorDeclBits and the accessor
@ -1679,12 +1683,12 @@ class DeclContext {
/// For the bits in FunctionDeclBitfields.
uint64_t : NumFunctionDeclBits;
/// 23 bits to fit in the remaining available space.
/// 22 bits to fit in the remaining available space.
/// Note that this makes CXXConstructorDeclBitfields take
/// exactly 64 bits and thus the width of NumCtorInitializers
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
uint64_t NumCtorInitializers : 20;
uint64_t NumCtorInitializers : 19;
uint64_t IsInheritingConstructor : 1;
/// Whether this constructor has a trail-allocated explicit specifier.

View File

@ -3673,6 +3673,31 @@ public:
bool ConsiderCudaAttrs = true,
bool ConsiderRequiresClauses = true);
// Calculates whether the expression Constraint depends on an enclosing
// template, for the purposes of [temp.friend] p9.
// TemplateDepth is the 'depth' of the friend function, which is used to
// compare whether a declaration reference is referring to a containing
// template, or just the current friend function. A 'lower' TemplateDepth in
// the AST refers to a 'containing' template. As the constraint is
// uninstantiated, this is relative to the 'top' of the TU.
bool ConstraintExpressionDependsOnEnclosingTemplate(unsigned TemplateDepth,
const Expr *Constraint);
// Calculates whether the friend function depends on an enclosing template for
// the purposes of [temp.friend] p9.
bool FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD);
// Calculates whether two constraint expressions are equal irrespective of a
// difference in 'depth'. This takes a pair of optional 'NamedDecl's 'Old' and
// 'New', which are the "source" of the constraint, since this is necessary
// for figuring out the relative 'depth' of the constraint. The depth of the
// 'primary template' and the 'instantiated from' templates aren't necessarily
// the same, such as a case when one is a 'friend' defined in a class.
bool AreConstraintExpressionsEqual(const NamedDecl *Old,
const Expr *OldConstr,
const NamedDecl *New,
const Expr *NewConstr);
enum class AllowedExplicit {
/// Allow no explicit functions to be used.
None,
@ -7152,6 +7177,21 @@ private:
LocalInstantiationScope &Scope,
const MultiLevelTemplateArgumentList &TemplateArgs);
/// used by SetupConstraintCheckingTemplateArgumentsAndScope to recursively(in
/// the case of lambdas) set up the LocalInstantiationScope of the current
/// function.
bool SetupConstraintScope(
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope);
/// Used during constraint checking, sets up the constraint template arguemnt
/// lists, and calls SetupConstraintScope to set up the
/// LocalInstantiationScope to have the proper set of ParVarDecls configured.
llvm::Optional<MultiLevelTemplateArgumentList>
SetupConstraintCheckingTemplateArgumentsAndScope(
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
LocalInstantiationScope &Scope);
public:
const NormalizedConstraint *
getNormalizedAssociatedConstraints(
@ -7194,6 +7234,39 @@ public:
bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
const MultiLevelTemplateArgumentList &TemplateArgLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
llvm::SmallVector<Expr *, 4> Converted;
return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted,
TemplateArgLists, TemplateIDRange,
Satisfaction);
}
/// \brief Check whether the given list of constraint expressions are
/// satisfied (as if in a 'conjunction') given template arguments.
/// Additionally, takes an empty list of Expressions which is populated with
/// the instantiated versions of the ConstraintExprs.
/// \param Template the template-like entity that triggered the constraints
/// check (either a concept or a constrained entity).
/// \param ConstraintExprs a list of constraint expressions, treated as if
/// they were 'AND'ed together.
/// \param ConvertedConstraints a out parameter that will get populated with
/// the instantiated version of the ConstraintExprs if we successfully checked
/// satisfaction.
/// \param TemplateArgList the multi-level list of template arguments to
/// substitute into the constraint expression. This should be relative to the
/// top-level (hence multi-level), since we need to instantiate fully at the
/// time of checking.
/// \param TemplateIDRange The source range of the template id that
/// caused the constraints check.
/// \param Satisfaction if true is returned, will contain details of the
/// satisfaction, with enough information to diagnose an unsatisfied
/// expression.
/// \returns true if an error occurred and satisfaction could not be checked,
/// false otherwise.
bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgList,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
/// \brief Check whether the given non-dependent constraint expression is
@ -7213,8 +7286,8 @@ public:
/// \returns true if an error occurred, false otherwise.
bool CheckFunctionConstraints(const FunctionDecl *FD,
ConstraintSatisfaction &Satisfaction,
SourceLocation UsageLoc = SourceLocation());
SourceLocation UsageLoc = SourceLocation(),
bool ForOverloadResolution = false);
/// \brief Ensure that the given template arguments satisfy the constraints
/// associated with the given template, emitting a diagnostic if they do not.
@ -8222,12 +8295,19 @@ public:
TPL_TemplateTemplateArgumentMatch
};
bool TemplateParameterListsAreEqual(TemplateParameterList *New,
TemplateParameterList *Old,
bool Complain,
TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc
= SourceLocation());
bool TemplateParameterListsAreEqual(
const NamedDecl *NewInstFrom, TemplateParameterList *New,
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc = SourceLocation());
bool TemplateParameterListsAreEqual(
TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc = SourceLocation()) {
return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
Kind, TemplateArgLoc);
}
bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams);
@ -8962,7 +9042,8 @@ public:
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
const NamedDecl *D, const TemplateArgumentList *Innermost = nullptr,
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr);
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
bool LookBeyondLambda = false, bool IncludeContainingStruct = false);
/// A context in which code is being synthesized (where a source location
/// alone is not sufficient to identify the context). This covers template
@ -9670,23 +9751,21 @@ public:
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity);
TypeSourceInfo *SubstFunctionDeclType(TypeSourceInfo *T,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc,
DeclarationName Entity,
CXXRecordDecl *ThisContext,
Qualifiers ThisTypeQuals);
TypeSourceInfo *SubstFunctionDeclType(
TypeSourceInfo *T, const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext,
Qualifiers ThisTypeQuals, bool EvaluateConstraints = true);
void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
const MultiLevelTemplateArgumentList &Args);
bool SubstExceptionSpec(SourceLocation Loc,
FunctionProtoType::ExceptionSpecInfo &ESI,
SmallVectorImpl<QualType> &ExceptionStorage,
const MultiLevelTemplateArgumentList &Args);
ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D,
const MultiLevelTemplateArgumentList &TemplateArgs,
int indexAdjustment,
Optional<unsigned> NumExpansions,
bool ExpectParameterPack);
ParmVarDecl *
SubstParmVarDecl(ParmVarDecl *D,
const MultiLevelTemplateArgumentList &TemplateArgs,
int indexAdjustment, Optional<unsigned> NumExpansions,
bool ExpectParameterPack, bool EvaluateConstraints = true);
bool SubstParmTypes(SourceLocation Loc, ArrayRef<ParmVarDecl *> Params,
const FunctionProtoType::ExtParameterInfo *ExtParamInfos,
const MultiLevelTemplateArgumentList &TemplateArgs,
@ -9696,6 +9775,25 @@ public:
ExprResult SubstExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);
// A RAII type used by the TemplateDeclInstantiator and TemplateInstantiator
// to disable constraint evaluation, then restore the state.
template <typename InstTy> struct ConstraintEvalRAII {
InstTy &TI;
bool OldValue;
ConstraintEvalRAII(InstTy &TI)
: TI(TI), OldValue(TI.getEvaluateConstraints()) {
TI.setEvaluateConstraints(false);
}
~ConstraintEvalRAII() { TI.setEvaluateConstraints(OldValue); }
};
// Unlike the above, this evaluates constraints, which should only happen at
// 'constraint checking' time.
ExprResult
SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);
/// Substitute the given template arguments into a list of
/// expressions, expanding pack expansions if required.
///
@ -9725,7 +9823,6 @@ public:
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Outputs);
Decl *SubstDecl(Decl *D, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs);
@ -9816,7 +9913,8 @@ public:
const MultiLevelTemplateArgumentList &TemplateArgs);
bool SubstTypeConstraint(TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
const MultiLevelTemplateArgumentList &TemplateArgs);
const MultiLevelTemplateArgumentList &TemplateArgs,
bool EvaluateConstraint);
bool InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
ParmVarDecl *Param);

View File

@ -503,6 +503,7 @@ enum class TemplateSubstitutionKind : char {
const MultiLevelTemplateArgumentList &TemplateArgs;
Sema::LateInstantiatedAttrVec* LateAttrs = nullptr;
LocalInstantiationScope *StartingScope = nullptr;
bool EvaluateConstraints = true;
/// A list of out-of-line class template partial
/// specializations that will need to be instantiated after the
@ -526,6 +527,13 @@ enum class TemplateSubstitutionKind : char {
SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex),
Owner(Owner), TemplateArgs(TemplateArgs) {}
void setEvaluateConstraints(bool B) {
EvaluateConstraints = B;
}
bool getEvaluateConstraints() {
return EvaluateConstraints;
}
// Define all the decl visitors using DeclNodes.inc
#define DECL(DERIVED, BASE) \
Decl *Visit ## DERIVED ## Decl(DERIVED ## Decl *D);

View File

@ -6452,6 +6452,31 @@ static bool hasSameOverloadableAttrs(const FunctionDecl *A,
return true;
}
bool ASTContext::FriendsDifferByConstraints(const FunctionDecl *X,
const FunctionDecl *Y) const {
// If these aren't friends, then they aren't friends that differ by
// constraints.
if (!X->getFriendObjectKind() || !Y->getFriendObjectKind())
return false;
// If the the two functions share lexical declaration context, they are not in
// separate instantations, and thus in the same scope.
if (X->getLexicalDeclContext() == Y->getLexicalDeclContext())
return false;
if (!X->getDescribedFunctionTemplate()) {
assert(!Y->getDescribedFunctionTemplate() &&
"How would these be the same if they aren't both templates?");
// If these friends don't have constraints, they aren't constrained, and
// thus don't fall under temp.friend p9. Else the simple presence of a
// constraint makes them unique.
return X->getTrailingRequiresClause();
}
return X->FriendConstraintRefersToEnclosingTemplate();
}
bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
if (X == Y)
return true;
@ -6532,6 +6557,10 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
FuncY->getTrailingRequiresClause()))
return false;
// Constrained friends are different in certain cases, see: [temp.friend]p9.
if (FriendsDifferByConstraints(FuncX, FuncY))
return false;
auto GetTypeAsWritten = [](const FunctionDecl *FD) {
// Map to the first declaration that we've already merged into this one.
// The TSI of redeclarations might not match (due to calling conventions

View File

@ -3701,6 +3701,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
ToFunction->setDefaulted(D->isDefaulted());
ToFunction->setExplicitlyDefaulted(D->isExplicitlyDefaulted());
ToFunction->setDeletedAsWritten(D->isDeletedAsWritten());
ToFunction->setFriendConstraintRefersToEnclosingTemplate(
D->FriendConstraintRefersToEnclosingTemplate());
ToFunction->setRangeEnd(ToEndLoc);
// Set the parameters.

View File

@ -2970,6 +2970,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
FunctionDeclBits.IsMultiVersion = false;
FunctionDeclBits.IsCopyDeductionCandidate = false;
FunctionDeclBits.HasODRHash = false;
FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false;
if (TrailingRequiresClause)
setTrailingRequiresClause(TrailingRequiresClause);
}

View File

@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
#include "TreeTransform.h"
#include "clang/Sema/SemaConcept.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
@ -18,6 +19,7 @@
#include "clang/Sema/Template.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/Initialization.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/OperatorPrecedence.h"
@ -30,6 +32,7 @@ using namespace sema;
namespace {
class LogicalBinOp {
SourceLocation Loc;
OverloadedOperatorKind Op = OO_None;
const Expr *LHS = nullptr;
const Expr *RHS = nullptr;
@ -40,12 +43,14 @@ public:
Op = BinaryOperator::getOverloadedOperator(BO->getOpcode());
LHS = BO->getLHS();
RHS = BO->getRHS();
Loc = BO->getExprLoc();
} else if (auto *OO = dyn_cast<CXXOperatorCallExpr>(E)) {
// If OO is not || or && it might not have exactly 2 arguments.
if (OO->getNumArgs() == 2) {
Op = OO->getOperator();
LHS = OO->getArg(0);
RHS = OO->getArg(1);
Loc = OO->getOperatorLoc();
}
}
}
@ -56,6 +61,26 @@ public:
const Expr *getLHS() const { return LHS; }
const Expr *getRHS() const { return RHS; }
ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS) const {
return recreateBinOp(SemaRef, LHS, const_cast<Expr *>(getRHS()));
}
ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS,
ExprResult RHS) const {
assert((isAnd() || isOr()) && "Not the right kind of op?");
assert((!LHS.isInvalid() && !RHS.isInvalid()) && "not good expressions?");
if (!LHS.isUsable() || !RHS.isUsable())
return ExprEmpty();
// We should just be able to 'normalize' these to the builtin Binary
// Operator, since that is how they are evaluated in constriant checks.
return BinaryOperator::Create(SemaRef.Context, LHS.get(), RHS.get(),
BinaryOperator::getOverloadedOpcode(Op),
SemaRef.Context.BoolTy, VK_PRValue,
OK_Ordinary, Loc, FPOptionsOverride{});
}
};
}
@ -122,16 +147,18 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
}
template <typename AtomicEvaluator>
static bool
static ExprResult
calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction,
AtomicEvaluator &&Evaluator) {
ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
if (LogicalBinOp BO = ConstraintExpr) {
if (calculateConstraintSatisfaction(S, BO.getLHS(), Satisfaction,
Evaluator))
return true;
ExprResult LHSRes = calculateConstraintSatisfaction(
S, BO.getLHS(), Satisfaction, Evaluator);
if (LHSRes.isInvalid())
return ExprError();
bool IsLHSSatisfied = Satisfaction.IsSatisfied;
@ -142,7 +169,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
// is checked. If that is satisfied, the disjunction is satisfied.
// Otherwise, the disjunction is satisfied if and only if the second
// operand is satisfied.
return false;
return BO.recreateBinOp(S, LHSRes);
if (BO.isAnd() && !IsLHSSatisfied)
// [temp.constr.op] p2
@ -151,12 +178,21 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
// is checked. If that is not satisfied, the conjunction is not
// satisfied. Otherwise, the conjunction is satisfied if and only if
// the second operand is satisfied.
return false;
return BO.recreateBinOp(S, LHSRes);
return calculateConstraintSatisfaction(
ExprResult RHSRes = calculateConstraintSatisfaction(
S, BO.getRHS(), Satisfaction, std::forward<AtomicEvaluator>(Evaluator));
} else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
if (RHSRes.isInvalid())
return ExprError();
return BO.recreateBinOp(S, LHSRes, RHSRes);
}
if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
// These aren't evaluated, so we don't care about cleanups, so we can just
// evaluate these as if the cleanups didn't exist.
return calculateConstraintSatisfaction(
S, C->getSubExpr(), Satisfaction,
std::forward<AtomicEvaluator>(Evaluator));
}
@ -164,11 +200,11 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
if (SubstitutedAtomicExpr.isInvalid())
return true;
return ExprError();
if (!SubstitutedAtomicExpr.isUsable())
// Evaluator has decided satisfaction without yielding an expression.
return false;
return ExprEmpty();
EnterExpressionEvaluationContext ConstantEvaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
@ -185,7 +221,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
<< SubstitutedAtomicExpr.get()->getSourceRange();
for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
S.Diag(PDiag.first, PDiag.second);
return true;
return ExprError();
}
assert(EvalResult.Val.isInt() &&
@ -195,10 +231,10 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
Satisfaction.Details.emplace_back(ConstraintExpr,
SubstitutedAtomicExpr.get());
return false;
return SubstitutedAtomicExpr;
}
static bool calculateConstraintSatisfaction(
static ExprResult calculateConstraintSatisfaction(
Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
@ -219,8 +255,8 @@ static bool calculateConstraintSatisfaction(
return ExprError();
// We do not want error diagnostics escaping here.
Sema::SFINAETrap Trap(S);
SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr),
MLTAL);
SubstitutedExpression =
S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
// Substitution might have stripped off a contextual conversion to
// bool if this is the operand of an '&&' or '||'. For example, we
// might lose an lvalue-to-rvalue conversion here. If so, put it back
@ -270,6 +306,7 @@ static bool calculateConstraintSatisfaction(
static bool CheckConstraintSatisfaction(
Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
llvm::SmallVectorImpl<Expr *> &Converted,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
if (ConstraintExprs.empty()) {
@ -294,22 +331,30 @@ static bool CheckConstraintSatisfaction(
return true;
for (const Expr *ConstraintExpr : ConstraintExprs) {
if (calculateConstraintSatisfaction(S, Template, TemplateIDRange.getBegin(),
TemplateArgsLists, ConstraintExpr,
Satisfaction))
ExprResult Res = calculateConstraintSatisfaction(
S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
ConstraintExpr, Satisfaction);
if (Res.isInvalid())
return true;
if (!Satisfaction.IsSatisfied)
Converted.push_back(Res.get());
if (!Satisfaction.IsSatisfied) {
// Backfill the 'converted' list with nulls so we can keep the Converted
// and unconverted lists in sync.
Converted.append(ConstraintExprs.size() - Converted.size(), nullptr);
// [temp.constr.op] p2
// [...] To determine if a conjunction is satisfied, the satisfaction
// of the first operand is checked. If that is not satisfied, the
// conjunction is not satisfied. [...]
// [...] To determine if a conjunction is satisfied, the satisfaction
// of the first operand is checked. If that is not satisfied, the
// conjunction is not satisfied. [...]
return false;
}
}
return false;
}
bool Sema::CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
if (ConstraintExprs.empty()) {
@ -317,9 +362,9 @@ bool Sema::CheckConstraintSatisfaction(
return false;
}
if (!Template) {
return ::CheckConstraintSatisfaction(*this, nullptr, ConstraintExprs,
TemplateArgsLists, TemplateIDRange,
OutSatisfaction);
return ::CheckConstraintSatisfaction(
*this, nullptr, ConstraintExprs, ConvertedConstraints,
TemplateArgsLists, TemplateIDRange, OutSatisfaction);
}
// A list of the template argument list flattened in a predictible manner for
@ -340,8 +385,8 @@ bool Sema::CheckConstraintSatisfaction(
auto Satisfaction =
std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
TemplateArgsLists, TemplateIDRange,
*Satisfaction)) {
ConvertedConstraints, TemplateArgsLists,
TemplateIDRange, *Satisfaction)) {
return true;
}
OutSatisfaction = *Satisfaction;
@ -355,21 +400,120 @@ bool Sema::CheckConstraintSatisfaction(
bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
return calculateConstraintSatisfaction(
*this, ConstraintExpr, Satisfaction,
[this](const Expr *AtomicExpr) -> ExprResult {
// We only do this to immitate lvalue-to-rvalue conversion.
return PerformContextuallyConvertToBool(const_cast<Expr *>(AtomicExpr));
});
*this, ConstraintExpr, Satisfaction,
[this](const Expr *AtomicExpr) -> ExprResult {
// We only do this to immitate lvalue-to-rvalue conversion.
return PerformContextuallyConvertToBool(
const_cast<Expr *>(AtomicExpr));
})
.isInvalid();
}
bool Sema::SetupConstraintScope(
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) {
if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) {
FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
InstantiatingTemplate Inst(
*this, FD->getPointOfInstantiation(),
Sema::InstantiatingTemplate::ConstraintsCheck{}, PrimaryTemplate,
TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
SourceRange());
if (Inst.isInvalid())
return true;
// addInstantiatedParametersToScope creates a map of 'uninstantiated' to
// 'instantiated' parameters and adds it to the context. For the case where
// this function is a template being instantiated NOW, we also need to add
// the list of current template arguments to the list so that they also can
// be picked out of the map.
if (auto *SpecArgs = FD->getTemplateSpecializationArgs()) {
MultiLevelTemplateArgumentList JustTemplArgs(*SpecArgs);
if (addInstantiatedParametersToScope(
FD, PrimaryTemplate->getTemplatedDecl(), Scope, JustTemplArgs))
return true;
}
// If this is a member function, make sure we get the parameters that
// reference the original primary template.
if (const auto *FromMemTempl =
PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
Scope, MLTAL))
return true;
}
return false;
}
if (FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization ||
FD->getTemplatedKind() == FunctionDecl::TK_DependentNonTemplate) {
FunctionDecl *InstantiatedFrom =
FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization
? FD->getInstantiatedFromMemberFunction()
: FD->getInstantiatedFromDecl();
InstantiatingTemplate Inst(
*this, FD->getPointOfInstantiation(),
Sema::InstantiatingTemplate::ConstraintsCheck{}, InstantiatedFrom,
TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
SourceRange());
if (Inst.isInvalid())
return true;
// Case where this was not a template, but instantiated as a
// child-function.
if (addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, MLTAL))
return true;
}
return false;
}
// This function collects all of the template arguments for the purposes of
// constraint-instantiation and checking.
llvm::Optional<MultiLevelTemplateArgumentList>
Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
LocalInstantiationScope &Scope) {
MultiLevelTemplateArgumentList MLTAL;
// Collect the list of template arguments relative to the 'primary' template.
// We need the entire list, since the constraint is completely uninstantiated
// at this point.
MLTAL = getTemplateInstantiationArgs(FD, nullptr, /*RelativeToPrimary*/ true,
/*Pattern*/ nullptr,
/*LookBeyondLambda*/ true);
if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
return {};
return MLTAL;
}
bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
ConstraintSatisfaction &Satisfaction,
SourceLocation UsageLoc) {
const Expr *RC = FD->getTrailingRequiresClause();
if (RC->isInstantiationDependent()) {
SourceLocation UsageLoc,
bool ForOverloadResolution) {
// Don't check constraints if the function is dependent. Also don't check if
// this is a function template specialization, as the call to
// CheckinstantiatedFunctionTemplateConstraints after this will check it
// better.
if (FD->isDependentContext() ||
FD->getTemplatedKind() ==
FunctionDecl::TK_FunctionTemplateSpecialization) {
Satisfaction.IsSatisfied = true;
return false;
}
ContextRAII SavedContext{
*this, cast<DeclContext>(
const_cast<FunctionDecl *>(FD)->getNonClosureContext())};
LocalInstantiationScope Scope(*this, !ForOverloadResolution ||
isLambdaCallOperator(FD));
llvm::Optional<MultiLevelTemplateArgumentList> MLTAL =
SetupConstraintCheckingTemplateArgumentsAndScope(
const_cast<FunctionDecl *>(FD), {}, Scope);
Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
@ -380,10 +524,112 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
// We substitute with empty arguments in order to rebuild the atomic
// constraint in a constant-evaluated context.
// FIXME: Should this be a dedicated TreeTransform?
return CheckConstraintSatisfaction(
FD, {RC}, /*TemplateArgs=*/{},
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
Satisfaction);
const Expr *RC = FD->getTrailingRequiresClause();
llvm::SmallVector<Expr *, 1> Converted;
if (CheckConstraintSatisfaction(
FD, {RC}, Converted, *MLTAL,
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
Satisfaction))
return true;
// FIXME: we need to do this for the function constraints for
// comparison of constraints to work, but do we also need to do it for
// CheckInstantiatedFunctionConstraints? That one is more difficult, but we
// seem to always just pick up the constraints from the primary template.
assert(Converted.size() <= 1 && "Got more expressions converted?");
if (!Converted.empty() && Converted[0] != nullptr)
const_cast<FunctionDecl *>(FD)->setTrailingRequiresClause(Converted[0]);
return false;
}
// Figure out the to-translation-unit depth for this function declaration for
// the purpose of seeing if they differ by constraints. This isn't the same as
// getTemplateDepth, because it includes already instantiated parents.
static unsigned CalculateTemplateDepthForConstraints(Sema &S,
const NamedDecl *ND) {
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
ND, nullptr, /*RelativeToPrimary*/ true,
/*Pattern*/ nullptr,
/*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true);
return MLTAL.getNumSubstitutedLevels();
}
namespace {
class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
unsigned TemplateDepth = 0;
public:
using inherited = TreeTransform<AdjustConstraintDepth>;
AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
: inherited(SemaRef), TemplateDepth(TemplateDepth) {}
QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
TemplateTypeParmTypeLoc TL) {
const TemplateTypeParmType *T = TL.getTypePtr();
TemplateTypeParmDecl *NewTTPDecl = nullptr;
if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
TransformDecl(TL.getNameLoc(), OldTTPDecl));
QualType Result = getSema().Context.getTemplateTypeParmType(
T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
NewTTPDecl);
TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc());
return Result;
}
};
} // namespace
bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
const Expr *OldConstr,
const NamedDecl *New,
const Expr *NewConstr) {
if (Old && New && Old != New) {
unsigned Depth1 = CalculateTemplateDepthForConstraints(
*this, Old);
unsigned Depth2 = CalculateTemplateDepthForConstraints(
*this, New);
// Adjust the 'shallowest' verison of this to increase the depth to match
// the 'other'.
if (Depth2 > Depth1) {
OldConstr = AdjustConstraintDepth(*this, Depth2 - Depth1)
.TransformExpr(const_cast<Expr *>(OldConstr))
.get();
} else if (Depth1 > Depth2) {
NewConstr = AdjustConstraintDepth(*this, Depth1 - Depth2)
.TransformExpr(const_cast<Expr *>(NewConstr))
.get();
}
}
llvm::FoldingSetNodeID ID1, ID2;
OldConstr->Profile(ID1, Context, /*Canonical=*/true);
NewConstr->Profile(ID2, Context, /*Canonical=*/true);
return ID1 == ID2;
}
bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) {
assert(FD->getFriendObjectKind() && "Must be a friend!");
// The logic for non-templates is handled in ASTContext::isSameEntity, so we
// don't have to bother checking 'DependsOnEnclosingTemplate' for a
// non-function-template.
assert(FD->getDescribedFunctionTemplate() &&
"Non-function templates don't need to be checked");
SmallVector<const Expr *, 3> ACs;
FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs);
unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD);
for (const Expr *Constraint : ACs)
if (ConstraintExpressionDependsOnEnclosingTemplate(OldTemplateDepth,
Constraint))
return true;
return false;
}
bool Sema::EnsureTemplateArgumentListConstraints(
@ -432,26 +678,14 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
// PushDeclContext because we don't have a scope.
Sema::ContextRAII savedContext(*this, Decl);
LocalInstantiationScope Scope(*this);
MultiLevelTemplateArgumentList MLTAL;
// FIXME: This will be replaced with some logic to get all the template
// arguments when we switch to deferred template instantiation.
MLTAL.addOuterTemplateArguments(TemplateArgs);
// If this is not an explicit specialization - we need to get the instantiated
// version of the template arguments and add them to scope for the
// substitution.
if (Decl->isTemplateInstantiation()) {
InstantiatingTemplate Inst(*this, Decl->getPointOfInstantiation(),
InstantiatingTemplate::ConstraintsCheck{}, Decl->getPrimaryTemplate(),
TemplateArgs, SourceRange());
if (Inst.isInvalid())
return true;
MultiLevelTemplateArgumentList MLTAL(
*Decl->getTemplateSpecializationArgs());
if (addInstantiatedParametersToScope(
Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), Scope, MLTAL))
return true;
}
Optional<MultiLevelTemplateArgumentList> MLTAL =
SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
Scope);
if (!MLTAL)
return true;
Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
@ -459,7 +693,8 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Record = Method->getParent();
}
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL,
llvm::SmallVector<Expr *, 1> Converted;
return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL,
PointOfInstantiation, Satisfaction);
}
@ -734,22 +969,22 @@ Sema::getNormalizedAssociatedConstraints(
return CacheEntry->second;
}
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
ConceptDecl *Concept, ArrayRef<TemplateArgument> TemplateArgs,
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
static bool
substituteParameterMappings(Sema &S, NormalizedConstraint &N,
ConceptDecl *Concept,
const MultiLevelTemplateArgumentList &MLTAL,
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
if (!N.isAtomic()) {
if (substituteParameterMappings(S, N.getLHS(), Concept, TemplateArgs,
if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
ArgsAsWritten))
return true;
return substituteParameterMappings(S, N.getRHS(), Concept, TemplateArgs,
return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
ArgsAsWritten);
}
TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
AtomicConstraint &Atomic = *N.getAtomicConstraint();
TemplateArgumentListInfo SubstArgs;
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(TemplateArgs);
if (!Atomic.ParameterMapping) {
llvm::SmallBitVector OccurringIndices(TemplateParams->size());
S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
@ -790,6 +1025,20 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
return false;
}
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
const ConceptSpecializationExpr *CSE) {
TemplateArgumentList TAL{TemplateArgumentList::OnStack,
CSE->getTemplateArguments()};
MultiLevelTemplateArgumentList MLTAL =
S.getTemplateInstantiationArgs(CSE->getNamedConcept(), &TAL,
/*RelativeToPrimary*/ true,
/*Pattern*/ nullptr,
/*LookBeyondLambda*/ true);
return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
CSE->getTemplateArgsAsWritten());
}
Optional<NormalizedConstraint>
NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D,
ArrayRef<const Expr *> E) {
@ -852,9 +1101,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
Optional<NormalizedConstraint> New;
New.emplace(S.Context, *SubNF);
if (substituteParameterMappings(
S, *New, CSE->getNamedConcept(),
CSE->getTemplateArguments(), CSE->getTemplateArgsAsWritten()))
if (substituteParameterMappings(S, *New, CSE))
return None;
return New;

View File

@ -10480,6 +10480,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
if (getLangOpts().CPlusPlus) {
// Precalculate whether this is a friend function template with a constraint
// that depends on an enclosing template, per [temp.friend]p9.
if (isFriend && FunctionTemplate &&
FriendConstraintsDependOnEnclosingTemplate(NewFD))
NewFD->setFriendConstraintRefersToEnclosingTemplate(true);
if (FunctionTemplate) {
if (NewFD->isInvalidDecl())
FunctionTemplate->setInvalidDecl();

View File

@ -283,7 +283,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
// definition.
if (FD->getTrailingRequiresClause()) {
ConstraintSatisfaction Satisfaction;
if (CheckFunctionConstraints(FD, Satisfaction, Loc))
if (CheckFunctionConstraints(FD, Satisfaction, Loc,
/*ForOverloadResolution*/ true))
// A diagnostic will have already been generated (non-constant
// constraint expression, for example)
return true;

View File

@ -1074,6 +1074,15 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old,
!shouldLinkPossiblyHiddenDecl(*I, New))
continue;
// C++20 [temp.friend] p9: A non-template friend declaration with a
// requires-clause shall be a definition. A friend function template
// with a constraint that depends on a template parameter from an
// enclosing template shall be a definition. Such a constrained friend
// function or function template declaration does not declare the same
// function or function template as a declaration in any other scope.
if (Context.FriendsDifferByConstraints(OldF, New))
continue;
Match = *I;
return Ovl_Match;
}
@ -6521,7 +6530,8 @@ void Sema::AddOverloadCandidate(
if (Function->getTrailingRequiresClause()) {
ConstraintSatisfaction Satisfaction;
if (CheckFunctionConstraints(Function, Satisfaction) ||
if (CheckFunctionConstraints(Function, Satisfaction, /*Loc*/ {},
/*ForOverloadResolution*/ true) ||
!Satisfaction.IsSatisfied) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
@ -7027,7 +7037,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
if (Method->getTrailingRequiresClause()) {
ConstraintSatisfaction Satisfaction;
if (CheckFunctionConstraints(Method, Satisfaction) ||
if (CheckFunctionConstraints(Method, Satisfaction, /*Loc*/ {},
/*ForOverloadResolution*/ true) ||
!Satisfaction.IsSatisfied) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;

View File

@ -1687,6 +1687,61 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(Scope* S,
return Param;
}
namespace {
class ConstraintRefersToContainingTemplateChecker
: public TreeTransform<ConstraintRefersToContainingTemplateChecker> {
bool Result = false;
unsigned TemplateDepth = 0;
public:
using inherited = TreeTransform<ConstraintRefersToContainingTemplateChecker>;
ConstraintRefersToContainingTemplateChecker(Sema &SemaRef,
unsigned TemplateDepth)
: inherited(SemaRef), TemplateDepth(TemplateDepth) {}
bool getResult() const { return Result; }
// This should be the only template parm type that we have to deal with.
// SubstTempalteTypeParmPack, SubstNonTypeTemplateParmPack, and
// FunctionParmPackExpr are all partially substituted, which cannot happen
// with concepts at this point in translation.
QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
TemplateTypeParmTypeLoc TL) {
assert(TL.getDecl()->getDepth() <= TemplateDepth &&
"Nothing should reference a value below the actual template depth, "
"depth is likely wrong");
if (TL.getDecl()->getDepth() != TemplateDepth)
Result = true;
return inherited::TransformTemplateTypeParmType(TLB, TL);
}
Decl *TransformDecl(SourceLocation Loc, Decl *D) {
// FIXME : This is possibly an incomplete list, but it is unclear what other
// Decl kinds could be used to refer to the template parameters. This is a
// best guess so far based on examples currently available, but the
// unreachable should catch future instances/cases.
if (auto *TD = dyn_cast<TypedefNameDecl>(D))
TransformType(TD->getUnderlyingType());
else if (auto *VD = dyn_cast<ValueDecl>(D))
TransformType(VD->getType());
else if (auto *TD = dyn_cast<TemplateDecl>(D))
TransformTemplateParameterList(TD->getTemplateParameters());
else if (isa<NamedDecl>(D)) {
// No direct types to visit here I believe.
} else
llvm_unreachable("Don't know how to handle this declaration type yet");
return D;
}
};
} // namespace
bool Sema::ConstraintExpressionDependsOnEnclosingTemplate(
unsigned TemplateDepth, const Expr *Constraint) {
ConstraintRefersToContainingTemplateChecker Checker(*this, TemplateDepth);
Checker.TransformExpr(const_cast<Expr *>(Constraint));
return Checker.getResult();
}
/// ActOnTemplateParameterList - Builds a TemplateParameterList, optionally
/// constrained by RequiresClause, that contains the template parameters in
/// Params.
@ -2288,7 +2343,8 @@ private:
TTP->isExpandedParameterPack() ?
llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None);
if (const auto *TC = TTP->getTypeConstraint())
SemaRef.SubstTypeConstraint(NewTTP, TC, Args);
SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
/*EvaluateConstraint*/ true);
if (TTP->hasDefaultArgument()) {
TypeSourceInfo *InstantiatedDefaultArg =
SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
@ -6014,10 +6070,30 @@ bool Sema::CheckTemplateArgumentList(
TemplateArgs = std::move(NewArgs);
if (!PartialTemplateArgs) {
// FIXME: This will be changed a bit once deferred concept instantiation is
// implemented.
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(Converted);
TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack,
Converted);
// Setup the context/ThisScope for the case where we are needing to
// re-instantiate constraints outside of normal instantiation.
DeclContext *NewContext = Template->getDeclContext();
// If this template is in a template, make sure we extract the templated
// decl.
if (auto *TD = dyn_cast<TemplateDecl>(NewContext))
NewContext = Decl::castToDeclContext(TD->getTemplatedDecl());
auto *RD = dyn_cast<CXXRecordDecl>(NewContext);
Qualifiers ThisQuals;
if (const auto *Method =
dyn_cast_or_null<CXXMethodDecl>(Template->getTemplatedDecl()))
ThisQuals = Method->getMethodQualifiers();
ContextRAII Context(*this, NewContext);
CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr);
MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
Template, &StackTemplateArgs, /*RelativeToPrimary*/ true,
/*Pattern*/ nullptr,
/*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true);
if (EnsureTemplateArgumentListConstraints(
Template, MLTAL,
SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
@ -7564,7 +7640,9 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
// are not considered.
if (ParamsAC.empty())
return false;
Template->getAssociatedConstraints(TemplateAC);
bool IsParamAtLeastAsConstrained;
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
IsParamAtLeastAsConstrained))
@ -7760,10 +7838,10 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
}
/// Match two template parameters within template parameter lists.
static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
bool Complain,
Sema::TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc) {
static bool MatchTemplateParameterKind(
Sema &S, NamedDecl *New, const NamedDecl *NewInstFrom, NamedDecl *Old,
const NamedDecl *OldInstFrom, bool Complain,
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
// Check the actual kind (type, non-type, template).
if (Old->getKind() != New->getKind()) {
if (Complain) {
@ -7845,13 +7923,13 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
else if (TemplateTemplateParmDecl *OldTTP
= dyn_cast<TemplateTemplateParmDecl>(Old)) {
TemplateTemplateParmDecl *NewTTP = cast<TemplateTemplateParmDecl>(New);
if (!S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(),
OldTTP->getTemplateParameters(),
Complain,
(Kind == Sema::TPL_TemplateMatch
? Sema::TPL_TemplateTemplateParmMatch
: Kind),
TemplateArgLoc))
if (!S.TemplateParameterListsAreEqual(
NewInstFrom, NewTTP->getTemplateParameters(), OldInstFrom,
OldTTP->getTemplateParameters(), Complain,
(Kind == Sema::TPL_TemplateMatch
? Sema::TPL_TemplateTemplateParmMatch
: Kind),
TemplateArgLoc))
return false;
} else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) {
const Expr *NewC = nullptr, *OldC = nullptr;
@ -7874,10 +7952,8 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
}
if (NewC) {
llvm::FoldingSetNodeID OldCID, NewCID;
OldC->Profile(OldCID, S.Context, /*Canonical=*/true);
NewC->Profile(NewCID, S.Context, /*Canonical=*/true);
if (OldCID != NewCID) {
if (!S.AreConstraintExpressionsEqual(OldInstFrom, OldC, NewInstFrom,
NewC)) {
if (Complain)
Diagnose();
return false;
@ -7933,12 +8009,10 @@ void DiagnoseTemplateParameterListArityMismatch(Sema &S,
///
/// \returns True if the template parameter lists are equal, false
/// otherwise.
bool
Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
TemplateParameterList *Old,
bool Complain,
TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc) {
bool Sema::TemplateParameterListsAreEqual(
const NamedDecl *NewInstFrom, TemplateParameterList *New,
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
if (Complain)
DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
@ -7968,8 +8042,9 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
return false;
}
if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
Kind, TemplateArgLoc))
if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
OldInstFrom, Complain, Kind,
TemplateArgLoc))
return false;
++NewParm;
@ -7984,8 +8059,9 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
// template parameter pack in P (ignoring whether those template
// parameters are template parameter packs).
for (; NewParm != NewParmEnd; ++NewParm) {
if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
Kind, TemplateArgLoc))
if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
OldInstFrom, Complain, Kind,
TemplateArgLoc))
return false;
}
}
@ -8017,10 +8093,8 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
}
if (NewRC) {
llvm::FoldingSetNodeID OldRCID, NewRCID;
OldRC->Profile(OldRCID, Context, /*Canonical=*/true);
NewRC->Profile(NewRCID, Context, /*Canonical=*/true);
if (OldRCID != NewRCID) {
if (!AreConstraintExpressionsEqual(OldInstFrom, OldRC, NewInstFrom,
NewRC)) {
if (Complain)
Diagnose();
return false;

View File

@ -2848,6 +2848,20 @@ template<>
struct IsPartialSpecialization<VarTemplatePartialSpecializationDecl> {
static constexpr bool value = true;
};
template <typename TemplateDeclT>
static bool DeducedArgsNeedReplacement(TemplateDeclT *Template) {
return false;
}
template <>
bool DeducedArgsNeedReplacement<VarTemplatePartialSpecializationDecl>(
VarTemplatePartialSpecializationDecl *Spec) {
return !Spec->isClassScopeExplicitSpecialization();
}
template <>
bool DeducedArgsNeedReplacement<ClassTemplatePartialSpecializationDecl>(
ClassTemplatePartialSpecializationDecl *Spec) {
return !Spec->isClassScopeExplicitSpecialization();
}
template<typename TemplateDeclT>
static Sema::TemplateDeductionResult
@ -2856,13 +2870,25 @@ CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,
TemplateDeductionInfo& Info) {
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
Template->getAssociatedConstraints(AssociatedConstraints);
// FIXME: This will change quite a bit once deferred concept instantiation is
// implemented.
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(DeducedArgs);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints,
MLTAL, Info.getLocation(),
bool NeedsReplacement = DeducedArgsNeedReplacement(Template);
TemplateArgumentList DeducedTAL{TemplateArgumentList::OnStack, DeducedArgs};
MLTAL = S.getTemplateInstantiationArgs(
Template, /*InnerMost*/ NeedsReplacement ? nullptr : &DeducedTAL,
/*RelativeToPrimary*/ true, /*Pattern*/
nullptr, /*LookBeyondLambda*/ true);
// getTemplateInstantiationArgs picks up the non-deduced version of the
// template args when this is a variable template partial specialization and
// not class-scope explicit specialization, so replace with Deduced Args
// instead of adding to inner-most.
if (NeedsReplacement)
MLTAL.replaceInnermostTemplateArguments(DeducedArgs);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
Info.getLocation(),
Info.AssociatedConstraintsSatisfaction) ||
!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs));

View File

@ -57,9 +57,18 @@ using namespace sema;
/// instantiating the definition of the given declaration, \p D. This is
/// used to determine the proper set of template instantiation arguments for
/// friend function template specializations.
///
/// \param LookBeyondLambda Indicates that this collection of arguments should
/// continue looking when it encounters a lambda generic call operator.
///
/// \param IncludeContainingStructArgs Indicates that this collection of
/// arguments should include arguments for any class template that this
/// declaration is included inside of.
MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
const NamedDecl *D, const TemplateArgumentList *Innermost,
bool RelativeToPrimary, const FunctionDecl *Pattern) {
bool RelativeToPrimary, const FunctionDecl *Pattern, bool LookBeyondLambda,
bool IncludeContainingStructArgs) {
// Accumulate the set of template argument lists in this structure.
MultiLevelTemplateArgumentList Result;
@ -155,11 +164,13 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
break;
// If this function is a generic lambda specialization, we are done.
if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
if (!LookBeyondLambda &&
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
break;
} else if (Function->getDescribedFunctionTemplate()) {
assert(Result.getNumSubstitutedLevels() == 0 &&
assert((IncludeContainingStructArgs ||
Result.getNumSubstitutedLevels() == 0) &&
"Outer template not instantiated?");
}
@ -176,10 +187,28 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
}
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(Ctx)) {
if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) {
assert(Result.getNumSubstitutedLevels() == 0 &&
assert((IncludeContainingStructArgs ||
Result.getNumSubstitutedLevels() == 0) &&
"Outer template not instantiated?");
if (ClassTemplate->isMemberSpecialization())
break;
if (IncludeContainingStructArgs) {
QualType RecordType = Context.getTypeDeclType(Rec);
QualType Injected = cast<InjectedClassNameType>(RecordType)
->getInjectedSpecializationType();
const auto *InjectedType = cast<TemplateSpecializationType>(Injected);
Result.addOuterTemplateArguments(InjectedType->template_arguments());
}
}
bool IsFriend = Rec->getFriendObjectKind() ||
(Rec->getDescribedClassTemplate() &&
Rec->getDescribedClassTemplate()->getFriendObjectKind());
if (IncludeContainingStructArgs && IsFriend &&
Rec->getNonTransparentDeclContext()->isFileContext() &&
(!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) {
Ctx = Rec->getLexicalDeclContext();
RelativeToPrimary = false;
continue;
}
}
@ -930,16 +959,23 @@ namespace {
const MultiLevelTemplateArgumentList &TemplateArgs;
SourceLocation Loc;
DeclarationName Entity;
bool EvaluateConstraints = true;
public:
typedef TreeTransform<TemplateInstantiator> inherited;
TemplateInstantiator(Sema &SemaRef,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc,
DeclarationName Entity)
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
Entity(Entity) { }
SourceLocation Loc, DeclarationName Entity)
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
Entity(Entity) {}
void setEvaluateConstraints(bool B) {
EvaluateConstraints = B;
}
bool getEvaluateConstraints() {
return EvaluateConstraints;
}
/// Determine whether the given type \p T has already been
/// transformed.
@ -1146,7 +1182,9 @@ namespace {
ExprResult TransformLambdaExpr(LambdaExpr *E) {
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
return inherited::TransformLambdaExpr(E);
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
ExprResult Res = inherited::TransformLambdaExpr(E);
return Res;
}
ExprResult TransformRequiresExpr(RequiresExpr *E) {
@ -1191,6 +1229,7 @@ namespace {
DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
TemplateDeclInstantiator DeclInstantiator(getSema(),
/* DeclContext *Owner */ Owner, TemplateArgs);
DeclInstantiator.setEvaluateConstraints(EvaluateConstraints);
return DeclInstantiator.SubstTemplateParams(OrigTPL);
}
@ -1766,9 +1805,9 @@ TemplateInstantiator::TransformFunctionTypeParam(ParmVarDecl *OldParm,
int indexAdjustment,
Optional<unsigned> NumExpansions,
bool ExpectParameterPack) {
auto NewParm =
SemaRef.SubstParmVarDecl(OldParm, TemplateArgs, indexAdjustment,
NumExpansions, ExpectParameterPack);
auto NewParm = SemaRef.SubstParmVarDecl(
OldParm, TemplateArgs, indexAdjustment, NumExpansions,
ExpectParameterPack, EvaluateConstraints);
if (NewParm && SemaRef.getLangOpts().OpenCL)
SemaRef.deduceOpenCLAddressSpace(NewParm);
return NewParm;
@ -1988,8 +2027,7 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
Req, Info, OrigTPL->getSourceRange());
if (TPLInst.isInvalid())
return nullptr;
TemplateParameterList *TPL =
TransformTemplateParameterList(OrigTPL);
TemplateParameterList *TPL = TransformTemplateParameterList(OrigTPL);
if (!TPL)
TransRetReq.emplace(createSubstDiag(SemaRef, Info,
[&] (llvm::raw_ostream& OS) {
@ -2199,7 +2237,8 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
SourceLocation Loc,
DeclarationName Entity,
CXXRecordDecl *ThisContext,
Qualifiers ThisTypeQuals) {
Qualifiers ThisTypeQuals,
bool EvaluateConstraints) {
assert(!CodeSynthesisContexts.empty() &&
"Cannot perform an instantiation without some context on the "
"instantiation stack");
@ -2208,6 +2247,7 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
return T;
TemplateInstantiator Instantiator(*this, Args, Loc, Entity);
Instantiator.setEvaluateConstraints(EvaluateConstraints);
TypeLocBuilder TLB;
@ -2352,9 +2392,19 @@ namespace {
bool Sema::SubstTypeConstraint(
TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
const MultiLevelTemplateArgumentList &TemplateArgs) {
const MultiLevelTemplateArgumentList &TemplateArgs,
bool EvaluateConstraints) {
const ASTTemplateArgumentListInfo *TemplArgInfo =
TC->getTemplateArgsAsWritten();
if (!EvaluateConstraints) {
Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(),
TC->getConceptNameInfo(), TC->getNamedConcept(),
TC->getNamedConcept(), TemplArgInfo,
TC->getImmediatelyDeclaredConstraint());
return false;
}
TemplateArgumentListInfo InstArgs;
if (TemplArgInfo) {
@ -2373,11 +2423,11 @@ bool Sema::SubstTypeConstraint(
: SourceLocation());
}
ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
const MultiLevelTemplateArgumentList &TemplateArgs,
int indexAdjustment,
Optional<unsigned> NumExpansions,
bool ExpectParameterPack) {
ParmVarDecl *
Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
const MultiLevelTemplateArgumentList &TemplateArgs,
int indexAdjustment, Optional<unsigned> NumExpansions,
bool ExpectParameterPack, bool EvaluateConstraint) {
TypeSourceInfo *OldDI = OldParm->getTypeSourceInfo();
TypeSourceInfo *NewDI = nullptr;
@ -2435,9 +2485,7 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
// template's described function, but we might also get here later.
// Make sure we do not instantiate the TypeConstraint more than once.
if (Inst && !Inst->getTypeConstraint()) {
// TODO: Concepts: do not instantiate the constraint (delayed constraint
// substitution)
if (SubstTypeConstraint(Inst, TC, TemplateArgs))
if (SubstTypeConstraint(Inst, TC, TemplateArgs, EvaluateConstraint))
return nullptr;
}
}
@ -2746,6 +2794,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
Instantiation->setInvalidDecl();
TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs);
Instantiator.setEvaluateConstraints(false);
SmallVector<Decl*, 4> Fields;
// Delay instantiation of late parsed attributes.
LateInstantiatedAttrVec LateAttrs;
@ -3524,11 +3573,9 @@ bool Sema::SubstTemplateArguments(
ArrayRef<TemplateArgumentLoc> Args,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Out) {
TemplateInstantiator Instantiator(*this, TemplateArgs,
SourceLocation(),
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
DeclarationName());
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(),
Out);
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
}
ExprResult
@ -3542,11 +3589,23 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
return Instantiator.TransformExpr(E);
}
ExprResult
Sema::SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs) {
if (!E)
return E;
// This is where we need to make sure we 'know' constraint checking needs to
// happen.
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
DeclarationName());
return Instantiator.TransformExpr(E);
}
ExprResult Sema::SubstInitializer(Expr *Init,
const MultiLevelTemplateArgumentList &TemplateArgs,
bool CXXDirectInit) {
TemplateInstantiator Instantiator(*this, TemplateArgs,
SourceLocation(),
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
DeclarationName());
return Instantiator.TransformInitializer(Init, CXXDirectInit);
}

View File

@ -1635,12 +1635,16 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
}
if (PrevClassTemplate) {
TemplateParameterList *PrevParams
= PrevClassTemplate->getMostRecentDecl()->getTemplateParameters();
const ClassTemplateDecl *MostRecentPrevCT =
PrevClassTemplate->getMostRecentDecl();
TemplateParameterList *PrevParams =
MostRecentPrevCT->getTemplateParameters();
// Make sure the parameter lists match.
if (!SemaRef.TemplateParameterListsAreEqual(InstParams, PrevParams, true,
Sema::TPL_TemplateMatch))
if (!SemaRef.TemplateParameterListsAreEqual(
D->getTemplatedDecl(), InstParams,
MostRecentPrevCT->getTemplatedDecl(), PrevParams, true,
Sema::TPL_TemplateMatch))
return nullptr;
// Do some additional validation, then merge default arguments
@ -1830,6 +1834,7 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
// merged with the local instantiation scope for the function template
// itself.
LocalInstantiationScope Scope(SemaRef);
Sema::ConstraintEvalRAII<TemplateDeclInstantiator> RAII(*this);
TemplateParameterList *TempParams = D->getTemplateParameters();
TemplateParameterList *InstParams = SubstTemplateParams(TempParams);
@ -2069,19 +2074,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
return nullptr;
}
// FIXME: Concepts: Do not substitute into constraint expressions
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
if (TrailingRequiresClause) {
EnterExpressionEvaluationContext ConstantEvaluated(
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
TemplateArgs);
if (SubstRC.isInvalid())
return nullptr;
TrailingRequiresClause = SubstRC.get();
if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause))
return nullptr;
}
// If we're instantiating a local function declaration, put the result
// in the enclosing namespace; otherwise we need to find the instantiated
@ -2121,6 +2114,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
D->getCanonicalDecl()->getStorageClass(), D->UsesFPIntrin(),
D->isInlineSpecified(), D->hasWrittenPrototype(), D->getConstexprKind(),
TrailingRequiresClause);
Function->setFriendConstraintRefersToEnclosingTemplate(
D->FriendConstraintRefersToEnclosingTemplate());
Function->setRangeEnd(D->getSourceRange().getEnd());
}
@ -2432,23 +2427,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
return nullptr;
}
// FIXME: Concepts: Do not substitute into constraint expressions
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
if (TrailingRequiresClause) {
EnterExpressionEvaluationContext ConstantEvaluated(
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
auto *ThisContext = dyn_cast_or_null<CXXRecordDecl>(Owner);
Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext,
D->getMethodQualifiers(), ThisContext);
ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
TemplateArgs);
if (SubstRC.isInvalid())
return nullptr;
TrailingRequiresClause = SubstRC.get();
if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause))
return nullptr;
}
DeclContext *DC = Owner;
if (isFriend) {
if (QualifierLoc) {
@ -2466,6 +2444,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
if (!DC) return nullptr;
}
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
DeclarationNameInfo NameInfo
= SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
@ -2473,7 +2454,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo);
// Build the instantiated method declaration.
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
CXXMethodDecl *Method = nullptr;
SourceLocation StartLoc = D->getInnerLocStart();
@ -2783,13 +2763,11 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
Inst->setImplicit(D->isImplicit());
if (auto *TC = D->getTypeConstraint()) {
if (!D->isImplicit()) {
// Invented template parameter type constraints will be instantiated with
// the corresponding auto-typed parameter as it might reference other
// parameters.
// TODO: Concepts: do not instantiate the constraint (delayed constraint
// substitution)
if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs))
// Invented template parameter type constraints will be instantiated
// with the corresponding auto-typed parameter as it might reference
// other parameters.
if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs,
EvaluateConstraints))
return nullptr;
}
}
@ -4033,18 +4011,7 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
if (Invalid)
return nullptr;
// FIXME: Concepts: Substitution into requires clause should only happen when
// checking satisfaction.
Expr *InstRequiresClause = nullptr;
if (Expr *E = L->getRequiresClause()) {
EnterExpressionEvaluationContext ConstantEvaluated(
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
ExprResult Res = SemaRef.SubstExpr(E, TemplateArgs);
if (Res.isInvalid() || !Res.isUsable()) {
return nullptr;
}
InstRequiresClause = Res.get();
}
Expr *InstRequiresClause = L->getRequiresClause();
TemplateParameterList *InstL
= TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(),
@ -4338,11 +4305,9 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
ThisTypeQuals = Method->getMethodQualifiers();
}
TypeSourceInfo *NewTInfo
= SemaRef.SubstFunctionDeclType(OldTInfo, TemplateArgs,
D->getTypeSpecStartLoc(),
D->getDeclName(),
ThisContext, ThisTypeQuals);
TypeSourceInfo *NewTInfo = SemaRef.SubstFunctionDeclType(
OldTInfo, TemplateArgs, D->getTypeSpecStartLoc(), D->getDeclName(),
ThisContext, ThisTypeQuals, EvaluateConstraints);
if (!NewTInfo)
return nullptr;

View File

@ -13077,13 +13077,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
NewCallOpType);
}
// Transform the trailing requires clause
ExprResult NewTrailingRequiresClause;
if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause())
// FIXME: Concepts: Substitution into requires clause should only happen
// when checking satisfaction.
NewTrailingRequiresClause = getDerived().TransformExpr(TRC);
// Create the local class that will describe the lambda.
// FIXME: DependencyKind below is wrong when substituting inside a templated
@ -13118,7 +13111,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
E->getCallOperator()->getEndLoc(),
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(),
E->getCallOperator()->getConstexprKind(),
NewTrailingRequiresClause.get());
E->getCallOperator()->getTrailingRequiresClause());
LSI->CallOperator = NewCallOperator;

View File

@ -930,6 +930,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
FD->setHasSkippedBody(Record.readInt());
FD->setIsMultiVersion(Record.readInt());
FD->setLateTemplateParsed(Record.readInt());
FD->setFriendConstraintRefersToEnclosingTemplate(Record.readInt());
FD->setCachedLinkage(static_cast<Linkage>(Record.readInt()));
FD->EndRangeLoc = readSourceLocation();

View File

@ -802,7 +802,7 @@ void ASTStmtReader::VisitConceptSpecializationExpr(
E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo();
llvm::SmallVector<TemplateArgument, 4> Args;
for (unsigned I = 0; I < NumTemplateArgs; ++I)
Args.push_back(Record.readTemplateArgument());
Args.push_back(Record.readTemplateArgument(/*Canonicalize*/ true));
E->setTemplateArguments(Args);
E->Satisfaction = E->isValueDependent() ? nullptr :
ASTConstraintSatisfaction::Create(Record.getContext(),

View File

@ -563,6 +563,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
Record.push_back(D->hasSkippedBody());
Record.push_back(D->isMultiVersion());
Record.push_back(D->isLateTemplateParsed());
Record.push_back(D->FriendConstraintRefersToEnclosingTemplate());
Record.push_back(D->getLinkageInternal());
Record.AddSourceLocation(D->getEndLoc());
@ -2285,6 +2286,7 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // SkippedBody
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // MultiVersion
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // LateParsed
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // FriendConstraintRefersToEnclosingTemplate
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LocEnd
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // ODRHash

View File

@ -90,3 +90,24 @@ struct D { };
static_assert(C<int>{}); // expected-note{{while checking constraint satisfaction for template 'C<int>' required here}}
static_assert(D<int>{}); // expected-note{{while checking constraint satisfaction for template 'D<int>' required here}}
// Test the delayed instantiation, the 'foo' implementation shouldn't cause the
// constraint failure(or crash!) until the use to create 'y'.
namespace DelayedInst {
template <unsigned I>
struct AAA {
template <typename T>
requires(sizeof(T) == I) // expected-note {{because 'sizeof(int) == 5U' (4 == 5) evaluated to false}}
struct B {
static constexpr int a = 0;
};
static constexpr auto foo() {
return B<int>::a; // expected-error{{constraints not satisfied for class template 'B' [with T = int]}}
}
};
constexpr auto x = AAA<4>::foo();
constexpr auto y = AAA<5>::foo(); // expected-note {{in instantiation of member function 'DelayedInst::AAA<5>::foo' requested here}}
} // namespace DelayedInst

View File

@ -52,6 +52,30 @@ static_assert(F<unsigned>::value == 2);
static_assert(F<char[10]>::value == 3);
static_assert(F<char>::value == 1);
template <unsigned I>
struct S {
template <typename T>
struct F {
enum { value = 1 };
};
template <typename T>
requires C1<T> && C2<T>
struct F<T> {
enum { value = 2 };
};
template <typename T>
requires C1<T> || C2<T>
struct F<T> {
enum { value = 3 };
};
};
static_assert(S<1>::F<unsigned>::value == 2);
static_assert(S<1>::F<char[10]>::value == 3);
static_assert(S<1>::F<char>::value == 1);
// Make sure atomic constraints subsume each other only if their parameter
// mappings are identical.

View File

@ -51,5 +51,20 @@ static_assert(f<unsigned> == 2);
static_assert(f<char[10]> == 3);
static_assert(f<char> == 1);
template <int I>
struct S {
template <typename T>
static constexpr int f = 1;
template <typename T>
requires C1<T> && C2<T>
static constexpr int f<T> = 2;
template <typename T>
requires C1<T> || C2<T>
static constexpr int f<T> = 3;
};
static_assert(S<1>::f<unsigned> == 2);
static_assert(S<1>::f<char[10]> == 3);
static_assert(S<1>::f<char> == 1);

View File

@ -0,0 +1,27 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -std=c++20 -fmodules-cache-path=%t -x c++ %s -verify
// expected-no-diagnostics
#pragma clang module build std
module std [system] { module concepts [system] {} }
#pragma clang module contents
#pragma clang module begin std.concepts
template <class T>
T declval();
template<class T, class U>
concept common_reference_with = T::val;
template<class T>
concept input_or_output_iterator = true;
template <class T>
concept input_iterator = input_or_output_iterator<T> &&
common_reference_with<decltype(declval<T&>)&&, T&>;
#pragma clang module end /*std.concepts*/
#pragma clang module endbuild /*std*/
#pragma clang module import std.concepts
template<input_or_output_iterator>
struct iter_value_or_void{};
// ensure that we don't assert on a subsumption check due to improper
// deserialization.
template<input_iterator I>
struct iter_value_or_void<I>{};

View File

@ -196,17 +196,13 @@ static_assert(!__is_trivial(ComplexConstraints<int>), "");
// This is evaluated at the completion of CRTPBase, while `T` is not yet completed.
// This is probably correct behavior.
// FIXME: We should not throw an error, instead SFINAE should make the constraint
// silently unsatisfied. See [temp.constr.constr]p5
template <class T>
struct CRTPBase {
CRTPBase() requires (sizeof(T) > 0); // expected-error {{to an incomplete type}}
CRTPBase() requires (sizeof(T) > 0);
CRTPBase() = default;
};
struct Child : CRTPBase<Child> { int x; };
// expected-note@-1 {{definition of 'Child' is not complete until}}
// expected-note@-2 {{in instantiation of template class 'CRTPBase<Child>' requested here}}
static Child c;

View File

@ -0,0 +1,396 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
template <typename T>
concept constraint = false;
namespace temp_friend_9 {
// A non-template friend declaration with a requires-clause shall be a
// definition. ...Such a constrained friend function ... does not declare the
// same function or function template as a declaration in any other scope.
template <typename T>
struct NonTemplateFriend {
friend void foo()
requires true
{}
};
// A friend function template with a constraint that depends on a template
// parameter from an enclosing template shall be a definition. Such a ...
// function template declaration does not declare the same function or
// function template as a declaration in any other scope.
template <typename T>
struct TemplateFromEnclosing {
template <typename U>
friend void foo()
requires constraint<T>
{}
T variable;
template <typename U>
friend void foo2()
requires constraint<decltype(variable)>
{}
template <typename U>
friend void foo3(T parmvar)
requires constraint<decltype(parmvar)>
{}
template <typename U>
friend void foo4()
requires requires(T &req) { (void)req; }
{}
using Alias = T;
template <typename U>
friend void foo5()
requires constraint<Alias>
{}
// All of these refer to a parent, so these are not duplicate definitions.
struct ChildOfEnclosing {
template <typename U>
friend void foo6()
requires constraint<T>
{}
template <typename U>
friend void foo7()
requires constraint<decltype(variable)>
{}
template <typename U>
friend void foo8(T parmvar)
requires constraint<decltype(parmvar)>
{}
// This is NOT a duplicate since it itself is not a template.
friend void foo9()
requires true
{}
};
template <typename T2>
struct TemplChildOfEnclosing {
template <typename U>
friend void foo10()
requires constraint<T>
{}
};
};
// Doesn't meet either of the requirements in the above as they don't refer to
// an enclosing scope.
template <typename T>
struct Redefinition {
template <typename U>
friend void foo() // #REDEF
requires constraint<U>
{}
struct ChildOfRedef {
template <typename U>
friend void foo2() // #REDEF2
requires constraint<U>
{}
};
template <typename T2>
struct ChildOfRedef2 {
template <typename U>
friend void foo3() // #REDEF3
requires constraint<U>
{}
};
};
void bar() {
NonTemplateFriend<int> S1;
NonTemplateFriend<float> S2;
TemplateFromEnclosing<int> S3;
TemplateFromEnclosing<int>::ChildOfEnclosing S3b;
TemplateFromEnclosing<float> S4;
TemplateFromEnclosing<float>::ChildOfEnclosing S4b;
Redefinition<int> S5;
Redefinition<float> S6;
// expected-error@#REDEF {{redefinition of 'foo'}}
// expected-note@-2{{in instantiation of template class }}
// expected-note@#REDEF {{previous definition is here}}
Redefinition<int>::ChildOfRedef S7;
Redefinition<float>::ChildOfRedef S8;
// expected-error@#REDEF2 {{redefinition of 'foo2'}}
// expected-note@-2{{in instantiation of member class }}
// expected-note@#REDEF2 {{previous definition is here}}
Redefinition<int>::ChildOfRedef2<int> S9;
Redefinition<float>::ChildOfRedef2<float> S10;
// expected-error@#REDEF3 {{redefinition of 'foo3'}}
// expected-note@-2{{in instantiation of template class }}
// expected-note@#REDEF3 {{previous definition is here}}
}
} // namespace temp_friend_9
namespace SameScopeRedefs {
template <typename T>
struct NonTemplateFriend {
friend void foo() // #NTF1
requires true
{}
friend void foo() // #NTF2
requires true
{}
};
template <typename T>
struct TemplateFromEnclosing {
template <typename U>
friend void foo() // #TFE1
requires constraint<T>
{}
template <typename U>
friend void foo() // #TFE2
requires constraint<T>
{}
};
// Same as above, but doesn't require an instantiation pair to cause.
template <typename T>
struct Redefinition {
template <typename U>
friend void foo() // #RD1
requires constraint<U>
{}
template <typename U>
friend void foo() // #RD2
requires constraint<U>
{}
};
void bar() {
NonTemplateFriend<int> S1;
// expected-error@#NTF2 {{redefinition of 'foo'}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#NTF1 {{previous definition is here}}
TemplateFromEnclosing<int> S2;
// expected-error@#TFE2 {{redefinition of 'foo'}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#TFE1 {{previous definition is here}}
Redefinition<int> S3;
// expected-error@#RD2 {{redefinition of 'foo'}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#RD1 {{previous definition is here}}
}
} // namespace SameScopeRedefs
namespace LibCXXOperatorRedef {
template <typename T, typename U> struct is_same {
static constexpr bool value = false;
};
template <typename T> struct is_same<T, T> {
static constexpr bool value = false;
};
template <typename T, typename U>
concept same_as = is_same<T, U>::value;
// An issue found from libcxx when trying to commit the deferred concepts patch.
// This caused an error of 'redefinition of funcN'.
template <class _Tp> struct __range_adaptor_closure {
template <typename _View, typename _Closure>
requires same_as<_Tp, _Closure>
friend constexpr decltype(auto) R1func1(_View &&__view,
_Closure &&__closure){};
template <typename _View, typename _Closure>
friend constexpr decltype(auto) R1func2(_View &&__view,
_Closure &&__closure)
requires same_as<_Tp, _Closure>
{};
template <same_as<_Tp> _View, typename _Closure>
friend constexpr decltype(auto) R1func3(_View &&__view,
_Closure &&__closure){};
};
struct A : __range_adaptor_closure<A> {};
struct B : __range_adaptor_closure<B> {};
// These three fail because after the 1st pass of instantiation, they are still
// identical.
template <class _Tp> struct __range_adaptor_closure2 {
template <typename _View, typename _Closure>
requires same_as<_View, _Closure>
friend constexpr decltype(auto) R2func1(_View &&__view, // #FUNC1
_Closure &&__closure){};
template <typename _View, typename _Closure>
friend constexpr decltype(auto) R2func2(_View &&__view, // #FUNC2
_Closure &&__closure)
requires same_as<_View, _Closure>
{};
template <typename _View, same_as<_View> _Closure>
friend constexpr decltype(auto) R2func3(_View &&__view, // #FUNC3
_Closure &&__closure){};
};
struct A2 : __range_adaptor_closure2<A2> {};
struct B2 : __range_adaptor_closure2<B2> {};
// expected-error@#FUNC1{{redefinition of 'R2func1'}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#FUNC1{{previous definition is here}}
// expected-error@#FUNC2{{redefinition of 'R2func2'}}
// expected-note@#FUNC2{{previous definition is here}}
// expected-error@#FUNC3{{redefinition of 'R2func3'}}
// expected-note@#FUNC3{{previous definition is here}}
// These three are fine, they all depend on the parent template parameter, so
// are different despite ::type not being valid.
template <class _Tp> struct __range_adaptor_closure3 {
template <typename _View, typename _Closure>
requires same_as<typename _Tp::type, _Closure>
friend constexpr decltype(auto) R3func1(_View &&__view,
_Closure &&__closure){};
template <typename _View, typename _Closure>
friend constexpr decltype(auto) R3func2(_View &&__view,
_Closure &&__closure)
requires same_as<typename _Tp::type, _Closure>
{};
template <same_as<typename _Tp::type> _View, typename _Closure>
friend constexpr decltype(auto) R3func3(_View &&__view,
_Closure &&__closure){};
};
struct A3 : __range_adaptor_closure3<A3> {};
struct B3 : __range_adaptor_closure3<B3> {};
template <class _Tp> struct __range_adaptor_closure4 {
template <typename _View, typename _Closure>
requires same_as<_Tp, _View>
// expected-note@+1{{previous definition is here}}
void foo1(_View &&, _Closure &&) {}
template <typename _View, typename _Closure>
requires same_as<_Tp, _View>
// expected-error@+1{{class member cannot be redeclared}}
void foo1(_View &&, _Closure &&) {}
template <typename _View, typename _Closure>
// expected-note@+1{{previous definition is here}}
void foo2(_View &&, _Closure &&)
requires same_as<_Tp, _View>
{}
template <typename _View, typename _Closure>
// expected-error@+1{{class member cannot be redeclared}}
void foo2(_View &&, _Closure &&)
requires same_as<_Tp, _View>
{}
template <same_as<_Tp> _View, typename _Closure>
// expected-note@+1{{previous definition is here}}
void foo3(_View &&, _Closure &&) {}
template <same_as<_Tp> _View, typename _Closure>
// expected-error@+1{{class member cannot be redeclared}}
void foo3(_View &&, _Closure &&) {}
};
// Requires instantiation to fail, so no errors here.
template <class _Tp> struct __range_adaptor_closure5 {
template <same_as<_Tp> U>
friend void foo() {}
template <same_as<_Tp> U>
friend void foo() {}
};
template <class _Tp> struct __range_adaptor_closure6 {
template <same_as<_Tp> U>
friend void foo() {} // #RAC6FOO1
template <same_as<_Tp> U>
friend void foo() {} // #RAC6FOO2
};
struct A6 : __range_adaptor_closure6<A6> {};
// expected-error@#RAC6FOO2{{redefinition of 'foo'}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#RAC6FOO1{{previous definition is here}}
template <class T> struct S1 {
template <typename U>
friend void dupe() {} // #S1DUPE
template <typename U>
requires same_as<U, U>
friend void dupe2() {} // #S1DUPE2
};
template <class T> struct S2 {
template <typename U>
friend void dupe() {} // #S2DUPE
template <typename U>
requires same_as<U, U>
friend void dupe2() {} // #S2DUPE2
};
template <class T> struct S3 {
template <typename U>
requires same_as<T, U>
friend void dupe() {}
};
template <class T> struct S4 {
template <typename U>
requires same_as<T, U>
friend void dupe() {}
};
// Same as S3 and S4, but aren't instantiated with the same T.
template <class T> struct S5 {
template <typename U>
requires same_as<T, U>
friend void not_dupe() {}
};
template <class T> struct S6 {
template <typename U>
requires same_as<T, U>
friend void not_dupe() {}
};
template <class T> struct S7 {
void not_dupe()
requires same_as<T, T>
{}
};
void useS() {
S1<int> s1;
S2<double> s2;
// expected-error@#S2DUPE{{redefinition}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#S1DUPE{{previous definition is here}}
// expected-error@#S2DUPE2{{redefinition}}
// expected-note@#S1DUPE2{{previous definition is here}}
// OK, they have different 'scopes'.
S3<int> s3;
S4<int> s4;
// OK, because only instantiated with different T.
S5<int> s5;
S6<double> s6;
S7<int> s7;
}
} // namespace LibCXXOperatorRedef
namespace NamedDeclRefs {
namespace my_std {
template<typename T, typename U>
concept Outer = true;
template<typename T>
using Inner = T;
}
template<typename T>
struct Proxy {
template<class U>
friend constexpr void RefOuter()
requires my_std::Outer<my_std::Inner<T>, my_std::Inner<U>>{}
template<class U>
friend constexpr void NoRefOuter() // #NOREFOUTER
requires my_std::Outer<my_std::Inner<U>, my_std::Inner<U>>{}
};
void use() {
Proxy<int> p;
Proxy<float> p2;
// expected-error@#NOREFOUTER {{redefinition of 'NoRefOuter'}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#NOREFOUTER{{previous definition is here}}
}
} // namespace NamedDeclRefs

View File

@ -262,3 +262,449 @@ template<class, template <class> class> concept C = true;
template <class> struct S {};
void f(C<GH55567::S> auto);
} // namespace GH55567
namespace SubConstraintChecks {
template <typename T>
concept TrueConstraint = true;
template <typename T>
concept FalseConstraint = false;
template <typename T, typename... Us>
class ContainsConstrainedFuncTrue {
public:
template <typename V, TrueConstraint Constrained>
static void func(V &&, Constrained &&C);
};
template <typename T, typename... Us>
class ContainsConstrainedFuncFalse {
public:
template <typename V, FalseConstraint Constrained>
static void func(V &&, Constrained &&C);
};
template <typename... Us>
concept TrueConstraint2 =
requires(float &&t) {
ContainsConstrainedFuncTrue<float, Us...>::func(5, 0.0);
};
template <typename... Us>
concept FalseConstraint2 =
requires(float &&t) {
ContainsConstrainedFuncFalse<float, Us...>::func(5, 0.0); // #FC2_CONSTR
};
template <typename T>
void useTrue(int F)
requires TrueConstraint2<int>
{}
template <typename T>
void useFalse(int F) // #USE_FALSE
requires FalseConstraint2<int> // #USE_FALSE_CONSTR
{}
// Should only diagnose 'false' once instantiated.
void UseUse() {
useTrue<int>(5);
useFalse<int>(5);
// expected-error@-1{{no matching function for call to 'useFalse'}}
// expected-note@#USE_FALSE{{constraints not satisfied}}
// expected-note@#USE_FALSE_CONSTR{{because 'int' does not satisfy 'FalseConstraint2'}}
// expected-note@#FC2_CONSTR {{would be invalid: no matching function for call to 'func'}}
}
} // namespace SubConstraintChecks
namespace DeducedTemplateArgs {
template <typename Itr> struct ItrTraits {
template <typename PtrItr> struct Ptr {
};
template <typename PtrItr>
requires requires { typename PtrItr::pointer; }
struct Ptr<PtrItr> {
using type = typename Itr::pointer;
};
using pointer = typename Ptr<Itr>::type; // #TRAITS_PTR
};
struct complete_itr {
using pointer = int;
};
template <typename T> class Complete {
using ItrType = ItrTraits<complete_itr>;
ItrType begin() noexcept { return ItrType(); }
};
// This version doesn't have 'pointer', so error confirms we are in the first
// verison of 'Ptr'.
struct not_complete_itr {
};
template <typename T> class NotComplete {
using ItrType = ItrTraits<not_complete_itr>;
ItrType begin() noexcept { return ItrType(); }
// expected-error@#TRAITS_PTR{{no type named 'type' in }}
// expected-note@-2{{in instantiation of template class }}
};
} // namespace DeducedTemplateArgs
namespace DeferredInstantiationInstScope {
template <typename T>
struct remove_ref {
using type = T;
};
template <typename T>
struct remove_ref<T &> {
using type = T;
};
template <typename T>
struct remove_ref<T &&> {
using type = T;
};
template <typename T>
constexpr bool IsInt = PR54443::is_same<typename remove_ref<T>::type,
int>::value;
template <typename U>
void SingleDepthReferencesTop(U &&u) {
struct lc {
void operator()() // #SDRT_OP
requires IsInt<decltype(u)> // #SDRT_REQ
{}
};
lc lv;
lv(); // #SDRT_CALL
}
template <typename U>
void SingleDepthReferencesTopNotCalled(U &&u) {
struct lc {
void operator()()
requires IsInt<typename decltype(u)::FOO>
{}
};
lc lv;
}
template <typename U>
void SingleDepthReferencesTopCalled(U &&u) {
struct lc {
void operator()() // #CALLOP
requires IsInt<typename decltype(u)::FOO> // #CONSTR
{}
};
lc lv;
lv();
// expected-error@-1{{no matching function for call to object of type 'lc'}}
// expected-note@#SDRTC{{in instantiation of function template}}
// expected-note@#CALLOP{{constraints not satisfied}}
// expected-note@#CONSTR{{substituted constraint expression is ill-formed}}
}
template <typename U>
void SingleDepthReferencesTopLambda(U &&u) {
[]()
requires IsInt<decltype(u)>
{}();
}
template <typename U>
void DoubleDepthReferencesTop(U &&u) {
struct lc { // #DDRT_STRCT
void operator()() {
struct lc2 {
void operator()() // #DDRT_OP
requires IsInt<decltype(u)> // #DDRT_REQ
{}
};
lc2 lv2;
lv2(); // #DDRT_CALL
}
};
lc lv;
lv();
}
template <typename U>
void DoubleDepthReferencesTopLambda(U &&u) {
[]() { []()
requires IsInt<decltype(u)>
{}(); }();
}
template <typename U>
void DoubleDepthReferencesAll(U &&u) {
struct lc { // #DDRA_STRCT
void operator()(U &&u2) {
struct lc2 {
void operator()(U &&u3) // #DDRA_OP
requires IsInt<decltype(u)> && // #DDRA_REQ
IsInt<decltype(u2)> && IsInt<decltype(u3)>
{}
};
lc2 lv2;
lv2(u2); // #DDRA_CALL
}
};
lc lv;
lv(u);
}
template <typename U>
void DoubleDepthReferencesAllLambda(U &&u) {
[](U &&u2) {
[](U && u3)
requires IsInt<decltype(u)> &&
IsInt<decltype(u2)> && IsInt<decltype(u3)>
{}(u2);
}(u);
}
template <typename U>
struct CausesFriendConstraint {
template <typename V>
friend void FriendFunc(CausesFriendConstraint, V) // #FF_DECL
requires IsInt<U> &&
IsInt<V> // #FF_REQ
{}
};
// FIXME: Re-enable this test when constraints are allowed to refer to captures.
// template<typename T>
// void ChecksCapture(T x) {
// [y = x]() requires(IsInt<decltype(y)>){}();
// }
template <typename T>
void ChecksLocalVar(T x) {
T Local;
[]()
requires(IsInt<decltype(Local)>)
{}();
}
template <typename T>
void LocalStructMemberVar(T x) {
struct S {
T local;
void foo()
requires(IsInt<decltype(local)>) // #LSMV_REQ
{}
} s;
s.foo(); // #LSMV_CALL
};
template <typename T>
struct ChecksMemberVar {
T t;
void foo()
requires(IsInt<decltype(t)>) // #CMV_FOO
{}
template <typename U>
void foo2() // #CMV_FOO2
requires(IsInt<decltype(t)>) // #CMV_FOO2_REQ
{}
};
void test_dependent() {
int v = 0;
float will_fail;
SingleDepthReferencesTop(v);
SingleDepthReferencesTop(will_fail);
// expected-error@#SDRT_CALL{{no matching function for call to object of type 'lc'}}
// expected-note@-2{{in instantiation of function template specialization}}
// expected-note@#SDRT_OP{{candidate function not viable}}
// expected-note@#SDRT_REQ{{'IsInt<decltype(u)>' evaluated to false}}
SingleDepthReferencesTopNotCalled(v);
// Won't error unless we try to call it.
SingleDepthReferencesTopNotCalled(will_fail);
SingleDepthReferencesTopCalled(v); // #SDRTC
SingleDepthReferencesTopLambda(v);
// FIXME: This should error on constraint failure! (Lambda!)
SingleDepthReferencesTopLambda(will_fail);
DoubleDepthReferencesTop(v);
DoubleDepthReferencesTop(will_fail);
// expected-error@#DDRT_CALL{{no matching function for call to object of type 'lc2'}}
// expected-note@-2{{in instantiation of function template specialization}}
// expected-note@#DDRT_STRCT{{in instantiation of member function}}
// expected-note@#DDRT_OP{{candidate function not viable}}
// expected-note@#DDRT_REQ{{'IsInt<decltype(u)>' evaluated to false}}
DoubleDepthReferencesTopLambda(v);
// FIXME: This should error on constraint failure! (Lambda!)
DoubleDepthReferencesTopLambda(will_fail);
DoubleDepthReferencesAll(v);
DoubleDepthReferencesAll(will_fail);
// expected-error@#DDRA_CALL{{no matching function for call to object of type 'lc2'}}
// expected-note@-2{{in instantiation of function template specialization}}
// expected-note@#DDRA_STRCT{{in instantiation of member function}}
// expected-note@#DDRA_OP{{candidate function not viable}}
// expected-note@#DDRA_REQ{{'IsInt<decltype(u)>' evaluated to false}}
DoubleDepthReferencesAllLambda(v);
// FIXME: This should error on constraint failure! (Lambda!)
DoubleDepthReferencesAllLambda(will_fail);
CausesFriendConstraint<int> CFC;
FriendFunc(CFC, 1);
FriendFunc(CFC, 1.0);
// expected-error@-1{{no matching function for call to 'FriendFunc'}}
// expected-note@#FF_DECL{{constraints not satisfied}}
// expected-note@#FF_REQ{{because 'IsInt<double>' evaluated to false}}
// FIXME: Re-enable this test when constraints are allowed to refer to captures.
// ChecksCapture(v);
ChecksLocalVar(v);
// FIXME: This should error on constraint failure! (Lambda!)
ChecksLocalVar(will_fail);
LocalStructMemberVar(v);
LocalStructMemberVar(will_fail);
// expected-error@#LSMV_CALL{{invalid reference to function 'foo'}}
// expected-note@-2{{in instantiation of function template specialization}}
// expected-note@#LSMV_REQ{{because 'IsInt<decltype(this->local)>' evaluated to false}}
ChecksMemberVar<int> CMV;
CMV.foo();
CMV.foo2<int>();
ChecksMemberVar<float> CMV2;
CMV2.foo();
// expected-error@-1{{invalid reference to function 'foo'}}
// expected-note@#CMV_FOO{{because 'IsInt<decltype(this->t)>' evaluated to false}}
CMV2.foo2<float>();
// expected-error@-1{{no matching member function for call to 'foo2'}}
// expected-note@#CMV_FOO2{{constraints not satisfied}}
// expected-note@#CMV_FOO2_REQ{{because 'IsInt<decltype(this->t)>' evaluated to false}}
}
} // namespace DeferredInstantiationInstScope
// Ane example of evaluating a concept at two different depths in the same
// evaluation. No diagnostic is expected.
namespace SameConceptDifferentDepth {
template <class _Ip>
concept sentinel_for =
requires(_Ip __i) {
__i++;
};
template <class _Ip>
concept bidirectional_iterator =
sentinel_for<_Ip>;
template <class _Iter>
class move_iterator {
public:
auto operator++(int)
requires sentinel_for<_Iter>{}
};
static_assert(bidirectional_iterator<move_iterator<int>>);
} // namespace SameConceptDifferentDepth
namespace VarInit {
template <class _Tp>
concept __can_reference = true;
template <class _Iter>
class common_iterator {
public:
common_iterator() {
constexpr auto x = requires(_Iter & __i) { { __i } -> __can_reference; };
}
};
void test() {
auto commonIter1 = common_iterator<int>();
}
} // namespace VarInit
namespace InlineFriendOperator {
template <typename T>
concept C = true;
template <class _Iter>
class counted_iterator {
_Iter I;
public:
constexpr counted_iterator() = default;
friend constexpr auto operator+( // expected-note {{candidate function not viable}}
int __n, const counted_iterator &__x)
requires C<decltype(I)>
{
return __x + __n; // expected-error{{invalid operands to binary expression}}
}
};
constexpr bool test() {
counted_iterator<int> iter;
auto x = 2 + iter; // expected-note{{in instantiation of member function 'InlineFriendOperator::operator+'}}
return true;
}
} // namespace InlineFriendOperator
namespace ClassTemplateInstantiation {
struct Type;
template < typename A, typename B, typename C>
concept ConstraintF = false; // #ConstraintF
template < typename A, typename B, typename C>
concept ConstraintT = true;
template < typename T > struct Parent {
template < typename U, ConstraintT<T, U> > struct ChildT{};
ChildT<Type, Type> CauseInstT;
template < typename U, ConstraintF<T, U> > struct ChildF{};// #ChildF
ChildF<Type, Type> CauseInstF; //#CauseInstF
};
// expected-error@#CauseInstF{{constraints not satisfied for class template}}
// expected-note@+3{{in instantiation of template class}}
// expected-note@#ChildF{{evaluated to false}}
// expected-note@#ConstraintF{{because 'false' evaluated to false}}
Parent<int> Inst;
} // namespace ClassTemplateInstantiation
namespace SelfFriend {
template<class T>
concept Constraint = requires (T i) { (*i); };
template<class T>
concept Constraint2 = requires (T i) { (*i); };
template<Constraint T>
struct Iterator {
template <Constraint>
friend class Iterator;
void operator*();
};
template<Constraint T> // #ITER_BAD
struct IteratorBad {
template <Constraint2>//#ITER_BAD_FRIEND
friend class IteratorBad;
void operator*();
};
Iterator<int*> I;
Iterator<char*> I2;
IteratorBad<int*> I3; // expected-error@#ITER_BAD_FRIEND{{constraint differs}}
// expected-note@-1{{in instantiation of template class}}
// expected-note@#ITER_BAD{{previous template declaration}}
} // namespace SelfFriend
namespace ConstrainedMemberVarTemplate {
template <long Size> struct Container {
static constexpr long arity = Size;
template <typename U>
requires(sizeof(U) == arity) // #CMVT_REQ
using var_templ = int;
};
Container<4>::var_templ<int> inst;
Container<5>::var_templ<int> inst_fail;
// expected-error@-1{{constraints not satisfied for alias template 'var_templ'}}
// expected-note@#CMVT_REQ{{because 'sizeof(int) == arity' (4 == 5) evaluated to false}}
} // namespace ConstrainedMemberVarTemplate

View File

@ -0,0 +1,23 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify -fsyntax-only -Wno-unused-value
// expected-no-diagnostics
namespace GithubBug44178 {
template <typename D>
struct CRTP {
void call_foo()
requires requires(D &v) { v.foo(); }
{
static_cast<D *>(this)->foo();
}
};
struct Test : public CRTP<Test> {
void foo() {}
};
int main() {
Test t;
t.call_foo();
return 0;
}
} // namespace GithubBug44178

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
// RUN: %clang_cc1 -std=c++2a -x c++ %s -Wno-unused-value -verify
template <typename... Args> requires ((sizeof(Args) == 1), ...)
// expected-note@-1 {{because '(sizeof(int) == 1) , (sizeof(char) == 1) , (sizeof(int) == 1)' evaluated to false}}
@ -40,6 +40,20 @@ struct S {
static_assert(S<void>::f(1));
// Similar to the 'S' test, but tries to use 'U' in the requires clause.
template <typename T2>
struct S1 {
// expected-note@+3 {{candidate template ignored: constraints not satisfied [with U = int]}}
// expected-note@+3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}}
template <typename U>
static constexpr auto f(U const index)
requires(U::foo)
{ return true; }
};
// expected-error@+1 {{no matching function for call to 'f'}}
static_assert(S1<void>::f(1));
constexpr auto value = 0;
template<typename T>

View File

@ -0,0 +1,62 @@
// RUN: %clang_cc1 -std=c++20 -verify %s
template <class T>
requires(sizeof(T) > 2) || T::value // #FOO_REQ
void Foo(T){}; // #FOO
template <class T>
void TrailingReturn(T) // #TRAILING
requires(sizeof(T) > 2) || // #TRAILING_REQ
T::value // #TRAILING_REQ_VAL
{};
template <bool B>
struct HasValue {
static constexpr bool value = B;
};
static_assert(sizeof(HasValue<true>) <= 2);
template <bool B>
struct HasValueLarge {
static constexpr bool value = B;
int I;
};
static_assert(sizeof(HasValueLarge<true>) > 2);
void usage() {
// Passes the 1st check, short-circuit so the 2nd ::value is not evaluated.
Foo(1.0);
TrailingReturn(1.0);
// Fails the 1st check, but has a ::value, so the check happens correctly.
Foo(HasValue<true>{});
TrailingReturn(HasValue<true>{});
// Passes the 1st check, but would have passed the 2nd one.
Foo(HasValueLarge<true>{});
TrailingReturn(HasValueLarge<true>{});
// Fails the 1st check, fails 2nd because there is no ::value.
Foo(true);
// expected-error@-1{{no matching function for call to 'Foo'}}
// expected-note@#FOO{{candidate template ignored: constraints not satisfied [with T = bool]}}
// expected-note@#FOO_REQ{{because 'sizeof(_Bool) > 2' (1 > 2) evaluated to false}}
// expected-note@#FOO_REQ{{because substituted constraint expression is ill-formed: type 'bool' cannot be used prior to '::' because it has no members}}
TrailingReturn(true);
// expected-error@-1{{no matching function for call to 'TrailingReturn'}}
// expected-note@#TRAILING{{candidate template ignored: constraints not satisfied [with T = bool]}}
// expected-note@#TRAILING_REQ{{because 'sizeof(_Bool) > 2' (1 > 2) evaluated to false}}
// expected-note@#TRAILING_REQ_VAL{{because substituted constraint expression is ill-formed: type 'bool' cannot be used prior to '::' because it has no members}}
// Fails the 1st check, fails 2nd because ::value is false.
Foo(HasValue<false>{});
// expected-error@-1 {{no matching function for call to 'Foo'}}
// expected-note@#FOO{{candidate template ignored: constraints not satisfied [with T = HasValue<false>]}}
// expected-note@#FOO_REQ{{because 'sizeof(HasValue<false>) > 2' (1 > 2) evaluated to false}}
// expected-note@#FOO_REQ{{and 'HasValue<false>::value' evaluated to false}}
TrailingReturn(HasValue<false>{});
// expected-error@-1 {{no matching function for call to 'TrailingReturn'}}
// expected-note@#TRAILING{{candidate template ignored: constraints not satisfied [with T = HasValue<false>]}}
// expected-note@#TRAILING_REQ{{because 'sizeof(HasValue<false>) > 2' (1 > 2) evaluated to false}}
// expected-note@#TRAILING_REQ_VAL{{and 'HasValue<false>::value' evaluated to false}}
}