"Re-apply 4b6c2cd642 "Deferred Concept Instantiation Implementation""""

This includes a fix for the libc++ issue I ran across with friend
declarations not properly being identified as overloads.

This reverts commit 45c07db31cc76802a1a2e41bed1ce9c1b8198181.
This commit is contained in:
Erich Keane 2022-05-05 08:35:13 -07:00
parent d38915ffeb
commit a425cac31e
23 changed files with 1145 additions and 173 deletions

View File

@ -323,10 +323,11 @@ C++20 Feature Support
- No longer attempt to evaluate a consteval UDL function call at runtime when
it is called through a template instantiation. This fixes
`Issue 54578 <https://github.com/llvm/llvm-project/issues/54578>`_.
- Implemented ``__builtin_source_location()``, which enables library support
for ``std::source_location``.
- Implemented `__builtin_source_location()` which enables library support for std::source_location.
- 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>`_.
- The mangling scheme for C++20 modules has incompatibly changed. The
initial mangling was discovered not to be reversible, and the weak
ownership design decision did not give the backwards compatibility

View File

@ -1890,7 +1890,9 @@ public:
TK_FunctionTemplateSpecialization,
// A function template specialization that hasn't yet been resolved to a
// particular specialized function template.
TK_DependentFunctionTemplateSpecialization
TK_DependentFunctionTemplateSpecialization,
// A non templated function which is in a dependent scope.
TK_DependentNonTemplate
};
/// Stashed information about a defaulted function definition whose body has
@ -1939,20 +1941,21 @@ private:
/// The template or declaration that this declaration
/// describes or was instantiated from, respectively.
///
/// For non-templates, this value will be NULL. For function
/// declarations that describe a function template, this will be a
/// pointer to a FunctionTemplateDecl. For member functions
/// of class template specializations, this will be a MemberSpecializationInfo
/// pointer containing information about the specialization.
/// For function template specializations, this will be a
/// FunctionTemplateSpecializationInfo, which contains information about
/// the template being specialized and the template arguments involved in
/// that specialization.
llvm::PointerUnion<FunctionTemplateDecl *,
MemberSpecializationInfo *,
/// For non-templates this value will be NULL, unless this non-template
/// function declaration was declared directly inside of a function template,
/// in which case this will have a pointer to a FunctionDecl, stored in the
/// NamedDecl. For function declarations that describe a function template,
/// this will be a pointer to a FunctionTemplateDecl, stored in the NamedDecl.
/// For member functions of class template specializations, this will be a
/// MemberSpecializationInfo pointer containing information about the
/// specialization. For function template specializations, this will be a
/// FunctionTemplateSpecializationInfo, which contains information about the
/// template being specialized and the template arguments involved in that
/// specialization.
llvm::PointerUnion<NamedDecl *, MemberSpecializationInfo *,
FunctionTemplateSpecializationInfo *,
DependentFunctionTemplateSpecializationInfo *>
TemplateOrSpecialization;
TemplateOrSpecialization;
/// Provides source/type location info for the declaration name embedded in
/// the DeclaratorDecl base class.
@ -2688,6 +2691,11 @@ public:
setInstantiationOfMemberFunction(getASTContext(), FD, TSK);
}
/// Specify that this function declaration was instantiated from FunctionDecl
/// FD. This is only used if this is a function declaration declared locally
/// inside of a function template.
void setInstantiatedFromDecl(FunctionDecl *FD);
/// Retrieves the function template that is described by this
/// function declaration.
///
@ -2702,6 +2710,8 @@ public:
/// FunctionTemplateDecl from a FunctionDecl.
FunctionTemplateDecl *getDescribedFunctionTemplate() const;
FunctionDecl *getInstantiatedFromDecl() const;
void setDescribedFunctionTemplate(FunctionTemplateDecl *Template);
/// Determine whether this function is a function template

View File

@ -906,6 +906,14 @@ public:
const_cast<const Decl*>(this)->getParentFunctionOrMethod());
}
/// Does the same thing as getParentFunctionOrMethod, except starts with the
/// lexical declaration context instead.
const DeclContext *getLexicalParentFunctionOrMethod() const;
DeclContext *getLexicalParentFunctionOrMethod() {
return const_cast<DeclContext *>(
const_cast<const Decl *>(this)->getLexicalParentFunctionOrMethod());
}
/// Retrieves the "canonical" declaration of the given declaration.
virtual Decl *getCanonicalDecl() { return this; }
const Decl *getCanonicalDecl() const {

View File

@ -6989,7 +6989,34 @@ 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);
// Keep track of whether we are evaluating a constraint.
unsigned ConstraintEvaluationDepth = 0;
class ConstraintEvalRAII {
Sema &S;
public:
ConstraintEvalRAII(Sema &S) : S(S) { ++S.ConstraintEvaluationDepth; }
~ConstraintEvalRAII() { --S.ConstraintEvaluationDepth; }
};
public:
bool IsEvaluatingAConstraint() { return ConstraintEvaluationDepth > 0; }
const NormalizedConstraint *
getNormalizedAssociatedConstraints(
NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints);
@ -7019,8 +7046,10 @@ public:
/// check (either a concept or a constrained entity).
/// \param ConstraintExprs a list of constraint expressions, treated as if
/// they were 'AND'ed together.
/// \param TemplateArgs the list of template arguments to substitute into the
/// constraint expression.
/// \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
@ -7030,7 +7059,40 @@ public:
/// false otherwise.
bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
ArrayRef<TemplateArgument> TemplateArgs,
const MultiLevelTemplateArgumentList &TemplateArgList,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
llvm::SmallVector<Expr *, 4> Converted;
return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted,
TemplateArgList, 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
@ -7066,9 +7128,9 @@ public:
///
/// \returns true if the constrains are not satisfied or could not be checked
/// for satisfaction, false if the constraints are satisfied.
bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template,
ArrayRef<TemplateArgument> TemplateArgs,
SourceRange TemplateIDRange);
bool EnsureTemplateArgumentListConstraints(
TemplateDecl *Template, MultiLevelTemplateArgumentList TemplateArgs,
SourceRange TemplateIDRange);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
@ -8802,7 +8864,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
@ -9535,6 +9598,11 @@ public:
ExtParameterInfoBuilder &ParamInfos);
ExprResult SubstExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);
// 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.

View File

@ -75,6 +75,9 @@ enum class TemplateSubstitutionKind : char {
class MultiLevelTemplateArgumentList {
/// The template argument list at a certain template depth
using ArgList = ArrayRef<TemplateArgument>;
using ArgListsIterator = SmallVector<ArgList, 4>::reverse_iterator;
using ConstArgListsIterator =
SmallVector<ArgList, 4>::const_reverse_iterator;
/// The template argument lists, stored from the innermost template
/// argument list (first) to the outermost template argument list (last).
@ -121,6 +124,12 @@ enum class TemplateSubstitutionKind : char {
return TemplateArgumentLists.size();
}
/// Determine the number of substituted args at 'Depth'.
unsigned getNumSubstitutedArgs(unsigned Depth) const {
assert(NumRetainedOuterLevels <= Depth && Depth < getNumLevels());
return TemplateArgumentLists[getNumLevels() - Depth - 1].size();
}
unsigned getNumRetainedOuterLevels() const {
return NumRetainedOuterLevels;
}
@ -158,6 +167,14 @@ enum class TemplateSubstitutionKind : char {
return !(*this)(Depth, Index).isNull();
}
bool isAnyArgInstantiationDependent() const {
for (ArgList List : TemplateArgumentLists)
for (const TemplateArgument &TA : List)
if (TA.isInstantiationDependent())
return true;
return false;
}
/// Clear out a specific template argument.
void setArgument(unsigned Depth, unsigned Index,
TemplateArgument Arg) {
@ -197,6 +214,16 @@ enum class TemplateSubstitutionKind : char {
const ArgList &getInnermost() const {
return TemplateArgumentLists.front();
}
/// Retrieve the outermost template argument list.
const ArgList &getOutermost() const { return TemplateArgumentLists.back(); }
ArgListsIterator begin() { return TemplateArgumentLists.rbegin(); }
ConstArgListsIterator begin() const {
return TemplateArgumentLists.rbegin();
}
ArgListsIterator end() { return TemplateArgumentLists.rend(); }
ConstArgListsIterator end() const { return TemplateArgumentLists.rend(); }
};
/// The context in which partial ordering of function templates occurs.

View File

@ -3109,6 +3109,11 @@ Error ASTNodeImporter::ImportTemplateInformation(
case FunctionDecl::TK_FunctionTemplate:
return Error::success();
case FunctionDecl::TK_DependentNonTemplate:
if (Expected<FunctionDecl *> InstFDOrErr =
import(FromFD->getInstantiatedFromDecl()))
ToFD->setInstantiatedFromDecl(*InstFDOrErr);
return Error::success();
case FunctionDecl::TK_MemberSpecialization: {
TemplateSpecializationKind TSK = FromFD->getTemplateSpecializationKind();

View File

@ -3729,8 +3729,12 @@ const IdentifierInfo *FunctionDecl::getLiteralIdentifier() const {
FunctionDecl::TemplatedKind FunctionDecl::getTemplatedKind() const {
if (TemplateOrSpecialization.isNull())
return TK_NonTemplate;
if (TemplateOrSpecialization.is<FunctionTemplateDecl *>())
if (auto *ND = TemplateOrSpecialization.dyn_cast<NamedDecl *>()) {
if (isa<FunctionDecl>(ND))
return TK_DependentNonTemplate;
assert(isa<FunctionTemplateDecl>(ND) && "No other types it could be?");
return TK_FunctionTemplate;
}
if (TemplateOrSpecialization.is<MemberSpecializationInfo *>())
return TK_MemberSpecialization;
if (TemplateOrSpecialization.is<FunctionTemplateSpecializationInfo *>())
@ -3771,13 +3775,26 @@ FunctionDecl::setInstantiationOfMemberFunction(ASTContext &C,
}
FunctionTemplateDecl *FunctionDecl::getDescribedFunctionTemplate() const {
return TemplateOrSpecialization.dyn_cast<FunctionTemplateDecl *>();
return dyn_cast_or_null<FunctionTemplateDecl>(
TemplateOrSpecialization.dyn_cast<NamedDecl *>());
}
void FunctionDecl::setDescribedFunctionTemplate(FunctionTemplateDecl *Template) {
void FunctionDecl::setDescribedFunctionTemplate(
FunctionTemplateDecl *Template) {
assert(TemplateOrSpecialization.isNull() &&
"Member function is already a specialization");
TemplateOrSpecialization = Template;
TemplateOrSpecialization = static_cast<NamedDecl *>(Template);
}
void FunctionDecl::setInstantiatedFromDecl(FunctionDecl *FD) {
assert(TemplateOrSpecialization.isNull() &&
"function is already a specialization");
TemplateOrSpecialization = static_cast<NamedDecl *>(FD);
}
FunctionDecl *FunctionDecl::getInstantiatedFromDecl() const {
return dyn_cast_or_null<FunctionDecl>(
TemplateOrSpecialization.dyn_cast<NamedDecl *>());
}
bool FunctionDecl::isImplicitlyInstantiable() const {

View File

@ -293,6 +293,16 @@ const DeclContext *Decl::getParentFunctionOrMethod() const {
return nullptr;
}
const DeclContext *Decl::getLexicalParentFunctionOrMethod() const {
for (const DeclContext *DC = getLexicalDeclContext();
DC && !DC->isTranslationUnit() && !DC->isNamespace();
DC = DC->getParent())
if (DC->isFunctionOrMethod())
return DC;
return nullptr;
}
//===----------------------------------------------------------------------===//
// PrettyStackTraceDecl Implementation
//===----------------------------------------------------------------------===//

View File

@ -303,6 +303,7 @@ public:
// Skip templated functions.
switch (Decl->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
case FunctionDecl::TK_DependentNonTemplate:
break;
case FunctionDecl::TK_MemberSpecialization:
case FunctionDecl::TK_FunctionTemplateSpecialization:

View File

@ -30,6 +30,7 @@ using namespace sema;
namespace {
class LogicalBinOp {
SourceLocation Loc;
OverloadedOperatorKind Op = OO_None;
const Expr *LHS = nullptr;
const Expr *RHS = nullptr;
@ -40,12 +41,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 +59,25 @@ public:
const Expr *getLHS() const { return LHS; }
const Expr *getRHS() const { return RHS; }
ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS) {
return recreateBinOp(SemaRef, LHS, const_cast<Expr *>(getRHS()));
}
ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS, ExprResult RHS) {
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 +144,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 +166,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 LHSRes.isUsable() ? BO.recreateBinOp(S, LHSRes) : ExprEmpty();
if (BO.isAnd() && !IsLHSSatisfied)
// [temp.constr.op] p2
@ -151,12 +175,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 LHSRes.isUsable() ? BO.recreateBinOp(S, LHSRes) : ExprEmpty();
return calculateConstraintSatisfaction(
ExprResult RHSRes = calculateConstraintSatisfaction(
S, BO.getRHS(), Satisfaction, std::forward<AtomicEvaluator>(Evaluator));
if (RHSRes.isInvalid())
return ExprError();
if (!LHSRes.isUsable() || !RHSRes.isUsable())
return ExprEmpty();
return BO.recreateBinOp(S, LHSRes, RHSRes);
} else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
// 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 +197,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 +218,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,13 +228,13 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
Satisfaction.Details.emplace_back(ConstraintExpr,
SubstitutedAtomicExpr.get());
return false;
return SubstitutedAtomicExpr;
}
static bool calculateConstraintSatisfaction(
Sema &S, const NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,
const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {
static ExprResult calculateConstraintSatisfaction(
Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
return calculateConstraintSatisfaction(
S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
EnterExpressionEvaluationContext ConstantEvaluated(
@ -219,8 +252,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
@ -268,72 +301,92 @@ static bool calculateConstraintSatisfaction(
});
}
static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template,
ArrayRef<const Expr *> ConstraintExprs,
ArrayRef<TemplateArgument> TemplateArgs,
SourceRange TemplateIDRange,
ConstraintSatisfaction &Satisfaction) {
static bool CheckConstraintSatisfaction(
Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
llvm::SmallVectorImpl<Expr *> &Converted,
const MultiLevelTemplateArgumentList &TemplateArgsList,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
if (ConstraintExprs.empty()) {
Satisfaction.IsSatisfied = true;
return false;
}
for (auto& Arg : TemplateArgs)
if (Arg.isInstantiationDependent()) {
// No need to check satisfaction for dependent constraint expressions.
Satisfaction.IsSatisfied = true;
return false;
}
if (TemplateArgsList.isAnyArgInstantiationDependent()) {
// No need to check satisfaction for dependent constraint expressions.
Satisfaction.IsSatisfied = true;
return false;
}
Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
ArrayRef<TemplateArgument> TemplateArgs =
TemplateArgsList.getNumSubstitutedLevels() > 0
? TemplateArgsList.getOutermost()
: ArrayRef<TemplateArgument>{};
Sema::InstantiatingTemplate Inst(
S, TemplateIDRange.getBegin(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
if (Inst.isInvalid())
return true;
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(TemplateArgs);
for (const Expr *ConstraintExpr : ConstraintExprs) {
if (calculateConstraintSatisfaction(S, Template, TemplateArgs,
TemplateIDRange.getBegin(), MLTAL,
ConstraintExpr, Satisfaction))
ExprResult Res = calculateConstraintSatisfaction(
S, Template, TemplateIDRange.getBegin(), TemplateArgsList,
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,
ArrayRef<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange,
ConstraintSatisfaction &OutSatisfaction) {
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsList,
SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
if (ConstraintExprs.empty()) {
OutSatisfaction.IsSatisfied = true;
return false;
}
if (!Template) {
return ::CheckConstraintSatisfaction(*this, nullptr, ConstraintExprs,
TemplateArgs, TemplateIDRange,
OutSatisfaction);
ConvertedConstraints, TemplateArgsList,
TemplateIDRange, OutSatisfaction);
}
// A list of the template argument list flattened in a predictible manner for
// the purposes of caching. The ConstraintSatisfaction type is in AST so it
// has no access to the MultiLevelTemplateArgumentList, so this has to happen
// here.
llvm::SmallVector<TemplateArgument, 4> FlattenedArgs;
for (ArrayRef<TemplateArgument> List : TemplateArgsList)
FlattenedArgs.insert(FlattenedArgs.end(), List.begin(), List.end());
llvm::FoldingSetNodeID ID;
ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs);
ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
void *InsertPos;
if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
OutSatisfaction = *Cached;
return false;
}
auto Satisfaction =
std::make_unique<ConstraintSatisfaction>(Template, TemplateArgs);
std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
TemplateArgs, TemplateIDRange,
*Satisfaction)) {
ConvertedConstraints, TemplateArgsList,
TemplateIDRange, *Satisfaction)) {
return true;
}
OutSatisfaction = *Satisfaction;
@ -347,20 +400,111 @@ bool Sema::CheckConstraintSatisfaction(
bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
return calculateConstraintSatisfaction(
*this, ConstraintExpr, Satisfaction,
[](const Expr *AtomicExpr) -> ExprResult {
return ExprResult(const_cast<Expr *>(AtomicExpr));
});
*this, ConstraintExpr, Satisfaction,
[](const Expr *AtomicExpr) -> ExprResult {
return ExprResult(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;
}
} else 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()) {
// 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, true);
llvm::Optional<MultiLevelTemplateArgumentList> MLTAL =
SetupConstraintCheckingTemplateArgumentsAndScope(
const_cast<FunctionDecl *>(FD), {}, Scope);
Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
@ -371,14 +515,27 @@ 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;
}
bool Sema::EnsureTemplateArgumentListConstraints(
TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
TemplateDecl *TD, MultiLevelTemplateArgumentList TemplateArgs,
SourceRange TemplateIDRange) {
ConstraintSatisfaction Satisfaction;
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
@ -391,7 +548,8 @@ bool Sema::EnsureTemplateArgumentListConstraints(
SmallString<128> TemplateArgString;
TemplateArgString = " ";
TemplateArgString += getTemplateArgumentBindingsText(
TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
TD->getTemplateParameters(), TemplateArgs.getInnermost().data(),
TemplateArgs.getInnermost().size());
Diag(TemplateIDRange.getBegin(),
diag::err_template_arg_list_constraints_not_satisfied)
@ -423,21 +581,13 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Sema::ContextRAII savedContext(*this, Decl);
LocalInstantiationScope Scope(*this);
// 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)) {
@ -445,7 +595,8 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Record = Method->getParent();
}
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
return CheckConstraintSatisfaction(Template, TemplateAC, TemplateArgs,
llvm::SmallVector<Expr *, 1> Converted;
return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL,
PointOfInstantiation, Satisfaction);
}

View File

@ -992,6 +992,82 @@ static bool checkArgPlaceholdersForOverload(Sema &S, MultiExprArg Args,
return false;
}
static bool FriendMembersDifferByConstraints(Sema &S, DeclContext *CurContext,
FunctionDecl *Old,
FunctionDecl *New) {
// Only necessary/valid if we're instantiating a
// ClassTemplateSpecializationDecl.
if (CurContext->getDeclKind() != Decl::ClassTemplateSpecialization)
return false;
// Only when we're dealing with a friend function.
if (!Old->getFriendObjectKind() || !New->getFriendObjectKind())
return false;
// If the the two functions share lexical declaration context, they are not in
// separate instantations.
if (New->getLexicalDeclContext() == Old->getLexicalDeclContext())
return false;
auto *OldLexCtx =
dyn_cast<ClassTemplateSpecializationDecl>(Old->getLexicalDeclContext());
auto *NewLexCtx =
dyn_cast<ClassTemplateSpecializationDecl>(New->getLexicalDeclContext());
if (!OldLexCtx || !NewLexCtx)
return false;
auto InitAssocConstrs = [](FunctionDecl *FD,
SmallVectorImpl<const Expr *> &AC) {
if (const auto *FT = FD->getDescribedFunctionTemplate())
FT->getAssociatedConstraints(AC);
else
FD->getAssociatedConstraints(AC);
};
SmallVector<const Expr *, 3> OldAC;
SmallVector<const Expr *, 3> NewAC;
InitAssocConstrs(Old, OldAC);
InitAssocConstrs(New, NewAC);
assert(OldAC.size() == NewAC.size() &&
"Should not have been identical unless constraints were the same?");
// At this point, we know the constraints lists should be the same.
auto *OldBegin = OldAC.begin();
auto *NewBegin = NewAC.begin();
auto SubstConstraint = [](Sema &S, ClassTemplateSpecializationDecl *LexCtx,
const Expr *Constraint) {
Sema::ContextRAII SavedContext(S, LexCtx);
LocalInstantiationScope Scope(S);
EnterExpressionEvaluationContext EvalCtx(
S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
Sema::SFINAETrap Trap(S);
return S.SubstConstraintExpr(const_cast<Expr *>(Constraint),
S.getTemplateInstantiationArgs(LexCtx));
};
for (; OldBegin != OldAC.end(); ++OldBegin, ++NewBegin) {
ExprResult OldConverted = SubstConstraint(S, OldLexCtx, *OldBegin);
ExprResult NewConverted = SubstConstraint(S, NewLexCtx, *NewBegin);
// We can consider these different, since they depend on the instantiation,
// and cannot prove they are identical.
if (OldConverted.isInvalid() || NewConverted.isInvalid())
return true;
// profile & compare. If they are now different, they aren't equal.
llvm::FoldingSetNodeID NewID, OldID;
NewConverted.get()->Profile(NewID, S.getASTContext(), /*Canonical=*/true);
OldConverted.get()->Profile(OldID, S.getASTContext(), /*Canonical=*/true);
if (NewID != OldID)
return true;
}
// If we haven't found a differing constraint, these are the same.
return false;
}
/// Determine whether the given New declaration is an overload of the
/// declarations in Old. This routine returns Ovl_Match or Ovl_NonFunction if
/// New and Old cannot be overloaded, e.g., if New has the same signature as
@ -1068,6 +1144,20 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old,
!shouldLinkPossiblyHiddenDecl(*I, New))
continue;
// If this is a friend function currently being instantiated as a part
// of a ClassTemplateSpecializationDecl, it could have
// otherwise-identical-looking constraints that depend on the current
// instantiation. In this case, if the otherwise apparent 'match' and
// the new declaration differ by lexical declaration context (meaning
// different Class Template Specializations), AND one of the collected
// constraints seems to 'depend' on the current instantiation in some
// way, than these are not matches and are likely instead overloads.
// Note that an 'error' case might still be valid later (as it could be
// something that would be 'changed' at 'checking' time), but is proof
// we depend on the Class Template Specialization.
if (FriendMembersDifferByConstraints(*this, CurContext, OldF, New))
continue;
Match = *I;
return Ovl_Match;
}

View File

@ -4702,9 +4702,11 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
bool AreArgsDependent =
TemplateSpecializationType::anyDependentTemplateArguments(*TemplateArgs,
Converted);
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(Converted);
if (!AreArgsDependent &&
CheckConstraintSatisfaction(
NamedConcept, {NamedConcept->getConstraintExpr()}, Converted,
NamedConcept, {NamedConcept->getConstraintExpr()}, MLTAL,
SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
TemplateArgs->getRAngleLoc()),
Satisfaction))
@ -5564,6 +5566,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,
if (Inst.isInvalid())
return true;
ConstraintEvalRAII EvalRAII(*this);
TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, Converted);
Params = SubstTemplateParams(Params, CurContext,
MultiLevelTemplateArgumentList(TemplateArgs));
@ -5921,13 +5924,20 @@ bool Sema::CheckTemplateArgumentList(
if (UpdateArgsWithConversions)
TemplateArgs = std::move(NewArgs);
if (!PartialTemplateArgs &&
EnsureTemplateArgumentListConstraints(
Template, Converted, SourceRange(TemplateLoc,
TemplateArgs.getRAngleLoc()))) {
if (ConstraintsNotSatisfied)
*ConstraintsNotSatisfied = true;
return true;
if (!PartialTemplateArgs) {
TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack,
Converted);
MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
Template, &StackTemplateArgs, /*RelativeToPrimary*/ true,
/*Pattern*/ nullptr,
/*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true);
if (EnsureTemplateArgumentListConstraints(
Template, MLTAL,
SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
if (ConstraintsNotSatisfied)
*ConstraintsNotSatisfied = true;
return true;
}
}
return false;
@ -7457,7 +7467,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))

View File

@ -2791,8 +2791,10 @@ CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,
TemplateDeductionInfo& Info) {
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
Template->getAssociatedConstraints(AssociatedConstraints);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints,
DeducedArgs, Info.getLocation(),
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(DeducedArgs);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
Info.getLocation(),
Info.AssociatedConstraintsSatisfaction) ||
!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs));
@ -4572,8 +4574,11 @@ CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs,
/*PartialTemplateArgs=*/false, Converted))
return Sema::DAR_FailedAlreadyDiagnosed;
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(Converted);
if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()},
Converted, TypeLoc.getLocalSourceRange(),
MLTAL, TypeLoc.getLocalSourceRange(),
Satisfaction))
return Sema::DAR_FailedAlreadyDiagnosed;
if (!Satisfaction.IsSatisfied) {

View File

@ -55,9 +55,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;
@ -153,11 +162,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?");
}
@ -174,10 +185,18 @@ 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());
}
}
}
@ -2327,6 +2346,18 @@ bool Sema::SubstTypeConstraint(
const MultiLevelTemplateArgumentList &TemplateArgs) {
const ASTTemplateArgumentListInfo *TemplArgInfo =
TC->getTemplateArgsAsWritten();
// If we're not checking a constraint, we shouldn't be instantiating the type
// constraint, so we should just create a copy of the previous one.
// TODO: ERICH: Should this be RebuildExprInCurrentInstantiation here?
if (!IsEvaluatingAConstraint()) {
Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(),
TC->getConceptNameInfo(), TC->getNamedConcept(),
TC->getNamedConcept(), TemplArgInfo,
TC->getImmediatelyDeclaredConstraint());
return false;
}
TemplateArgumentListInfo InstArgs;
if (TemplArgInfo) {
@ -3511,6 +3542,14 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
return Instantiator.TransformExpr(E);
}
ExprResult
Sema::SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs) {
ConstraintEvalRAII EvalRAII(*this);
return SubstExpr(E, TemplateArgs);
}
ExprResult Sema::SubstInitializer(Expr *Init,
const MultiLevelTemplateArgumentList &TemplateArgs,
bool CXXDirectInit) {

View File

@ -2062,19 +2062,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
@ -2182,6 +2170,11 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
// definition. We don't want non-template functions to be marked as being
// template instantiations.
Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
} else if (!isFriend) {
// If this is not a function template, and this is not a friend (that is,
// this is a locally declared function), save the instantiation relationship
// for the purposes of constraint instantiation.
Function->setInstantiatedFromDecl(D);
}
if (isFriend) {
@ -2420,23 +2413,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) {
@ -2454,6 +2430,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
if (!DC) return nullptr;
}
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
DeclarationNameInfo NameInfo
= SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
@ -2461,7 +2440,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();
@ -2768,9 +2746,6 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
// 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))
return nullptr;
}
@ -4013,18 +3988,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(),

View File

@ -12997,13 +12997,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
@ -13038,7 +13031,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

@ -941,6 +941,10 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
case FunctionDecl::TK_NonTemplate:
mergeRedeclarable(FD, Redecl);
break;
case FunctionDecl::TK_DependentNonTemplate:
mergeRedeclarable(FD, Redecl);
FD->setInstantiatedFromDecl(readDeclAs<FunctionDecl>());
break;
case FunctionDecl::TK_FunctionTemplate:
// Merged when we merge the template.
FD->setDescribedFunctionTemplate(readDeclAs<FunctionTemplateDecl>());

View File

@ -585,6 +585,9 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
switch (D->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
break;
case FunctionDecl::TK_DependentNonTemplate:
Record.AddDeclRef(D->getInstantiatedFromDecl());
break;
case FunctionDecl::TK_FunctionTemplate:
Record.AddDeclRef(D->getDescribedFunctionTemplate());
break;

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

@ -256,3 +256,447 @@ C auto **j1 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'
C auto **&j2 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'}}
C auto **&&j3 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'}}
}
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>
void HasInnerFunc(U &&u) {
void InnerFunc(U && u2)
requires IsInt<decltype(u)> && // #INNERFUNC_REQ
IsInt<decltype(u2)>;
InnerFunc(u); // #INNERFUNC_CALL
}
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);
HasInnerFunc(v);
HasInnerFunc(will_fail);
// expected-error@#INNERFUNC_CALL{{invalid reference to function 'InnerFunc': constraints not satisfied}}
// expected-note@-2{{in instantiation of function template specialization}}
// expected-note@#INNERFUNC_REQ{{'IsInt<decltype(u)>' evaluated to false}}
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
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(){} // #S3DUPE
};
template<class T> struct S4 {
template<typename U>
requires same_as<T, U>
friend void dupe(){} // #S4DUPE
};
// 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}}
S3<int> s3;
S4<int> s4;
// expected-error@#S4DUPE{{redefinition}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#S3DUPE{{previous definition is here}}
// OK, because only instantiated with different T.
S5<int> s5;
S6<double> s6;
S7<int> s7;
}
} // namespace LibCXXOperatorRedef

View File

@ -0,0 +1,23 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify -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}}
}