"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 - No longer attempt to evaluate a consteval UDL function call at runtime when
it is called through a template instantiation. This fixes it is called through a template instantiation. This fixes
`Issue 54578 <https://github.com/llvm/llvm-project/issues/54578>`_. `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 - Clang now correctly delays the instantiation of function constraints until
for ``std::source_location``. 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 - The mangling scheme for C++20 modules has incompatibly changed. The
initial mangling was discovered not to be reversible, and the weak initial mangling was discovered not to be reversible, and the weak
ownership design decision did not give the backwards compatibility ownership design decision did not give the backwards compatibility

View File

@ -1890,7 +1890,9 @@ public:
TK_FunctionTemplateSpecialization, TK_FunctionTemplateSpecialization,
// A function template specialization that hasn't yet been resolved to a // A function template specialization that hasn't yet been resolved to a
// particular specialized function template. // 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 /// Stashed information about a defaulted function definition whose body has
@ -1939,17 +1941,18 @@ private:
/// The template or declaration that this declaration /// The template or declaration that this declaration
/// describes or was instantiated from, respectively. /// describes or was instantiated from, respectively.
/// ///
/// For non-templates, this value will be NULL. For function /// For non-templates this value will be NULL, unless this non-template
/// declarations that describe a function template, this will be a /// function declaration was declared directly inside of a function template,
/// pointer to a FunctionTemplateDecl. For member functions /// in which case this will have a pointer to a FunctionDecl, stored in the
/// of class template specializations, this will be a MemberSpecializationInfo /// NamedDecl. For function declarations that describe a function template,
/// pointer containing information about the specialization. /// this will be a pointer to a FunctionTemplateDecl, stored in the NamedDecl.
/// For function template specializations, this will be a /// For member functions of class template specializations, this will be a
/// FunctionTemplateSpecializationInfo, which contains information about /// MemberSpecializationInfo pointer containing information about the
/// the template being specialized and the template arguments involved in /// specialization. For function template specializations, this will be a
/// that specialization. /// FunctionTemplateSpecializationInfo, which contains information about the
llvm::PointerUnion<FunctionTemplateDecl *, /// template being specialized and the template arguments involved in that
MemberSpecializationInfo *, /// specialization.
llvm::PointerUnion<NamedDecl *, MemberSpecializationInfo *,
FunctionTemplateSpecializationInfo *, FunctionTemplateSpecializationInfo *,
DependentFunctionTemplateSpecializationInfo *> DependentFunctionTemplateSpecializationInfo *>
TemplateOrSpecialization; TemplateOrSpecialization;
@ -2688,6 +2691,11 @@ public:
setInstantiationOfMemberFunction(getASTContext(), FD, TSK); 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 /// Retrieves the function template that is described by this
/// function declaration. /// function declaration.
/// ///
@ -2702,6 +2710,8 @@ public:
/// FunctionTemplateDecl from a FunctionDecl. /// FunctionTemplateDecl from a FunctionDecl.
FunctionTemplateDecl *getDescribedFunctionTemplate() const; FunctionTemplateDecl *getDescribedFunctionTemplate() const;
FunctionDecl *getInstantiatedFromDecl() const;
void setDescribedFunctionTemplate(FunctionTemplateDecl *Template); void setDescribedFunctionTemplate(FunctionTemplateDecl *Template);
/// Determine whether this function is a function template /// Determine whether this function is a function template

View File

@ -906,6 +906,14 @@ public:
const_cast<const Decl*>(this)->getParentFunctionOrMethod()); 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. /// Retrieves the "canonical" declaration of the given declaration.
virtual Decl *getCanonicalDecl() { return this; } virtual Decl *getCanonicalDecl() { return this; }
const Decl *getCanonicalDecl() const { const Decl *getCanonicalDecl() const {

View File

@ -6989,7 +6989,34 @@ private:
LocalInstantiationScope &Scope, LocalInstantiationScope &Scope,
const MultiLevelTemplateArgumentList &TemplateArgs); 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: public:
ConstraintEvalRAII(Sema &S) : S(S) { ++S.ConstraintEvaluationDepth; }
~ConstraintEvalRAII() { --S.ConstraintEvaluationDepth; }
};
public:
bool IsEvaluatingAConstraint() { return ConstraintEvaluationDepth > 0; }
const NormalizedConstraint * const NormalizedConstraint *
getNormalizedAssociatedConstraints( getNormalizedAssociatedConstraints(
NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints); NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints);
@ -7019,8 +7046,10 @@ public:
/// check (either a concept or a constrained entity). /// check (either a concept or a constrained entity).
/// \param ConstraintExprs a list of constraint expressions, treated as if /// \param ConstraintExprs a list of constraint expressions, treated as if
/// they were 'AND'ed together. /// they were 'AND'ed together.
/// \param TemplateArgs the list of template arguments to substitute into the /// \param TemplateArgList the multi-level list of template arguments to
/// constraint expression. /// 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 /// \param TemplateIDRange The source range of the template id that
/// caused the constraints check. /// caused the constraints check.
/// \param Satisfaction if true is returned, will contain details of the /// \param Satisfaction if true is returned, will contain details of the
@ -7030,7 +7059,40 @@ public:
/// false otherwise. /// false otherwise.
bool CheckConstraintSatisfaction( bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, 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); SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
/// \brief Check whether the given non-dependent constraint expression is /// \brief Check whether the given non-dependent constraint expression is
@ -7066,8 +7128,8 @@ public:
/// ///
/// \returns true if the constrains are not satisfied or could not be checked /// \returns true if the constrains are not satisfied or could not be checked
/// for satisfaction, false if the constraints are satisfied. /// for satisfaction, false if the constraints are satisfied.
bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template, bool EnsureTemplateArgumentListConstraints(
ArrayRef<TemplateArgument> TemplateArgs, TemplateDecl *Template, MultiLevelTemplateArgumentList TemplateArgs,
SourceRange TemplateIDRange); SourceRange TemplateIDRange);
/// \brief Emit diagnostics explaining why a constraint expression was deemed /// \brief Emit diagnostics explaining why a constraint expression was deemed
@ -8802,7 +8864,8 @@ public:
MultiLevelTemplateArgumentList getTemplateInstantiationArgs( MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
const NamedDecl *D, const TemplateArgumentList *Innermost = nullptr, 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 /// A context in which code is being synthesized (where a source location
/// alone is not sufficient to identify the context). This covers template /// alone is not sufficient to identify the context). This covers template
@ -9535,6 +9598,11 @@ public:
ExtParameterInfoBuilder &ParamInfos); ExtParameterInfoBuilder &ParamInfos);
ExprResult SubstExpr(Expr *E, ExprResult SubstExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs); 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 /// Substitute the given template arguments into a list of
/// expressions, expanding pack expansions if required. /// expressions, expanding pack expansions if required.

View File

@ -75,6 +75,9 @@ enum class TemplateSubstitutionKind : char {
class MultiLevelTemplateArgumentList { class MultiLevelTemplateArgumentList {
/// The template argument list at a certain template depth /// The template argument list at a certain template depth
using ArgList = ArrayRef<TemplateArgument>; 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 /// The template argument lists, stored from the innermost template
/// argument list (first) to the outermost template argument list (last). /// argument list (first) to the outermost template argument list (last).
@ -121,6 +124,12 @@ enum class TemplateSubstitutionKind : char {
return TemplateArgumentLists.size(); 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 { unsigned getNumRetainedOuterLevels() const {
return NumRetainedOuterLevels; return NumRetainedOuterLevels;
} }
@ -158,6 +167,14 @@ enum class TemplateSubstitutionKind : char {
return !(*this)(Depth, Index).isNull(); 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. /// Clear out a specific template argument.
void setArgument(unsigned Depth, unsigned Index, void setArgument(unsigned Depth, unsigned Index,
TemplateArgument Arg) { TemplateArgument Arg) {
@ -197,6 +214,16 @@ enum class TemplateSubstitutionKind : char {
const ArgList &getInnermost() const { const ArgList &getInnermost() const {
return TemplateArgumentLists.front(); 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. /// The context in which partial ordering of function templates occurs.

View File

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

View File

@ -3729,8 +3729,12 @@ const IdentifierInfo *FunctionDecl::getLiteralIdentifier() const {
FunctionDecl::TemplatedKind FunctionDecl::getTemplatedKind() const { FunctionDecl::TemplatedKind FunctionDecl::getTemplatedKind() const {
if (TemplateOrSpecialization.isNull()) if (TemplateOrSpecialization.isNull())
return TK_NonTemplate; 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; return TK_FunctionTemplate;
}
if (TemplateOrSpecialization.is<MemberSpecializationInfo *>()) if (TemplateOrSpecialization.is<MemberSpecializationInfo *>())
return TK_MemberSpecialization; return TK_MemberSpecialization;
if (TemplateOrSpecialization.is<FunctionTemplateSpecializationInfo *>()) if (TemplateOrSpecialization.is<FunctionTemplateSpecializationInfo *>())
@ -3771,13 +3775,26 @@ FunctionDecl::setInstantiationOfMemberFunction(ASTContext &C,
} }
FunctionTemplateDecl *FunctionDecl::getDescribedFunctionTemplate() const { 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() && assert(TemplateOrSpecialization.isNull() &&
"Member function is already a specialization"); "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 { bool FunctionDecl::isImplicitlyInstantiable() const {

View File

@ -293,6 +293,16 @@ const DeclContext *Decl::getParentFunctionOrMethod() const {
return nullptr; 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 // PrettyStackTraceDecl Implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

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

View File

@ -30,6 +30,7 @@ using namespace sema;
namespace { namespace {
class LogicalBinOp { class LogicalBinOp {
SourceLocation Loc;
OverloadedOperatorKind Op = OO_None; OverloadedOperatorKind Op = OO_None;
const Expr *LHS = nullptr; const Expr *LHS = nullptr;
const Expr *RHS = nullptr; const Expr *RHS = nullptr;
@ -40,12 +41,14 @@ public:
Op = BinaryOperator::getOverloadedOperator(BO->getOpcode()); Op = BinaryOperator::getOverloadedOperator(BO->getOpcode());
LHS = BO->getLHS(); LHS = BO->getLHS();
RHS = BO->getRHS(); RHS = BO->getRHS();
Loc = BO->getExprLoc();
} else if (auto *OO = dyn_cast<CXXOperatorCallExpr>(E)) { } else if (auto *OO = dyn_cast<CXXOperatorCallExpr>(E)) {
// If OO is not || or && it might not have exactly 2 arguments. // If OO is not || or && it might not have exactly 2 arguments.
if (OO->getNumArgs() == 2) { if (OO->getNumArgs() == 2) {
Op = OO->getOperator(); Op = OO->getOperator();
LHS = OO->getArg(0); LHS = OO->getArg(0);
RHS = OO->getArg(1); RHS = OO->getArg(1);
Loc = OO->getOperatorLoc();
} }
} }
} }
@ -56,6 +59,25 @@ public:
const Expr *getLHS() const { return LHS; } const Expr *getLHS() const { return LHS; }
const Expr *getRHS() const { return RHS; } 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> template <typename AtomicEvaluator>
static bool static ExprResult
calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction, ConstraintSatisfaction &Satisfaction,
AtomicEvaluator &&Evaluator) { AtomicEvaluator &&Evaluator) {
ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts(); ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
if (LogicalBinOp BO = ConstraintExpr) { if (LogicalBinOp BO = ConstraintExpr) {
if (calculateConstraintSatisfaction(S, BO.getLHS(), Satisfaction, ExprResult LHSRes = calculateConstraintSatisfaction(
Evaluator)) S, BO.getLHS(), Satisfaction, Evaluator);
return true;
if (LHSRes.isInvalid())
return ExprError();
bool IsLHSSatisfied = Satisfaction.IsSatisfied; bool IsLHSSatisfied = Satisfaction.IsSatisfied;
@ -142,7 +166,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
// is checked. If that is satisfied, the disjunction is satisfied. // is checked. If that is satisfied, the disjunction is satisfied.
// Otherwise, the disjunction is satisfied if and only if the second // Otherwise, the disjunction is satisfied if and only if the second
// operand is satisfied. // operand is satisfied.
return false; return LHSRes.isUsable() ? BO.recreateBinOp(S, LHSRes) : ExprEmpty();
if (BO.isAnd() && !IsLHSSatisfied) if (BO.isAnd() && !IsLHSSatisfied)
// [temp.constr.op] p2 // [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 // is checked. If that is not satisfied, the conjunction is not
// satisfied. Otherwise, the conjunction is satisfied if and only if // satisfied. Otherwise, the conjunction is satisfied if and only if
// the second operand is satisfied. // 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)); 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)) { } 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)); std::forward<AtomicEvaluator>(Evaluator));
} }
@ -164,11 +197,11 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr); ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
if (SubstitutedAtomicExpr.isInvalid()) if (SubstitutedAtomicExpr.isInvalid())
return true; return ExprError();
if (!SubstitutedAtomicExpr.isUsable()) if (!SubstitutedAtomicExpr.isUsable())
// Evaluator has decided satisfaction without yielding an expression. // Evaluator has decided satisfaction without yielding an expression.
return false; return ExprEmpty();
EnterExpressionEvaluationContext ConstantEvaluated( EnterExpressionEvaluationContext ConstantEvaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated); S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
@ -185,7 +218,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
<< SubstitutedAtomicExpr.get()->getSourceRange(); << SubstitutedAtomicExpr.get()->getSourceRange();
for (const PartialDiagnosticAt &PDiag : EvaluationDiags) for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
S.Diag(PDiag.first, PDiag.second); S.Diag(PDiag.first, PDiag.second);
return true; return ExprError();
} }
assert(EvalResult.Val.isInt() && assert(EvalResult.Val.isInt() &&
@ -195,13 +228,13 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
Satisfaction.Details.emplace_back(ConstraintExpr, Satisfaction.Details.emplace_back(ConstraintExpr,
SubstitutedAtomicExpr.get()); SubstitutedAtomicExpr.get());
return false; return SubstitutedAtomicExpr;
} }
static bool calculateConstraintSatisfaction( static ExprResult calculateConstraintSatisfaction(
Sema &S, const NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs, Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL, const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { ConstraintSatisfaction &Satisfaction) {
return calculateConstraintSatisfaction( return calculateConstraintSatisfaction(
S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) { S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
EnterExpressionEvaluationContext ConstantEvaluated( EnterExpressionEvaluationContext ConstantEvaluated(
@ -219,8 +252,8 @@ static bool calculateConstraintSatisfaction(
return ExprError(); return ExprError();
// We do not want error diagnostics escaping here. // We do not want error diagnostics escaping here.
Sema::SFINAETrap Trap(S); Sema::SFINAETrap Trap(S);
SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr), SubstitutedExpression =
MLTAL); S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
// Substitution might have stripped off a contextual conversion to // Substitution might have stripped off a contextual conversion to
// bool if this is the operand of an '&&' or '||'. For example, we // 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 // 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, static bool CheckConstraintSatisfaction(
ArrayRef<const Expr *> ConstraintExprs, Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
ArrayRef<TemplateArgument> TemplateArgs, llvm::SmallVectorImpl<Expr *> &Converted,
SourceRange TemplateIDRange, const MultiLevelTemplateArgumentList &TemplateArgsList,
ConstraintSatisfaction &Satisfaction) { SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
if (ConstraintExprs.empty()) { if (ConstraintExprs.empty()) {
Satisfaction.IsSatisfied = true; Satisfaction.IsSatisfied = true;
return false; return false;
} }
for (auto& Arg : TemplateArgs) if (TemplateArgsList.isAnyArgInstantiationDependent()) {
if (Arg.isInstantiationDependent()) {
// No need to check satisfaction for dependent constraint expressions. // No need to check satisfaction for dependent constraint expressions.
Satisfaction.IsSatisfied = true; Satisfaction.IsSatisfied = true;
return false; 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{}, Sema::InstantiatingTemplate::ConstraintsCheck{},
const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange); const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
if (Inst.isInvalid()) if (Inst.isInvalid())
return true; return true;
MultiLevelTemplateArgumentList MLTAL;
MLTAL.addOuterTemplateArguments(TemplateArgs);
for (const Expr *ConstraintExpr : ConstraintExprs) { for (const Expr *ConstraintExpr : ConstraintExprs) {
if (calculateConstraintSatisfaction(S, Template, TemplateArgs, ExprResult Res = calculateConstraintSatisfaction(
TemplateIDRange.getBegin(), MLTAL, S, Template, TemplateIDRange.getBegin(), TemplateArgsList,
ConstraintExpr, Satisfaction)) ConstraintExpr, Satisfaction);
if (Res.isInvalid())
return true; 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 // [temp.constr.op] p2
// [...] To determine if a conjunction is satisfied, the satisfaction // [...] To determine if a conjunction is satisfied, the satisfaction
// of the first operand is checked. If that is not satisfied, the // of the first operand is checked. If that is not satisfied, the
// conjunction is not satisfied. [...] // conjunction is not satisfied. [...]
return false; return false;
} }
}
return false; return false;
} }
bool Sema::CheckConstraintSatisfaction( bool Sema::CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
ArrayRef<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange, llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
ConstraintSatisfaction &OutSatisfaction) { const MultiLevelTemplateArgumentList &TemplateArgsList,
SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
if (ConstraintExprs.empty()) { if (ConstraintExprs.empty()) {
OutSatisfaction.IsSatisfied = true; OutSatisfaction.IsSatisfied = true;
return false; return false;
} }
if (!Template) { if (!Template) {
return ::CheckConstraintSatisfaction(*this, nullptr, ConstraintExprs, return ::CheckConstraintSatisfaction(*this, nullptr, ConstraintExprs,
TemplateArgs, TemplateIDRange, ConvertedConstraints, TemplateArgsList,
OutSatisfaction); 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; llvm::FoldingSetNodeID ID;
ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs); ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
void *InsertPos; void *InsertPos;
if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) { if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
OutSatisfaction = *Cached; OutSatisfaction = *Cached;
return false; return false;
} }
auto Satisfaction = auto Satisfaction =
std::make_unique<ConstraintSatisfaction>(Template, TemplateArgs); std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
TemplateArgs, TemplateIDRange, ConvertedConstraints, TemplateArgsList,
*Satisfaction)) { TemplateIDRange, *Satisfaction)) {
return true; return true;
} }
OutSatisfaction = *Satisfaction; OutSatisfaction = *Satisfaction;
@ -350,17 +403,108 @@ bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
*this, ConstraintExpr, Satisfaction, *this, ConstraintExpr, Satisfaction,
[](const Expr *AtomicExpr) -> ExprResult { [](const Expr *AtomicExpr) -> ExprResult {
return ExprResult(const_cast<Expr *>(AtomicExpr)); 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, bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
ConstraintSatisfaction &Satisfaction, ConstraintSatisfaction &Satisfaction,
SourceLocation UsageLoc) { SourceLocation UsageLoc) {
const Expr *RC = FD->getTrailingRequiresClause(); // Don't check constraints if the function is dependent. Also don't check if
if (RC->isInstantiationDependent()) { // 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; Satisfaction.IsSatisfied = true;
return false; 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; Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr; CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { 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 // We substitute with empty arguments in order to rebuild the atomic
// constraint in a constant-evaluated context. // constraint in a constant-evaluated context.
// FIXME: Should this be a dedicated TreeTransform? // FIXME: Should this be a dedicated TreeTransform?
return CheckConstraintSatisfaction( const Expr *RC = FD->getTrailingRequiresClause();
FD, {RC}, /*TemplateArgs=*/{}, llvm::SmallVector<Expr *, 1> Converted;
if (CheckConstraintSatisfaction(
FD, {RC}, Converted, *MLTAL,
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
Satisfaction); 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( bool Sema::EnsureTemplateArgumentListConstraints(
TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs, TemplateDecl *TD, MultiLevelTemplateArgumentList TemplateArgs,
SourceRange TemplateIDRange) { SourceRange TemplateIDRange) {
ConstraintSatisfaction Satisfaction; ConstraintSatisfaction Satisfaction;
llvm::SmallVector<const Expr *, 3> AssociatedConstraints; llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
@ -391,7 +548,8 @@ bool Sema::EnsureTemplateArgumentListConstraints(
SmallString<128> TemplateArgString; SmallString<128> TemplateArgString;
TemplateArgString = " "; TemplateArgString = " ";
TemplateArgString += getTemplateArgumentBindingsText( TemplateArgString += getTemplateArgumentBindingsText(
TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size()); TD->getTemplateParameters(), TemplateArgs.getInnermost().data(),
TemplateArgs.getInnermost().size());
Diag(TemplateIDRange.getBegin(), Diag(TemplateIDRange.getBegin(),
diag::err_template_arg_list_constraints_not_satisfied) diag::err_template_arg_list_constraints_not_satisfied)
@ -423,21 +581,13 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Sema::ContextRAII savedContext(*this, Decl); Sema::ContextRAII savedContext(*this, Decl);
LocalInstantiationScope Scope(*this); LocalInstantiationScope Scope(*this);
// If this is not an explicit specialization - we need to get the instantiated Optional<MultiLevelTemplateArgumentList> MLTAL =
// version of the template arguments and add them to scope for the SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
// substitution. Scope);
if (Decl->isTemplateInstantiation()) {
InstantiatingTemplate Inst(*this, Decl->getPointOfInstantiation(), if (!MLTAL)
InstantiatingTemplate::ConstraintsCheck{}, Decl->getPrimaryTemplate(),
TemplateArgs, SourceRange());
if (Inst.isInvalid())
return true; return true;
MultiLevelTemplateArgumentList MLTAL(
*Decl->getTemplateSpecializationArgs());
if (addInstantiatedParametersToScope(
Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), Scope, MLTAL))
return true;
}
Qualifiers ThisQuals; Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr; CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(Decl)) { if (auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
@ -445,7 +595,8 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Record = Method->getParent(); Record = Method->getParent();
} }
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); 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); PointOfInstantiation, Satisfaction);
} }

View File

@ -992,6 +992,82 @@ static bool checkArgPlaceholdersForOverload(Sema &S, MultiExprArg Args,
return false; 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 /// Determine whether the given New declaration is an overload of the
/// declarations in Old. This routine returns Ovl_Match or Ovl_NonFunction if /// 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 /// 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)) !shouldLinkPossiblyHiddenDecl(*I, New))
continue; 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; Match = *I;
return Ovl_Match; return Ovl_Match;
} }

View File

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

View File

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

View File

@ -55,9 +55,18 @@ using namespace sema;
/// instantiating the definition of the given declaration, \p D. This is /// instantiating the definition of the given declaration, \p D. This is
/// used to determine the proper set of template instantiation arguments for /// used to determine the proper set of template instantiation arguments for
/// friend function template specializations. /// 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( MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
const NamedDecl *D, const TemplateArgumentList *Innermost, 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. // Accumulate the set of template argument lists in this structure.
MultiLevelTemplateArgumentList Result; MultiLevelTemplateArgumentList Result;
@ -153,11 +162,13 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
break; break;
// If this function is a generic lambda specialization, we are done. // If this function is a generic lambda specialization, we are done.
if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) if (!LookBeyondLambda &&
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
break; break;
} else if (Function->getDescribedFunctionTemplate()) { } else if (Function->getDescribedFunctionTemplate()) {
assert(Result.getNumSubstitutedLevels() == 0 && assert((IncludeContainingStructArgs ||
Result.getNumSubstitutedLevels() == 0) &&
"Outer template not instantiated?"); "Outer template not instantiated?");
} }
@ -174,10 +185,18 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
} }
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(Ctx)) { } else if (const auto *Rec = dyn_cast<CXXRecordDecl>(Ctx)) {
if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) {
assert(Result.getNumSubstitutedLevels() == 0 && assert((IncludeContainingStructArgs ||
Result.getNumSubstitutedLevels() == 0) &&
"Outer template not instantiated?"); "Outer template not instantiated?");
if (ClassTemplate->isMemberSpecialization()) if (ClassTemplate->isMemberSpecialization())
break; 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 MultiLevelTemplateArgumentList &TemplateArgs) {
const ASTTemplateArgumentListInfo *TemplArgInfo = const ASTTemplateArgumentListInfo *TemplArgInfo =
TC->getTemplateArgsAsWritten(); 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; TemplateArgumentListInfo InstArgs;
if (TemplArgInfo) { if (TemplArgInfo) {
@ -3511,6 +3542,14 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
return Instantiator.TransformExpr(E); return Instantiator.TransformExpr(E);
} }
ExprResult
Sema::SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs) {
ConstraintEvalRAII EvalRAII(*this);
return SubstExpr(E, TemplateArgs);
}
ExprResult Sema::SubstInitializer(Expr *Init, ExprResult Sema::SubstInitializer(Expr *Init,
const MultiLevelTemplateArgumentList &TemplateArgs, const MultiLevelTemplateArgumentList &TemplateArgs,
bool CXXDirectInit) { bool CXXDirectInit) {

View File

@ -2062,19 +2062,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
return nullptr; return nullptr;
} }
// FIXME: Concepts: Do not substitute into constraint expressions
Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); 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 // If we're instantiating a local function declaration, put the result
// in the enclosing namespace; otherwise we need to find the instantiated // 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 // definition. We don't want non-template functions to be marked as being
// template instantiations. // template instantiations.
Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); 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) { if (isFriend) {
@ -2420,23 +2413,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
return nullptr; 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; DeclContext *DC = Owner;
if (isFriend) { if (isFriend) {
if (QualifierLoc) { if (QualifierLoc) {
@ -2454,6 +2430,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
if (!DC) return nullptr; if (!DC) return nullptr;
} }
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
DeclarationNameInfo NameInfo DeclarationNameInfo NameInfo
= SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs); = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
@ -2461,7 +2440,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo); adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo);
// Build the instantiated method declaration. // Build the instantiated method declaration.
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
CXXMethodDecl *Method = nullptr; CXXMethodDecl *Method = nullptr;
SourceLocation StartLoc = D->getInnerLocStart(); SourceLocation StartLoc = D->getInnerLocStart();
@ -2768,9 +2746,6 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
// Invented template parameter type constraints will be instantiated with // Invented template parameter type constraints will be instantiated with
// the corresponding auto-typed parameter as it might reference other // the corresponding auto-typed parameter as it might reference other
// parameters. // parameters.
// TODO: Concepts: do not instantiate the constraint (delayed constraint
// substitution)
if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs)) if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs))
return nullptr; return nullptr;
} }
@ -4013,18 +3988,7 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
if (Invalid) if (Invalid)
return nullptr; return nullptr;
// FIXME: Concepts: Substitution into requires clause should only happen when Expr *InstRequiresClause = L->getRequiresClause();
// 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();
}
TemplateParameterList *InstL TemplateParameterList *InstL
= TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(), = TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(),

View File

@ -12997,13 +12997,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
NewCallOpType); 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. // Create the local class that will describe the lambda.
// FIXME: DependencyKind below is wrong when substituting inside a templated // FIXME: DependencyKind below is wrong when substituting inside a templated
@ -13038,7 +13031,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
E->getCallOperator()->getEndLoc(), E->getCallOperator()->getEndLoc(),
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(), NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(),
E->getCallOperator()->getConstexprKind(), E->getCallOperator()->getConstexprKind(),
NewTrailingRequiresClause.get()); E->getCallOperator()->getTrailingRequiresClause());
LSI->CallOperator = NewCallOperator; LSI->CallOperator = NewCallOperator;

View File

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

View File

@ -585,6 +585,9 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
switch (D->getTemplatedKind()) { switch (D->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate: case FunctionDecl::TK_NonTemplate:
break; break;
case FunctionDecl::TK_DependentNonTemplate:
Record.AddDeclRef(D->getInstantiatedFromDecl());
break;
case FunctionDecl::TK_FunctionTemplate: case FunctionDecl::TK_FunctionTemplate:
Record.AddDeclRef(D->getDescribedFunctionTemplate()); Record.AddDeclRef(D->getDescribedFunctionTemplate());
break; 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(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}} 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 **&j2 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'}}
C auto **&&j3 = 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), ...) template <typename... Args> requires ((sizeof(Args) == 1), ...)
// expected-note@-1 {{because '(sizeof(int) == 1) , (sizeof(char) == 1) , (sizeof(int) == 1)' evaluated to false}} // 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)); 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; constexpr auto value = 0;
template<typename T> 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}}
}