mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 06:10:12 +00:00
Re-apply "Deferred Concept Instantiation Implementation"
This reverts commit 95d94a6775
.
This implements the deferred concepts instantiation, which should allow
the libstdc++ ranges to properly compile, and for the CRTP to work for
constrained functions.
Since the last attempt, this has fixed the issues from @wlei and
@mordante.
Differential Revision: https://reviews.llvm.org/D126907
This commit is contained in:
parent
e0cdafe8d4
commit
babdef27c5
@ -345,6 +345,10 @@ C++20 Feature Support
|
||||
`Issue 50455 <https://github.com/llvm/llvm-project/issues/50455>`_,
|
||||
`Issue 54872 <https://github.com/llvm/llvm-project/issues/54872>`_,
|
||||
`Issue 54587 <https://github.com/llvm/llvm-project/issues/54587>`_.
|
||||
- Clang now correctly delays the instantiation of function constraints until
|
||||
the time of checking, which should now allow the libstdc++ ranges implementation
|
||||
to work for at least trivial examples. This fixes
|
||||
`Issue 44178 <https://github.com/llvm/llvm-project/issues/44178>`_.
|
||||
|
||||
C++2b Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -2669,6 +2669,11 @@ public:
|
||||
/// template.
|
||||
bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y) const;
|
||||
|
||||
/// Determine whether two Friend functions are different because constraints
|
||||
/// that refer to an enclosing template, according to [temp.friend] p9.
|
||||
bool FriendsDifferByConstraints(const FunctionDecl *X,
|
||||
const FunctionDecl *Y) const;
|
||||
|
||||
/// Determine whether the two declarations refer to the same entity.
|
||||
bool isSameEntity(const NamedDecl *X, const NamedDecl *Y) const;
|
||||
|
||||
|
@ -2474,6 +2474,19 @@ public:
|
||||
getCanonicalDecl()->FunctionDeclBits.IsMultiVersion = V;
|
||||
}
|
||||
|
||||
// Sets that this is a constrained friend where the constraint refers to an
|
||||
// enclosing template.
|
||||
void setFriendConstraintRefersToEnclosingTemplate(bool V = true) {
|
||||
getCanonicalDecl()
|
||||
->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = V;
|
||||
}
|
||||
// Indicates this function is a constrained friend, where the constraint
|
||||
// refers to an enclosing template for hte purposes of [temp.friend]p9.
|
||||
bool FriendConstraintRefersToEnclosingTemplate() const {
|
||||
return getCanonicalDecl()
|
||||
->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate;
|
||||
}
|
||||
|
||||
/// Gets the kind of multiversioning attribute this declaration has. Note that
|
||||
/// this can return a value even if the function is not multiversion, such as
|
||||
/// the case of 'target'.
|
||||
|
@ -1664,10 +1664,14 @@ class DeclContext {
|
||||
|
||||
/// Indicates if the function uses Floating Point Constrained Intrinsics
|
||||
uint64_t UsesFPIntrin : 1;
|
||||
|
||||
// Indicates this function is a constrained friend, where the constraint
|
||||
// refers to an enclosing template for hte purposes of [temp.friend]p9.
|
||||
uint64_t FriendConstraintRefersToEnclosingTemplate : 1;
|
||||
};
|
||||
|
||||
/// Number of non-inherited bits in FunctionDeclBitfields.
|
||||
enum { NumFunctionDeclBits = 28 };
|
||||
enum { NumFunctionDeclBits = 29 };
|
||||
|
||||
/// Stores the bits used by CXXConstructorDecl. If modified
|
||||
/// NumCXXConstructorDeclBits and the accessor
|
||||
@ -1679,12 +1683,12 @@ class DeclContext {
|
||||
/// For the bits in FunctionDeclBitfields.
|
||||
uint64_t : NumFunctionDeclBits;
|
||||
|
||||
/// 23 bits to fit in the remaining available space.
|
||||
/// 22 bits to fit in the remaining available space.
|
||||
/// Note that this makes CXXConstructorDeclBitfields take
|
||||
/// exactly 64 bits and thus the width of NumCtorInitializers
|
||||
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
|
||||
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
|
||||
uint64_t NumCtorInitializers : 20;
|
||||
uint64_t NumCtorInitializers : 19;
|
||||
uint64_t IsInheritingConstructor : 1;
|
||||
|
||||
/// Whether this constructor has a trail-allocated explicit specifier.
|
||||
|
@ -3673,6 +3673,31 @@ public:
|
||||
bool ConsiderCudaAttrs = true,
|
||||
bool ConsiderRequiresClauses = true);
|
||||
|
||||
// Calculates whether the expression Constraint depends on an enclosing
|
||||
// template, for the purposes of [temp.friend] p9.
|
||||
// TemplateDepth is the 'depth' of the friend function, which is used to
|
||||
// compare whether a declaration reference is referring to a containing
|
||||
// template, or just the current friend function. A 'lower' TemplateDepth in
|
||||
// the AST refers to a 'containing' template. As the constraint is
|
||||
// uninstantiated, this is relative to the 'top' of the TU.
|
||||
bool ConstraintExpressionDependsOnEnclosingTemplate(unsigned TemplateDepth,
|
||||
const Expr *Constraint);
|
||||
|
||||
// Calculates whether the friend function depends on an enclosing template for
|
||||
// the purposes of [temp.friend] p9.
|
||||
bool FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD);
|
||||
|
||||
// Calculates whether two constraint expressions are equal irrespective of a
|
||||
// difference in 'depth'. This takes a pair of optional 'NamedDecl's 'Old' and
|
||||
// 'New', which are the "source" of the constraint, since this is necessary
|
||||
// for figuring out the relative 'depth' of the constraint. The depth of the
|
||||
// 'primary template' and the 'instantiated from' templates aren't necessarily
|
||||
// the same, such as a case when one is a 'friend' defined in a class.
|
||||
bool AreConstraintExpressionsEqual(const NamedDecl *Old,
|
||||
const Expr *OldConstr,
|
||||
const NamedDecl *New,
|
||||
const Expr *NewConstr);
|
||||
|
||||
enum class AllowedExplicit {
|
||||
/// Allow no explicit functions to be used.
|
||||
None,
|
||||
@ -7152,6 +7177,21 @@ private:
|
||||
LocalInstantiationScope &Scope,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs);
|
||||
|
||||
/// used by SetupConstraintCheckingTemplateArgumentsAndScope to recursively(in
|
||||
/// the case of lambdas) set up the LocalInstantiationScope of the current
|
||||
/// function.
|
||||
bool SetupConstraintScope(
|
||||
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
|
||||
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope);
|
||||
|
||||
/// Used during constraint checking, sets up the constraint template arguemnt
|
||||
/// lists, and calls SetupConstraintScope to set up the
|
||||
/// LocalInstantiationScope to have the proper set of ParVarDecls configured.
|
||||
llvm::Optional<MultiLevelTemplateArgumentList>
|
||||
SetupConstraintCheckingTemplateArgumentsAndScope(
|
||||
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
|
||||
LocalInstantiationScope &Scope);
|
||||
|
||||
public:
|
||||
const NormalizedConstraint *
|
||||
getNormalizedAssociatedConstraints(
|
||||
@ -7194,6 +7234,39 @@ public:
|
||||
bool CheckConstraintSatisfaction(
|
||||
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgLists,
|
||||
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
|
||||
llvm::SmallVector<Expr *, 4> Converted;
|
||||
return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted,
|
||||
TemplateArgLists, TemplateIDRange,
|
||||
Satisfaction);
|
||||
}
|
||||
|
||||
/// \brief Check whether the given list of constraint expressions are
|
||||
/// satisfied (as if in a 'conjunction') given template arguments.
|
||||
/// Additionally, takes an empty list of Expressions which is populated with
|
||||
/// the instantiated versions of the ConstraintExprs.
|
||||
/// \param Template the template-like entity that triggered the constraints
|
||||
/// check (either a concept or a constrained entity).
|
||||
/// \param ConstraintExprs a list of constraint expressions, treated as if
|
||||
/// they were 'AND'ed together.
|
||||
/// \param ConvertedConstraints a out parameter that will get populated with
|
||||
/// the instantiated version of the ConstraintExprs if we successfully checked
|
||||
/// satisfaction.
|
||||
/// \param TemplateArgList the multi-level list of template arguments to
|
||||
/// substitute into the constraint expression. This should be relative to the
|
||||
/// top-level (hence multi-level), since we need to instantiate fully at the
|
||||
/// time of checking.
|
||||
/// \param TemplateIDRange The source range of the template id that
|
||||
/// caused the constraints check.
|
||||
/// \param Satisfaction if true is returned, will contain details of the
|
||||
/// satisfaction, with enough information to diagnose an unsatisfied
|
||||
/// expression.
|
||||
/// \returns true if an error occurred and satisfaction could not be checked,
|
||||
/// false otherwise.
|
||||
bool CheckConstraintSatisfaction(
|
||||
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
||||
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgList,
|
||||
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
|
||||
|
||||
/// \brief Check whether the given non-dependent constraint expression is
|
||||
@ -7213,8 +7286,8 @@ public:
|
||||
/// \returns true if an error occurred, false otherwise.
|
||||
bool CheckFunctionConstraints(const FunctionDecl *FD,
|
||||
ConstraintSatisfaction &Satisfaction,
|
||||
SourceLocation UsageLoc = SourceLocation());
|
||||
|
||||
SourceLocation UsageLoc = SourceLocation(),
|
||||
bool ForOverloadResolution = false);
|
||||
|
||||
/// \brief Ensure that the given template arguments satisfy the constraints
|
||||
/// associated with the given template, emitting a diagnostic if they do not.
|
||||
@ -8222,12 +8295,19 @@ public:
|
||||
TPL_TemplateTemplateArgumentMatch
|
||||
};
|
||||
|
||||
bool TemplateParameterListsAreEqual(TemplateParameterList *New,
|
||||
TemplateParameterList *Old,
|
||||
bool Complain,
|
||||
TemplateParameterListEqualKind Kind,
|
||||
SourceLocation TemplateArgLoc
|
||||
= SourceLocation());
|
||||
bool TemplateParameterListsAreEqual(
|
||||
const NamedDecl *NewInstFrom, TemplateParameterList *New,
|
||||
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
|
||||
TemplateParameterListEqualKind Kind,
|
||||
SourceLocation TemplateArgLoc = SourceLocation());
|
||||
|
||||
bool TemplateParameterListsAreEqual(
|
||||
TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
|
||||
TemplateParameterListEqualKind Kind,
|
||||
SourceLocation TemplateArgLoc = SourceLocation()) {
|
||||
return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
|
||||
Kind, TemplateArgLoc);
|
||||
}
|
||||
|
||||
bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams);
|
||||
|
||||
@ -8962,7 +9042,8 @@ public:
|
||||
|
||||
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
|
||||
const NamedDecl *D, const TemplateArgumentList *Innermost = nullptr,
|
||||
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr);
|
||||
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
|
||||
bool LookBeyondLambda = false, bool IncludeContainingStruct = false);
|
||||
|
||||
/// A context in which code is being synthesized (where a source location
|
||||
/// alone is not sufficient to identify the context). This covers template
|
||||
@ -9670,23 +9751,21 @@ public:
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
SourceLocation Loc, DeclarationName Entity);
|
||||
|
||||
TypeSourceInfo *SubstFunctionDeclType(TypeSourceInfo *T,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
SourceLocation Loc,
|
||||
DeclarationName Entity,
|
||||
CXXRecordDecl *ThisContext,
|
||||
Qualifiers ThisTypeQuals);
|
||||
TypeSourceInfo *SubstFunctionDeclType(
|
||||
TypeSourceInfo *T, const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext,
|
||||
Qualifiers ThisTypeQuals, bool EvaluateConstraints = true);
|
||||
void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
|
||||
const MultiLevelTemplateArgumentList &Args);
|
||||
bool SubstExceptionSpec(SourceLocation Loc,
|
||||
FunctionProtoType::ExceptionSpecInfo &ESI,
|
||||
SmallVectorImpl<QualType> &ExceptionStorage,
|
||||
const MultiLevelTemplateArgumentList &Args);
|
||||
ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
int indexAdjustment,
|
||||
Optional<unsigned> NumExpansions,
|
||||
bool ExpectParameterPack);
|
||||
ParmVarDecl *
|
||||
SubstParmVarDecl(ParmVarDecl *D,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
int indexAdjustment, Optional<unsigned> NumExpansions,
|
||||
bool ExpectParameterPack, bool EvaluateConstraints = true);
|
||||
bool SubstParmTypes(SourceLocation Loc, ArrayRef<ParmVarDecl *> Params,
|
||||
const FunctionProtoType::ExtParameterInfo *ExtParamInfos,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
@ -9696,6 +9775,25 @@ public:
|
||||
ExprResult SubstExpr(Expr *E,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs);
|
||||
|
||||
// A RAII type used by the TemplateDeclInstantiator and TemplateInstantiator
|
||||
// to disable constraint evaluation, then restore the state.
|
||||
template <typename InstTy> struct ConstraintEvalRAII {
|
||||
InstTy &TI;
|
||||
bool OldValue;
|
||||
|
||||
ConstraintEvalRAII(InstTy &TI)
|
||||
: TI(TI), OldValue(TI.getEvaluateConstraints()) {
|
||||
TI.setEvaluateConstraints(false);
|
||||
}
|
||||
~ConstraintEvalRAII() { TI.setEvaluateConstraints(OldValue); }
|
||||
};
|
||||
|
||||
// Unlike the above, this evaluates constraints, which should only happen at
|
||||
// 'constraint checking' time.
|
||||
ExprResult
|
||||
SubstConstraintExpr(Expr *E,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs);
|
||||
|
||||
/// Substitute the given template arguments into a list of
|
||||
/// expressions, expanding pack expansions if required.
|
||||
///
|
||||
@ -9725,7 +9823,6 @@ public:
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
TemplateArgumentListInfo &Outputs);
|
||||
|
||||
|
||||
Decl *SubstDecl(Decl *D, DeclContext *Owner,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs);
|
||||
|
||||
@ -9816,7 +9913,8 @@ public:
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs);
|
||||
|
||||
bool SubstTypeConstraint(TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs);
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
bool EvaluateConstraint);
|
||||
|
||||
bool InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
|
||||
ParmVarDecl *Param);
|
||||
|
@ -503,6 +503,7 @@ enum class TemplateSubstitutionKind : char {
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs;
|
||||
Sema::LateInstantiatedAttrVec* LateAttrs = nullptr;
|
||||
LocalInstantiationScope *StartingScope = nullptr;
|
||||
bool EvaluateConstraints = true;
|
||||
|
||||
/// A list of out-of-line class template partial
|
||||
/// specializations that will need to be instantiated after the
|
||||
@ -526,6 +527,13 @@ enum class TemplateSubstitutionKind : char {
|
||||
SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex),
|
||||
Owner(Owner), TemplateArgs(TemplateArgs) {}
|
||||
|
||||
void setEvaluateConstraints(bool B) {
|
||||
EvaluateConstraints = B;
|
||||
}
|
||||
bool getEvaluateConstraints() {
|
||||
return EvaluateConstraints;
|
||||
}
|
||||
|
||||
// Define all the decl visitors using DeclNodes.inc
|
||||
#define DECL(DERIVED, BASE) \
|
||||
Decl *Visit ## DERIVED ## Decl(DERIVED ## Decl *D);
|
||||
|
@ -6452,6 +6452,31 @@ static bool hasSameOverloadableAttrs(const FunctionDecl *A,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ASTContext::FriendsDifferByConstraints(const FunctionDecl *X,
|
||||
const FunctionDecl *Y) const {
|
||||
// If these aren't friends, then they aren't friends that differ by
|
||||
// constraints.
|
||||
if (!X->getFriendObjectKind() || !Y->getFriendObjectKind())
|
||||
return false;
|
||||
|
||||
// If the the two functions share lexical declaration context, they are not in
|
||||
// separate instantations, and thus in the same scope.
|
||||
if (X->getLexicalDeclContext() == Y->getLexicalDeclContext())
|
||||
return false;
|
||||
|
||||
if (!X->getDescribedFunctionTemplate()) {
|
||||
assert(!Y->getDescribedFunctionTemplate() &&
|
||||
"How would these be the same if they aren't both templates?");
|
||||
|
||||
// If these friends don't have constraints, they aren't constrained, and
|
||||
// thus don't fall under temp.friend p9. Else the simple presence of a
|
||||
// constraint makes them unique.
|
||||
return X->getTrailingRequiresClause();
|
||||
}
|
||||
|
||||
return X->FriendConstraintRefersToEnclosingTemplate();
|
||||
}
|
||||
|
||||
bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
|
||||
if (X == Y)
|
||||
return true;
|
||||
@ -6532,6 +6557,10 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
|
||||
FuncY->getTrailingRequiresClause()))
|
||||
return false;
|
||||
|
||||
// Constrained friends are different in certain cases, see: [temp.friend]p9.
|
||||
if (FriendsDifferByConstraints(FuncX, FuncY))
|
||||
return false;
|
||||
|
||||
auto GetTypeAsWritten = [](const FunctionDecl *FD) {
|
||||
// Map to the first declaration that we've already merged into this one.
|
||||
// The TSI of redeclarations might not match (due to calling conventions
|
||||
|
@ -3701,6 +3701,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
||||
ToFunction->setDefaulted(D->isDefaulted());
|
||||
ToFunction->setExplicitlyDefaulted(D->isExplicitlyDefaulted());
|
||||
ToFunction->setDeletedAsWritten(D->isDeletedAsWritten());
|
||||
ToFunction->setFriendConstraintRefersToEnclosingTemplate(
|
||||
D->FriendConstraintRefersToEnclosingTemplate());
|
||||
ToFunction->setRangeEnd(ToEndLoc);
|
||||
|
||||
// Set the parameters.
|
||||
|
@ -2970,6 +2970,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
|
||||
FunctionDeclBits.IsMultiVersion = false;
|
||||
FunctionDeclBits.IsCopyDeductionCandidate = false;
|
||||
FunctionDeclBits.HasODRHash = false;
|
||||
FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false;
|
||||
if (TrailingRequiresClause)
|
||||
setTrailingRequiresClause(TrailingRequiresClause);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "TreeTransform.h"
|
||||
#include "clang/Sema/SemaConcept.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/Sema/SemaInternal.h"
|
||||
@ -18,6 +19,7 @@
|
||||
#include "clang/Sema/Template.h"
|
||||
#include "clang/Sema/Overload.h"
|
||||
#include "clang/Sema/Initialization.h"
|
||||
#include "clang/AST/ASTLambda.h"
|
||||
#include "clang/AST/ExprConcepts.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/OperatorPrecedence.h"
|
||||
@ -30,6 +32,7 @@ using namespace sema;
|
||||
|
||||
namespace {
|
||||
class LogicalBinOp {
|
||||
SourceLocation Loc;
|
||||
OverloadedOperatorKind Op = OO_None;
|
||||
const Expr *LHS = nullptr;
|
||||
const Expr *RHS = nullptr;
|
||||
@ -40,12 +43,14 @@ public:
|
||||
Op = BinaryOperator::getOverloadedOperator(BO->getOpcode());
|
||||
LHS = BO->getLHS();
|
||||
RHS = BO->getRHS();
|
||||
Loc = BO->getExprLoc();
|
||||
} else if (auto *OO = dyn_cast<CXXOperatorCallExpr>(E)) {
|
||||
// If OO is not || or && it might not have exactly 2 arguments.
|
||||
if (OO->getNumArgs() == 2) {
|
||||
Op = OO->getOperator();
|
||||
LHS = OO->getArg(0);
|
||||
RHS = OO->getArg(1);
|
||||
Loc = OO->getOperatorLoc();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,6 +61,26 @@ public:
|
||||
|
||||
const Expr *getLHS() const { return LHS; }
|
||||
const Expr *getRHS() const { return RHS; }
|
||||
|
||||
ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS) const {
|
||||
return recreateBinOp(SemaRef, LHS, const_cast<Expr *>(getRHS()));
|
||||
}
|
||||
|
||||
ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS,
|
||||
ExprResult RHS) const {
|
||||
assert((isAnd() || isOr()) && "Not the right kind of op?");
|
||||
assert((!LHS.isInvalid() && !RHS.isInvalid()) && "not good expressions?");
|
||||
|
||||
if (!LHS.isUsable() || !RHS.isUsable())
|
||||
return ExprEmpty();
|
||||
|
||||
// We should just be able to 'normalize' these to the builtin Binary
|
||||
// Operator, since that is how they are evaluated in constriant checks.
|
||||
return BinaryOperator::Create(SemaRef.Context, LHS.get(), RHS.get(),
|
||||
BinaryOperator::getOverloadedOpcode(Op),
|
||||
SemaRef.Context.BoolTy, VK_PRValue,
|
||||
OK_Ordinary, Loc, FPOptionsOverride{});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -122,16 +147,18 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression,
|
||||
}
|
||||
|
||||
template <typename AtomicEvaluator>
|
||||
static bool
|
||||
static ExprResult
|
||||
calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
||||
ConstraintSatisfaction &Satisfaction,
|
||||
AtomicEvaluator &&Evaluator) {
|
||||
ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
|
||||
|
||||
if (LogicalBinOp BO = ConstraintExpr) {
|
||||
if (calculateConstraintSatisfaction(S, BO.getLHS(), Satisfaction,
|
||||
Evaluator))
|
||||
return true;
|
||||
ExprResult LHSRes = calculateConstraintSatisfaction(
|
||||
S, BO.getLHS(), Satisfaction, Evaluator);
|
||||
|
||||
if (LHSRes.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
bool IsLHSSatisfied = Satisfaction.IsSatisfied;
|
||||
|
||||
@ -142,7 +169,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
||||
// is checked. If that is satisfied, the disjunction is satisfied.
|
||||
// Otherwise, the disjunction is satisfied if and only if the second
|
||||
// operand is satisfied.
|
||||
return false;
|
||||
return BO.recreateBinOp(S, LHSRes);
|
||||
|
||||
if (BO.isAnd() && !IsLHSSatisfied)
|
||||
// [temp.constr.op] p2
|
||||
@ -151,12 +178,21 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
||||
// is checked. If that is not satisfied, the conjunction is not
|
||||
// satisfied. Otherwise, the conjunction is satisfied if and only if
|
||||
// the second operand is satisfied.
|
||||
return false;
|
||||
return BO.recreateBinOp(S, LHSRes);
|
||||
|
||||
return calculateConstraintSatisfaction(
|
||||
ExprResult RHSRes = calculateConstraintSatisfaction(
|
||||
S, BO.getRHS(), Satisfaction, std::forward<AtomicEvaluator>(Evaluator));
|
||||
} else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
|
||||
return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
|
||||
if (RHSRes.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
return BO.recreateBinOp(S, LHSRes, RHSRes);
|
||||
}
|
||||
|
||||
if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
|
||||
// These aren't evaluated, so we don't care about cleanups, so we can just
|
||||
// evaluate these as if the cleanups didn't exist.
|
||||
return calculateConstraintSatisfaction(
|
||||
S, C->getSubExpr(), Satisfaction,
|
||||
std::forward<AtomicEvaluator>(Evaluator));
|
||||
}
|
||||
|
||||
@ -164,11 +200,11 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
||||
ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
|
||||
|
||||
if (SubstitutedAtomicExpr.isInvalid())
|
||||
return true;
|
||||
return ExprError();
|
||||
|
||||
if (!SubstitutedAtomicExpr.isUsable())
|
||||
// Evaluator has decided satisfaction without yielding an expression.
|
||||
return false;
|
||||
return ExprEmpty();
|
||||
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
||||
@ -185,7 +221,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
||||
<< SubstitutedAtomicExpr.get()->getSourceRange();
|
||||
for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
|
||||
S.Diag(PDiag.first, PDiag.second);
|
||||
return true;
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
assert(EvalResult.Val.isInt() &&
|
||||
@ -195,10 +231,10 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
|
||||
Satisfaction.Details.emplace_back(ConstraintExpr,
|
||||
SubstitutedAtomicExpr.get());
|
||||
|
||||
return false;
|
||||
return SubstitutedAtomicExpr;
|
||||
}
|
||||
|
||||
static bool calculateConstraintSatisfaction(
|
||||
static ExprResult calculateConstraintSatisfaction(
|
||||
Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
|
||||
const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
|
||||
ConstraintSatisfaction &Satisfaction) {
|
||||
@ -219,8 +255,8 @@ static bool calculateConstraintSatisfaction(
|
||||
return ExprError();
|
||||
// We do not want error diagnostics escaping here.
|
||||
Sema::SFINAETrap Trap(S);
|
||||
SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr),
|
||||
MLTAL);
|
||||
SubstitutedExpression =
|
||||
S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
|
||||
// Substitution might have stripped off a contextual conversion to
|
||||
// bool if this is the operand of an '&&' or '||'. For example, we
|
||||
// might lose an lvalue-to-rvalue conversion here. If so, put it back
|
||||
@ -270,6 +306,7 @@ static bool calculateConstraintSatisfaction(
|
||||
|
||||
static bool CheckConstraintSatisfaction(
|
||||
Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
||||
llvm::SmallVectorImpl<Expr *> &Converted,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgsLists,
|
||||
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
|
||||
if (ConstraintExprs.empty()) {
|
||||
@ -294,22 +331,30 @@ static bool CheckConstraintSatisfaction(
|
||||
return true;
|
||||
|
||||
for (const Expr *ConstraintExpr : ConstraintExprs) {
|
||||
if (calculateConstraintSatisfaction(S, Template, TemplateIDRange.getBegin(),
|
||||
TemplateArgsLists, ConstraintExpr,
|
||||
Satisfaction))
|
||||
ExprResult Res = calculateConstraintSatisfaction(
|
||||
S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
|
||||
ConstraintExpr, Satisfaction);
|
||||
if (Res.isInvalid())
|
||||
return true;
|
||||
if (!Satisfaction.IsSatisfied)
|
||||
|
||||
Converted.push_back(Res.get());
|
||||
if (!Satisfaction.IsSatisfied) {
|
||||
// Backfill the 'converted' list with nulls so we can keep the Converted
|
||||
// and unconverted lists in sync.
|
||||
Converted.append(ConstraintExprs.size() - Converted.size(), nullptr);
|
||||
// [temp.constr.op] p2
|
||||
// [...] To determine if a conjunction is satisfied, the satisfaction
|
||||
// of the first operand is checked. If that is not satisfied, the
|
||||
// conjunction is not satisfied. [...]
|
||||
// [...] To determine if a conjunction is satisfied, the satisfaction
|
||||
// of the first operand is checked. If that is not satisfied, the
|
||||
// conjunction is not satisfied. [...]
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sema::CheckConstraintSatisfaction(
|
||||
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
|
||||
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgsLists,
|
||||
SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
|
||||
if (ConstraintExprs.empty()) {
|
||||
@ -317,9 +362,9 @@ bool Sema::CheckConstraintSatisfaction(
|
||||
return false;
|
||||
}
|
||||
if (!Template) {
|
||||
return ::CheckConstraintSatisfaction(*this, nullptr, ConstraintExprs,
|
||||
TemplateArgsLists, TemplateIDRange,
|
||||
OutSatisfaction);
|
||||
return ::CheckConstraintSatisfaction(
|
||||
*this, nullptr, ConstraintExprs, ConvertedConstraints,
|
||||
TemplateArgsLists, TemplateIDRange, OutSatisfaction);
|
||||
}
|
||||
|
||||
// A list of the template argument list flattened in a predictible manner for
|
||||
@ -340,8 +385,8 @@ bool Sema::CheckConstraintSatisfaction(
|
||||
auto Satisfaction =
|
||||
std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
|
||||
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
|
||||
TemplateArgsLists, TemplateIDRange,
|
||||
*Satisfaction)) {
|
||||
ConvertedConstraints, TemplateArgsLists,
|
||||
TemplateIDRange, *Satisfaction)) {
|
||||
return true;
|
||||
}
|
||||
OutSatisfaction = *Satisfaction;
|
||||
@ -355,21 +400,120 @@ bool Sema::CheckConstraintSatisfaction(
|
||||
bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
|
||||
ConstraintSatisfaction &Satisfaction) {
|
||||
return calculateConstraintSatisfaction(
|
||||
*this, ConstraintExpr, Satisfaction,
|
||||
[this](const Expr *AtomicExpr) -> ExprResult {
|
||||
// We only do this to immitate lvalue-to-rvalue conversion.
|
||||
return PerformContextuallyConvertToBool(const_cast<Expr *>(AtomicExpr));
|
||||
});
|
||||
*this, ConstraintExpr, Satisfaction,
|
||||
[this](const Expr *AtomicExpr) -> ExprResult {
|
||||
// We only do this to immitate lvalue-to-rvalue conversion.
|
||||
return PerformContextuallyConvertToBool(
|
||||
const_cast<Expr *>(AtomicExpr));
|
||||
})
|
||||
.isInvalid();
|
||||
}
|
||||
|
||||
bool Sema::SetupConstraintScope(
|
||||
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
|
||||
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) {
|
||||
if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) {
|
||||
FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
|
||||
InstantiatingTemplate Inst(
|
||||
*this, FD->getPointOfInstantiation(),
|
||||
Sema::InstantiatingTemplate::ConstraintsCheck{}, PrimaryTemplate,
|
||||
TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
|
||||
SourceRange());
|
||||
if (Inst.isInvalid())
|
||||
return true;
|
||||
|
||||
// addInstantiatedParametersToScope creates a map of 'uninstantiated' to
|
||||
// 'instantiated' parameters and adds it to the context. For the case where
|
||||
// this function is a template being instantiated NOW, we also need to add
|
||||
// the list of current template arguments to the list so that they also can
|
||||
// be picked out of the map.
|
||||
if (auto *SpecArgs = FD->getTemplateSpecializationArgs()) {
|
||||
MultiLevelTemplateArgumentList JustTemplArgs(*SpecArgs);
|
||||
if (addInstantiatedParametersToScope(
|
||||
FD, PrimaryTemplate->getTemplatedDecl(), Scope, JustTemplArgs))
|
||||
return true;
|
||||
}
|
||||
|
||||
// If this is a member function, make sure we get the parameters that
|
||||
// reference the original primary template.
|
||||
if (const auto *FromMemTempl =
|
||||
PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
|
||||
if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
|
||||
Scope, MLTAL))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization ||
|
||||
FD->getTemplatedKind() == FunctionDecl::TK_DependentNonTemplate) {
|
||||
FunctionDecl *InstantiatedFrom =
|
||||
FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization
|
||||
? FD->getInstantiatedFromMemberFunction()
|
||||
: FD->getInstantiatedFromDecl();
|
||||
|
||||
InstantiatingTemplate Inst(
|
||||
*this, FD->getPointOfInstantiation(),
|
||||
Sema::InstantiatingTemplate::ConstraintsCheck{}, InstantiatedFrom,
|
||||
TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
|
||||
SourceRange());
|
||||
if (Inst.isInvalid())
|
||||
return true;
|
||||
|
||||
// Case where this was not a template, but instantiated as a
|
||||
// child-function.
|
||||
if (addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, MLTAL))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function collects all of the template arguments for the purposes of
|
||||
// constraint-instantiation and checking.
|
||||
llvm::Optional<MultiLevelTemplateArgumentList>
|
||||
Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
|
||||
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
|
||||
LocalInstantiationScope &Scope) {
|
||||
MultiLevelTemplateArgumentList MLTAL;
|
||||
|
||||
// Collect the list of template arguments relative to the 'primary' template.
|
||||
// We need the entire list, since the constraint is completely uninstantiated
|
||||
// at this point.
|
||||
MLTAL = getTemplateInstantiationArgs(FD, nullptr, /*RelativeToPrimary*/ true,
|
||||
/*Pattern*/ nullptr,
|
||||
/*LookBeyondLambda*/ true);
|
||||
if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
|
||||
return {};
|
||||
|
||||
return MLTAL;
|
||||
}
|
||||
|
||||
bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
|
||||
ConstraintSatisfaction &Satisfaction,
|
||||
SourceLocation UsageLoc) {
|
||||
const Expr *RC = FD->getTrailingRequiresClause();
|
||||
if (RC->isInstantiationDependent()) {
|
||||
SourceLocation UsageLoc,
|
||||
bool ForOverloadResolution) {
|
||||
// Don't check constraints if the function is dependent. Also don't check if
|
||||
// this is a function template specialization, as the call to
|
||||
// CheckinstantiatedFunctionTemplateConstraints after this will check it
|
||||
// better.
|
||||
if (FD->isDependentContext() ||
|
||||
FD->getTemplatedKind() ==
|
||||
FunctionDecl::TK_FunctionTemplateSpecialization) {
|
||||
Satisfaction.IsSatisfied = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
ContextRAII SavedContext{
|
||||
*this, cast<DeclContext>(
|
||||
const_cast<FunctionDecl *>(FD)->getNonClosureContext())};
|
||||
LocalInstantiationScope Scope(*this, !ForOverloadResolution ||
|
||||
isLambdaCallOperator(FD));
|
||||
llvm::Optional<MultiLevelTemplateArgumentList> MLTAL =
|
||||
SetupConstraintCheckingTemplateArgumentsAndScope(
|
||||
const_cast<FunctionDecl *>(FD), {}, Scope);
|
||||
|
||||
Qualifiers ThisQuals;
|
||||
CXXRecordDecl *Record = nullptr;
|
||||
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
|
||||
@ -380,10 +524,112 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
|
||||
// We substitute with empty arguments in order to rebuild the atomic
|
||||
// constraint in a constant-evaluated context.
|
||||
// FIXME: Should this be a dedicated TreeTransform?
|
||||
return CheckConstraintSatisfaction(
|
||||
FD, {RC}, /*TemplateArgs=*/{},
|
||||
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
|
||||
Satisfaction);
|
||||
const Expr *RC = FD->getTrailingRequiresClause();
|
||||
llvm::SmallVector<Expr *, 1> Converted;
|
||||
|
||||
if (CheckConstraintSatisfaction(
|
||||
FD, {RC}, Converted, *MLTAL,
|
||||
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
|
||||
Satisfaction))
|
||||
return true;
|
||||
|
||||
// FIXME: we need to do this for the function constraints for
|
||||
// comparison of constraints to work, but do we also need to do it for
|
||||
// CheckInstantiatedFunctionConstraints? That one is more difficult, but we
|
||||
// seem to always just pick up the constraints from the primary template.
|
||||
assert(Converted.size() <= 1 && "Got more expressions converted?");
|
||||
if (!Converted.empty() && Converted[0] != nullptr)
|
||||
const_cast<FunctionDecl *>(FD)->setTrailingRequiresClause(Converted[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Figure out the to-translation-unit depth for this function declaration for
|
||||
// the purpose of seeing if they differ by constraints. This isn't the same as
|
||||
// getTemplateDepth, because it includes already instantiated parents.
|
||||
static unsigned CalculateTemplateDepthForConstraints(Sema &S,
|
||||
const NamedDecl *ND) {
|
||||
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
|
||||
ND, nullptr, /*RelativeToPrimary*/ true,
|
||||
/*Pattern*/ nullptr,
|
||||
/*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true);
|
||||
return MLTAL.getNumSubstitutedLevels();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
|
||||
unsigned TemplateDepth = 0;
|
||||
public:
|
||||
using inherited = TreeTransform<AdjustConstraintDepth>;
|
||||
AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
|
||||
: inherited(SemaRef), TemplateDepth(TemplateDepth) {}
|
||||
QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
|
||||
TemplateTypeParmTypeLoc TL) {
|
||||
const TemplateTypeParmType *T = TL.getTypePtr();
|
||||
|
||||
TemplateTypeParmDecl *NewTTPDecl = nullptr;
|
||||
if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
|
||||
NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
|
||||
TransformDecl(TL.getNameLoc(), OldTTPDecl));
|
||||
|
||||
QualType Result = getSema().Context.getTemplateTypeParmType(
|
||||
T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
|
||||
NewTTPDecl);
|
||||
TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
|
||||
NewTL.setNameLoc(TL.getNameLoc());
|
||||
return Result;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
|
||||
const Expr *OldConstr,
|
||||
const NamedDecl *New,
|
||||
const Expr *NewConstr) {
|
||||
if (Old && New && Old != New) {
|
||||
unsigned Depth1 = CalculateTemplateDepthForConstraints(
|
||||
*this, Old);
|
||||
unsigned Depth2 = CalculateTemplateDepthForConstraints(
|
||||
*this, New);
|
||||
|
||||
// Adjust the 'shallowest' verison of this to increase the depth to match
|
||||
// the 'other'.
|
||||
if (Depth2 > Depth1) {
|
||||
OldConstr = AdjustConstraintDepth(*this, Depth2 - Depth1)
|
||||
.TransformExpr(const_cast<Expr *>(OldConstr))
|
||||
.get();
|
||||
} else if (Depth1 > Depth2) {
|
||||
NewConstr = AdjustConstraintDepth(*this, Depth1 - Depth2)
|
||||
.TransformExpr(const_cast<Expr *>(NewConstr))
|
||||
.get();
|
||||
}
|
||||
}
|
||||
|
||||
llvm::FoldingSetNodeID ID1, ID2;
|
||||
OldConstr->Profile(ID1, Context, /*Canonical=*/true);
|
||||
NewConstr->Profile(ID2, Context, /*Canonical=*/true);
|
||||
return ID1 == ID2;
|
||||
}
|
||||
|
||||
bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) {
|
||||
assert(FD->getFriendObjectKind() && "Must be a friend!");
|
||||
|
||||
// The logic for non-templates is handled in ASTContext::isSameEntity, so we
|
||||
// don't have to bother checking 'DependsOnEnclosingTemplate' for a
|
||||
// non-function-template.
|
||||
assert(FD->getDescribedFunctionTemplate() &&
|
||||
"Non-function templates don't need to be checked");
|
||||
|
||||
SmallVector<const Expr *, 3> ACs;
|
||||
FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs);
|
||||
|
||||
unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD);
|
||||
for (const Expr *Constraint : ACs)
|
||||
if (ConstraintExpressionDependsOnEnclosingTemplate(OldTemplateDepth,
|
||||
Constraint))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sema::EnsureTemplateArgumentListConstraints(
|
||||
@ -432,26 +678,14 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
|
||||
// PushDeclContext because we don't have a scope.
|
||||
Sema::ContextRAII savedContext(*this, Decl);
|
||||
LocalInstantiationScope Scope(*this);
|
||||
MultiLevelTemplateArgumentList MLTAL;
|
||||
// FIXME: This will be replaced with some logic to get all the template
|
||||
// arguments when we switch to deferred template instantiation.
|
||||
MLTAL.addOuterTemplateArguments(TemplateArgs);
|
||||
|
||||
// If this is not an explicit specialization - we need to get the instantiated
|
||||
// version of the template arguments and add them to scope for the
|
||||
// substitution.
|
||||
if (Decl->isTemplateInstantiation()) {
|
||||
InstantiatingTemplate Inst(*this, Decl->getPointOfInstantiation(),
|
||||
InstantiatingTemplate::ConstraintsCheck{}, Decl->getPrimaryTemplate(),
|
||||
TemplateArgs, SourceRange());
|
||||
if (Inst.isInvalid())
|
||||
return true;
|
||||
MultiLevelTemplateArgumentList MLTAL(
|
||||
*Decl->getTemplateSpecializationArgs());
|
||||
if (addInstantiatedParametersToScope(
|
||||
Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), Scope, MLTAL))
|
||||
return true;
|
||||
}
|
||||
Optional<MultiLevelTemplateArgumentList> MLTAL =
|
||||
SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
|
||||
Scope);
|
||||
|
||||
if (!MLTAL)
|
||||
return true;
|
||||
|
||||
Qualifiers ThisQuals;
|
||||
CXXRecordDecl *Record = nullptr;
|
||||
if (auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
|
||||
@ -459,7 +693,8 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
|
||||
Record = Method->getParent();
|
||||
}
|
||||
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
|
||||
return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL,
|
||||
llvm::SmallVector<Expr *, 1> Converted;
|
||||
return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL,
|
||||
PointOfInstantiation, Satisfaction);
|
||||
}
|
||||
|
||||
@ -734,22 +969,22 @@ Sema::getNormalizedAssociatedConstraints(
|
||||
return CacheEntry->second;
|
||||
}
|
||||
|
||||
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
||||
ConceptDecl *Concept, ArrayRef<TemplateArgument> TemplateArgs,
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
|
||||
static bool
|
||||
substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
||||
ConceptDecl *Concept,
|
||||
const MultiLevelTemplateArgumentList &MLTAL,
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
|
||||
if (!N.isAtomic()) {
|
||||
if (substituteParameterMappings(S, N.getLHS(), Concept, TemplateArgs,
|
||||
if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
|
||||
ArgsAsWritten))
|
||||
return true;
|
||||
return substituteParameterMappings(S, N.getRHS(), Concept, TemplateArgs,
|
||||
return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
|
||||
ArgsAsWritten);
|
||||
}
|
||||
TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
|
||||
|
||||
AtomicConstraint &Atomic = *N.getAtomicConstraint();
|
||||
TemplateArgumentListInfo SubstArgs;
|
||||
MultiLevelTemplateArgumentList MLTAL;
|
||||
MLTAL.addOuterTemplateArguments(TemplateArgs);
|
||||
if (!Atomic.ParameterMapping) {
|
||||
llvm::SmallBitVector OccurringIndices(TemplateParams->size());
|
||||
S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
|
||||
@ -790,6 +1025,20 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
||||
const ConceptSpecializationExpr *CSE) {
|
||||
TemplateArgumentList TAL{TemplateArgumentList::OnStack,
|
||||
CSE->getTemplateArguments()};
|
||||
MultiLevelTemplateArgumentList MLTAL =
|
||||
S.getTemplateInstantiationArgs(CSE->getNamedConcept(), &TAL,
|
||||
/*RelativeToPrimary*/ true,
|
||||
/*Pattern*/ nullptr,
|
||||
/*LookBeyondLambda*/ true);
|
||||
|
||||
return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
|
||||
CSE->getTemplateArgsAsWritten());
|
||||
}
|
||||
|
||||
Optional<NormalizedConstraint>
|
||||
NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D,
|
||||
ArrayRef<const Expr *> E) {
|
||||
@ -852,9 +1101,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
|
||||
Optional<NormalizedConstraint> New;
|
||||
New.emplace(S.Context, *SubNF);
|
||||
|
||||
if (substituteParameterMappings(
|
||||
S, *New, CSE->getNamedConcept(),
|
||||
CSE->getTemplateArguments(), CSE->getTemplateArgsAsWritten()))
|
||||
if (substituteParameterMappings(S, *New, CSE))
|
||||
return None;
|
||||
|
||||
return New;
|
||||
|
@ -10480,6 +10480,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
|
||||
}
|
||||
|
||||
if (getLangOpts().CPlusPlus) {
|
||||
// Precalculate whether this is a friend function template with a constraint
|
||||
// that depends on an enclosing template, per [temp.friend]p9.
|
||||
if (isFriend && FunctionTemplate &&
|
||||
FriendConstraintsDependOnEnclosingTemplate(NewFD))
|
||||
NewFD->setFriendConstraintRefersToEnclosingTemplate(true);
|
||||
|
||||
if (FunctionTemplate) {
|
||||
if (NewFD->isInvalidDecl())
|
||||
FunctionTemplate->setInvalidDecl();
|
||||
|
@ -283,7 +283,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
|
||||
// definition.
|
||||
if (FD->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckFunctionConstraints(FD, Satisfaction, Loc))
|
||||
if (CheckFunctionConstraints(FD, Satisfaction, Loc,
|
||||
/*ForOverloadResolution*/ true))
|
||||
// A diagnostic will have already been generated (non-constant
|
||||
// constraint expression, for example)
|
||||
return true;
|
||||
|
@ -1074,6 +1074,15 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old,
|
||||
!shouldLinkPossiblyHiddenDecl(*I, New))
|
||||
continue;
|
||||
|
||||
// C++20 [temp.friend] p9: A non-template friend declaration with a
|
||||
// requires-clause shall be a definition. A friend function template
|
||||
// with a constraint that depends on a template parameter from an
|
||||
// enclosing template shall be a definition. Such a constrained friend
|
||||
// function or function template declaration does not declare the same
|
||||
// function or function template as a declaration in any other scope.
|
||||
if (Context.FriendsDifferByConstraints(OldF, New))
|
||||
continue;
|
||||
|
||||
Match = *I;
|
||||
return Ovl_Match;
|
||||
}
|
||||
@ -6521,7 +6530,8 @@ void Sema::AddOverloadCandidate(
|
||||
|
||||
if (Function->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckFunctionConstraints(Function, Satisfaction) ||
|
||||
if (CheckFunctionConstraints(Function, Satisfaction, /*Loc*/ {},
|
||||
/*ForOverloadResolution*/ true) ||
|
||||
!Satisfaction.IsSatisfied) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
|
||||
@ -7027,7 +7037,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
|
||||
|
||||
if (Method->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckFunctionConstraints(Method, Satisfaction) ||
|
||||
if (CheckFunctionConstraints(Method, Satisfaction, /*Loc*/ {},
|
||||
/*ForOverloadResolution*/ true) ||
|
||||
!Satisfaction.IsSatisfied) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
|
||||
|
@ -1687,6 +1687,61 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(Scope* S,
|
||||
return Param;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class ConstraintRefersToContainingTemplateChecker
|
||||
: public TreeTransform<ConstraintRefersToContainingTemplateChecker> {
|
||||
bool Result = false;
|
||||
unsigned TemplateDepth = 0;
|
||||
|
||||
public:
|
||||
using inherited = TreeTransform<ConstraintRefersToContainingTemplateChecker>;
|
||||
|
||||
ConstraintRefersToContainingTemplateChecker(Sema &SemaRef,
|
||||
unsigned TemplateDepth)
|
||||
: inherited(SemaRef), TemplateDepth(TemplateDepth) {}
|
||||
bool getResult() const { return Result; }
|
||||
|
||||
// This should be the only template parm type that we have to deal with.
|
||||
// SubstTempalteTypeParmPack, SubstNonTypeTemplateParmPack, and
|
||||
// FunctionParmPackExpr are all partially substituted, which cannot happen
|
||||
// with concepts at this point in translation.
|
||||
QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
|
||||
TemplateTypeParmTypeLoc TL) {
|
||||
assert(TL.getDecl()->getDepth() <= TemplateDepth &&
|
||||
"Nothing should reference a value below the actual template depth, "
|
||||
"depth is likely wrong");
|
||||
if (TL.getDecl()->getDepth() != TemplateDepth)
|
||||
Result = true;
|
||||
return inherited::TransformTemplateTypeParmType(TLB, TL);
|
||||
}
|
||||
|
||||
Decl *TransformDecl(SourceLocation Loc, Decl *D) {
|
||||
// FIXME : This is possibly an incomplete list, but it is unclear what other
|
||||
// Decl kinds could be used to refer to the template parameters. This is a
|
||||
// best guess so far based on examples currently available, but the
|
||||
// unreachable should catch future instances/cases.
|
||||
if (auto *TD = dyn_cast<TypedefNameDecl>(D))
|
||||
TransformType(TD->getUnderlyingType());
|
||||
else if (auto *VD = dyn_cast<ValueDecl>(D))
|
||||
TransformType(VD->getType());
|
||||
else if (auto *TD = dyn_cast<TemplateDecl>(D))
|
||||
TransformTemplateParameterList(TD->getTemplateParameters());
|
||||
else if (isa<NamedDecl>(D)) {
|
||||
// No direct types to visit here I believe.
|
||||
} else
|
||||
llvm_unreachable("Don't know how to handle this declaration type yet");
|
||||
return D;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool Sema::ConstraintExpressionDependsOnEnclosingTemplate(
|
||||
unsigned TemplateDepth, const Expr *Constraint) {
|
||||
ConstraintRefersToContainingTemplateChecker Checker(*this, TemplateDepth);
|
||||
Checker.TransformExpr(const_cast<Expr *>(Constraint));
|
||||
return Checker.getResult();
|
||||
}
|
||||
|
||||
/// ActOnTemplateParameterList - Builds a TemplateParameterList, optionally
|
||||
/// constrained by RequiresClause, that contains the template parameters in
|
||||
/// Params.
|
||||
@ -2288,7 +2343,8 @@ private:
|
||||
TTP->isExpandedParameterPack() ?
|
||||
llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None);
|
||||
if (const auto *TC = TTP->getTypeConstraint())
|
||||
SemaRef.SubstTypeConstraint(NewTTP, TC, Args);
|
||||
SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
|
||||
/*EvaluateConstraint*/ true);
|
||||
if (TTP->hasDefaultArgument()) {
|
||||
TypeSourceInfo *InstantiatedDefaultArg =
|
||||
SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
|
||||
@ -6014,10 +6070,30 @@ bool Sema::CheckTemplateArgumentList(
|
||||
TemplateArgs = std::move(NewArgs);
|
||||
|
||||
if (!PartialTemplateArgs) {
|
||||
// FIXME: This will be changed a bit once deferred concept instantiation is
|
||||
// implemented.
|
||||
MultiLevelTemplateArgumentList MLTAL;
|
||||
MLTAL.addOuterTemplateArguments(Converted);
|
||||
TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack,
|
||||
Converted);
|
||||
// Setup the context/ThisScope for the case where we are needing to
|
||||
// re-instantiate constraints outside of normal instantiation.
|
||||
DeclContext *NewContext = Template->getDeclContext();
|
||||
|
||||
// If this template is in a template, make sure we extract the templated
|
||||
// decl.
|
||||
if (auto *TD = dyn_cast<TemplateDecl>(NewContext))
|
||||
NewContext = Decl::castToDeclContext(TD->getTemplatedDecl());
|
||||
auto *RD = dyn_cast<CXXRecordDecl>(NewContext);
|
||||
|
||||
Qualifiers ThisQuals;
|
||||
if (const auto *Method =
|
||||
dyn_cast_or_null<CXXMethodDecl>(Template->getTemplatedDecl()))
|
||||
ThisQuals = Method->getMethodQualifiers();
|
||||
|
||||
ContextRAII Context(*this, NewContext);
|
||||
CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr);
|
||||
|
||||
MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
|
||||
Template, &StackTemplateArgs, /*RelativeToPrimary*/ true,
|
||||
/*Pattern*/ nullptr,
|
||||
/*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true);
|
||||
if (EnsureTemplateArgumentListConstraints(
|
||||
Template, MLTAL,
|
||||
SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
|
||||
@ -7564,7 +7640,9 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
|
||||
// are not considered.
|
||||
if (ParamsAC.empty())
|
||||
return false;
|
||||
|
||||
Template->getAssociatedConstraints(TemplateAC);
|
||||
|
||||
bool IsParamAtLeastAsConstrained;
|
||||
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
|
||||
IsParamAtLeastAsConstrained))
|
||||
@ -7760,10 +7838,10 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
|
||||
}
|
||||
|
||||
/// Match two template parameters within template parameter lists.
|
||||
static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
|
||||
bool Complain,
|
||||
Sema::TemplateParameterListEqualKind Kind,
|
||||
SourceLocation TemplateArgLoc) {
|
||||
static bool MatchTemplateParameterKind(
|
||||
Sema &S, NamedDecl *New, const NamedDecl *NewInstFrom, NamedDecl *Old,
|
||||
const NamedDecl *OldInstFrom, bool Complain,
|
||||
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
|
||||
// Check the actual kind (type, non-type, template).
|
||||
if (Old->getKind() != New->getKind()) {
|
||||
if (Complain) {
|
||||
@ -7845,13 +7923,13 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
|
||||
else if (TemplateTemplateParmDecl *OldTTP
|
||||
= dyn_cast<TemplateTemplateParmDecl>(Old)) {
|
||||
TemplateTemplateParmDecl *NewTTP = cast<TemplateTemplateParmDecl>(New);
|
||||
if (!S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(),
|
||||
OldTTP->getTemplateParameters(),
|
||||
Complain,
|
||||
(Kind == Sema::TPL_TemplateMatch
|
||||
? Sema::TPL_TemplateTemplateParmMatch
|
||||
: Kind),
|
||||
TemplateArgLoc))
|
||||
if (!S.TemplateParameterListsAreEqual(
|
||||
NewInstFrom, NewTTP->getTemplateParameters(), OldInstFrom,
|
||||
OldTTP->getTemplateParameters(), Complain,
|
||||
(Kind == Sema::TPL_TemplateMatch
|
||||
? Sema::TPL_TemplateTemplateParmMatch
|
||||
: Kind),
|
||||
TemplateArgLoc))
|
||||
return false;
|
||||
} else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) {
|
||||
const Expr *NewC = nullptr, *OldC = nullptr;
|
||||
@ -7874,10 +7952,8 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
|
||||
}
|
||||
|
||||
if (NewC) {
|
||||
llvm::FoldingSetNodeID OldCID, NewCID;
|
||||
OldC->Profile(OldCID, S.Context, /*Canonical=*/true);
|
||||
NewC->Profile(NewCID, S.Context, /*Canonical=*/true);
|
||||
if (OldCID != NewCID) {
|
||||
if (!S.AreConstraintExpressionsEqual(OldInstFrom, OldC, NewInstFrom,
|
||||
NewC)) {
|
||||
if (Complain)
|
||||
Diagnose();
|
||||
return false;
|
||||
@ -7933,12 +8009,10 @@ void DiagnoseTemplateParameterListArityMismatch(Sema &S,
|
||||
///
|
||||
/// \returns True if the template parameter lists are equal, false
|
||||
/// otherwise.
|
||||
bool
|
||||
Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
|
||||
TemplateParameterList *Old,
|
||||
bool Complain,
|
||||
TemplateParameterListEqualKind Kind,
|
||||
SourceLocation TemplateArgLoc) {
|
||||
bool Sema::TemplateParameterListsAreEqual(
|
||||
const NamedDecl *NewInstFrom, TemplateParameterList *New,
|
||||
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
|
||||
TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
|
||||
if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
|
||||
if (Complain)
|
||||
DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
|
||||
@ -7968,8 +8042,9 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
|
||||
Kind, TemplateArgLoc))
|
||||
if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
|
||||
OldInstFrom, Complain, Kind,
|
||||
TemplateArgLoc))
|
||||
return false;
|
||||
|
||||
++NewParm;
|
||||
@ -7984,8 +8059,9 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
|
||||
// template parameter pack in P (ignoring whether those template
|
||||
// parameters are template parameter packs).
|
||||
for (; NewParm != NewParmEnd; ++NewParm) {
|
||||
if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain,
|
||||
Kind, TemplateArgLoc))
|
||||
if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
|
||||
OldInstFrom, Complain, Kind,
|
||||
TemplateArgLoc))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -8017,10 +8093,8 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
|
||||
}
|
||||
|
||||
if (NewRC) {
|
||||
llvm::FoldingSetNodeID OldRCID, NewRCID;
|
||||
OldRC->Profile(OldRCID, Context, /*Canonical=*/true);
|
||||
NewRC->Profile(NewRCID, Context, /*Canonical=*/true);
|
||||
if (OldRCID != NewRCID) {
|
||||
if (!AreConstraintExpressionsEqual(OldInstFrom, OldRC, NewInstFrom,
|
||||
NewRC)) {
|
||||
if (Complain)
|
||||
Diagnose();
|
||||
return false;
|
||||
|
@ -2848,6 +2848,20 @@ template<>
|
||||
struct IsPartialSpecialization<VarTemplatePartialSpecializationDecl> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template <typename TemplateDeclT>
|
||||
static bool DeducedArgsNeedReplacement(TemplateDeclT *Template) {
|
||||
return false;
|
||||
}
|
||||
template <>
|
||||
bool DeducedArgsNeedReplacement<VarTemplatePartialSpecializationDecl>(
|
||||
VarTemplatePartialSpecializationDecl *Spec) {
|
||||
return !Spec->isClassScopeExplicitSpecialization();
|
||||
}
|
||||
template <>
|
||||
bool DeducedArgsNeedReplacement<ClassTemplatePartialSpecializationDecl>(
|
||||
ClassTemplatePartialSpecializationDecl *Spec) {
|
||||
return !Spec->isClassScopeExplicitSpecialization();
|
||||
}
|
||||
|
||||
template<typename TemplateDeclT>
|
||||
static Sema::TemplateDeductionResult
|
||||
@ -2856,13 +2870,25 @@ CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,
|
||||
TemplateDeductionInfo& Info) {
|
||||
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
|
||||
Template->getAssociatedConstraints(AssociatedConstraints);
|
||||
// FIXME: This will change quite a bit once deferred concept instantiation is
|
||||
// implemented.
|
||||
MultiLevelTemplateArgumentList MLTAL;
|
||||
MLTAL.addOuterTemplateArguments(DeducedArgs);
|
||||
|
||||
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints,
|
||||
MLTAL, Info.getLocation(),
|
||||
bool NeedsReplacement = DeducedArgsNeedReplacement(Template);
|
||||
TemplateArgumentList DeducedTAL{TemplateArgumentList::OnStack, DeducedArgs};
|
||||
|
||||
MLTAL = S.getTemplateInstantiationArgs(
|
||||
Template, /*InnerMost*/ NeedsReplacement ? nullptr : &DeducedTAL,
|
||||
/*RelativeToPrimary*/ true, /*Pattern*/
|
||||
nullptr, /*LookBeyondLambda*/ true);
|
||||
|
||||
// getTemplateInstantiationArgs picks up the non-deduced version of the
|
||||
// template args when this is a variable template partial specialization and
|
||||
// not class-scope explicit specialization, so replace with Deduced Args
|
||||
// instead of adding to inner-most.
|
||||
if (NeedsReplacement)
|
||||
MLTAL.replaceInnermostTemplateArguments(DeducedArgs);
|
||||
|
||||
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
|
||||
Info.getLocation(),
|
||||
Info.AssociatedConstraintsSatisfaction) ||
|
||||
!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
|
||||
Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs));
|
||||
|
@ -57,9 +57,18 @@ using namespace sema;
|
||||
/// instantiating the definition of the given declaration, \p D. This is
|
||||
/// used to determine the proper set of template instantiation arguments for
|
||||
/// friend function template specializations.
|
||||
///
|
||||
/// \param LookBeyondLambda Indicates that this collection of arguments should
|
||||
/// continue looking when it encounters a lambda generic call operator.
|
||||
///
|
||||
/// \param IncludeContainingStructArgs Indicates that this collection of
|
||||
/// arguments should include arguments for any class template that this
|
||||
/// declaration is included inside of.
|
||||
|
||||
MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
|
||||
const NamedDecl *D, const TemplateArgumentList *Innermost,
|
||||
bool RelativeToPrimary, const FunctionDecl *Pattern) {
|
||||
bool RelativeToPrimary, const FunctionDecl *Pattern, bool LookBeyondLambda,
|
||||
bool IncludeContainingStructArgs) {
|
||||
// Accumulate the set of template argument lists in this structure.
|
||||
MultiLevelTemplateArgumentList Result;
|
||||
|
||||
@ -155,11 +164,13 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
|
||||
break;
|
||||
|
||||
// If this function is a generic lambda specialization, we are done.
|
||||
if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
|
||||
if (!LookBeyondLambda &&
|
||||
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
|
||||
break;
|
||||
|
||||
} else if (Function->getDescribedFunctionTemplate()) {
|
||||
assert(Result.getNumSubstitutedLevels() == 0 &&
|
||||
assert((IncludeContainingStructArgs ||
|
||||
Result.getNumSubstitutedLevels() == 0) &&
|
||||
"Outer template not instantiated?");
|
||||
}
|
||||
|
||||
@ -176,10 +187,28 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
|
||||
}
|
||||
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(Ctx)) {
|
||||
if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) {
|
||||
assert(Result.getNumSubstitutedLevels() == 0 &&
|
||||
assert((IncludeContainingStructArgs ||
|
||||
Result.getNumSubstitutedLevels() == 0) &&
|
||||
"Outer template not instantiated?");
|
||||
if (ClassTemplate->isMemberSpecialization())
|
||||
break;
|
||||
if (IncludeContainingStructArgs) {
|
||||
QualType RecordType = Context.getTypeDeclType(Rec);
|
||||
QualType Injected = cast<InjectedClassNameType>(RecordType)
|
||||
->getInjectedSpecializationType();
|
||||
const auto *InjectedType = cast<TemplateSpecializationType>(Injected);
|
||||
Result.addOuterTemplateArguments(InjectedType->template_arguments());
|
||||
}
|
||||
}
|
||||
bool IsFriend = Rec->getFriendObjectKind() ||
|
||||
(Rec->getDescribedClassTemplate() &&
|
||||
Rec->getDescribedClassTemplate()->getFriendObjectKind());
|
||||
if (IncludeContainingStructArgs && IsFriend &&
|
||||
Rec->getNonTransparentDeclContext()->isFileContext() &&
|
||||
(!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) {
|
||||
Ctx = Rec->getLexicalDeclContext();
|
||||
RelativeToPrimary = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -930,16 +959,23 @@ namespace {
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs;
|
||||
SourceLocation Loc;
|
||||
DeclarationName Entity;
|
||||
bool EvaluateConstraints = true;
|
||||
|
||||
public:
|
||||
typedef TreeTransform<TemplateInstantiator> inherited;
|
||||
|
||||
TemplateInstantiator(Sema &SemaRef,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
SourceLocation Loc,
|
||||
DeclarationName Entity)
|
||||
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
|
||||
Entity(Entity) { }
|
||||
SourceLocation Loc, DeclarationName Entity)
|
||||
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
|
||||
Entity(Entity) {}
|
||||
|
||||
void setEvaluateConstraints(bool B) {
|
||||
EvaluateConstraints = B;
|
||||
}
|
||||
bool getEvaluateConstraints() {
|
||||
return EvaluateConstraints;
|
||||
}
|
||||
|
||||
/// Determine whether the given type \p T has already been
|
||||
/// transformed.
|
||||
@ -1146,7 +1182,9 @@ namespace {
|
||||
|
||||
ExprResult TransformLambdaExpr(LambdaExpr *E) {
|
||||
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
|
||||
return inherited::TransformLambdaExpr(E);
|
||||
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
|
||||
ExprResult Res = inherited::TransformLambdaExpr(E);
|
||||
return Res;
|
||||
}
|
||||
|
||||
ExprResult TransformRequiresExpr(RequiresExpr *E) {
|
||||
@ -1191,6 +1229,7 @@ namespace {
|
||||
DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
|
||||
TemplateDeclInstantiator DeclInstantiator(getSema(),
|
||||
/* DeclContext *Owner */ Owner, TemplateArgs);
|
||||
DeclInstantiator.setEvaluateConstraints(EvaluateConstraints);
|
||||
return DeclInstantiator.SubstTemplateParams(OrigTPL);
|
||||
}
|
||||
|
||||
@ -1766,9 +1805,9 @@ TemplateInstantiator::TransformFunctionTypeParam(ParmVarDecl *OldParm,
|
||||
int indexAdjustment,
|
||||
Optional<unsigned> NumExpansions,
|
||||
bool ExpectParameterPack) {
|
||||
auto NewParm =
|
||||
SemaRef.SubstParmVarDecl(OldParm, TemplateArgs, indexAdjustment,
|
||||
NumExpansions, ExpectParameterPack);
|
||||
auto NewParm = SemaRef.SubstParmVarDecl(
|
||||
OldParm, TemplateArgs, indexAdjustment, NumExpansions,
|
||||
ExpectParameterPack, EvaluateConstraints);
|
||||
if (NewParm && SemaRef.getLangOpts().OpenCL)
|
||||
SemaRef.deduceOpenCLAddressSpace(NewParm);
|
||||
return NewParm;
|
||||
@ -1988,8 +2027,7 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
|
||||
Req, Info, OrigTPL->getSourceRange());
|
||||
if (TPLInst.isInvalid())
|
||||
return nullptr;
|
||||
TemplateParameterList *TPL =
|
||||
TransformTemplateParameterList(OrigTPL);
|
||||
TemplateParameterList *TPL = TransformTemplateParameterList(OrigTPL);
|
||||
if (!TPL)
|
||||
TransRetReq.emplace(createSubstDiag(SemaRef, Info,
|
||||
[&] (llvm::raw_ostream& OS) {
|
||||
@ -2199,7 +2237,8 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
|
||||
SourceLocation Loc,
|
||||
DeclarationName Entity,
|
||||
CXXRecordDecl *ThisContext,
|
||||
Qualifiers ThisTypeQuals) {
|
||||
Qualifiers ThisTypeQuals,
|
||||
bool EvaluateConstraints) {
|
||||
assert(!CodeSynthesisContexts.empty() &&
|
||||
"Cannot perform an instantiation without some context on the "
|
||||
"instantiation stack");
|
||||
@ -2208,6 +2247,7 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
|
||||
return T;
|
||||
|
||||
TemplateInstantiator Instantiator(*this, Args, Loc, Entity);
|
||||
Instantiator.setEvaluateConstraints(EvaluateConstraints);
|
||||
|
||||
TypeLocBuilder TLB;
|
||||
|
||||
@ -2352,9 +2392,19 @@ namespace {
|
||||
|
||||
bool Sema::SubstTypeConstraint(
|
||||
TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs) {
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
bool EvaluateConstraints) {
|
||||
const ASTTemplateArgumentListInfo *TemplArgInfo =
|
||||
TC->getTemplateArgsAsWritten();
|
||||
|
||||
if (!EvaluateConstraints) {
|
||||
Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(),
|
||||
TC->getConceptNameInfo(), TC->getNamedConcept(),
|
||||
TC->getNamedConcept(), TemplArgInfo,
|
||||
TC->getImmediatelyDeclaredConstraint());
|
||||
return false;
|
||||
}
|
||||
|
||||
TemplateArgumentListInfo InstArgs;
|
||||
|
||||
if (TemplArgInfo) {
|
||||
@ -2373,11 +2423,11 @@ bool Sema::SubstTypeConstraint(
|
||||
: SourceLocation());
|
||||
}
|
||||
|
||||
ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
int indexAdjustment,
|
||||
Optional<unsigned> NumExpansions,
|
||||
bool ExpectParameterPack) {
|
||||
ParmVarDecl *
|
||||
Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
int indexAdjustment, Optional<unsigned> NumExpansions,
|
||||
bool ExpectParameterPack, bool EvaluateConstraint) {
|
||||
TypeSourceInfo *OldDI = OldParm->getTypeSourceInfo();
|
||||
TypeSourceInfo *NewDI = nullptr;
|
||||
|
||||
@ -2435,9 +2485,7 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
|
||||
// template's described function, but we might also get here later.
|
||||
// Make sure we do not instantiate the TypeConstraint more than once.
|
||||
if (Inst && !Inst->getTypeConstraint()) {
|
||||
// TODO: Concepts: do not instantiate the constraint (delayed constraint
|
||||
// substitution)
|
||||
if (SubstTypeConstraint(Inst, TC, TemplateArgs))
|
||||
if (SubstTypeConstraint(Inst, TC, TemplateArgs, EvaluateConstraint))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -2746,6 +2794,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
|
||||
Instantiation->setInvalidDecl();
|
||||
|
||||
TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs);
|
||||
Instantiator.setEvaluateConstraints(false);
|
||||
SmallVector<Decl*, 4> Fields;
|
||||
// Delay instantiation of late parsed attributes.
|
||||
LateInstantiatedAttrVec LateAttrs;
|
||||
@ -3524,11 +3573,9 @@ bool Sema::SubstTemplateArguments(
|
||||
ArrayRef<TemplateArgumentLoc> Args,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
TemplateArgumentListInfo &Out) {
|
||||
TemplateInstantiator Instantiator(*this, TemplateArgs,
|
||||
SourceLocation(),
|
||||
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
|
||||
DeclarationName());
|
||||
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(),
|
||||
Out);
|
||||
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
|
||||
}
|
||||
|
||||
ExprResult
|
||||
@ -3542,11 +3589,23 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
|
||||
return Instantiator.TransformExpr(E);
|
||||
}
|
||||
|
||||
ExprResult
|
||||
Sema::SubstConstraintExpr(Expr *E,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs) {
|
||||
if (!E)
|
||||
return E;
|
||||
|
||||
// This is where we need to make sure we 'know' constraint checking needs to
|
||||
// happen.
|
||||
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
|
||||
DeclarationName());
|
||||
return Instantiator.TransformExpr(E);
|
||||
}
|
||||
|
||||
ExprResult Sema::SubstInitializer(Expr *Init,
|
||||
const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
bool CXXDirectInit) {
|
||||
TemplateInstantiator Instantiator(*this, TemplateArgs,
|
||||
SourceLocation(),
|
||||
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
|
||||
DeclarationName());
|
||||
return Instantiator.TransformInitializer(Init, CXXDirectInit);
|
||||
}
|
||||
|
@ -1635,12 +1635,16 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
|
||||
}
|
||||
|
||||
if (PrevClassTemplate) {
|
||||
TemplateParameterList *PrevParams
|
||||
= PrevClassTemplate->getMostRecentDecl()->getTemplateParameters();
|
||||
const ClassTemplateDecl *MostRecentPrevCT =
|
||||
PrevClassTemplate->getMostRecentDecl();
|
||||
TemplateParameterList *PrevParams =
|
||||
MostRecentPrevCT->getTemplateParameters();
|
||||
|
||||
// Make sure the parameter lists match.
|
||||
if (!SemaRef.TemplateParameterListsAreEqual(InstParams, PrevParams, true,
|
||||
Sema::TPL_TemplateMatch))
|
||||
if (!SemaRef.TemplateParameterListsAreEqual(
|
||||
D->getTemplatedDecl(), InstParams,
|
||||
MostRecentPrevCT->getTemplatedDecl(), PrevParams, true,
|
||||
Sema::TPL_TemplateMatch))
|
||||
return nullptr;
|
||||
|
||||
// Do some additional validation, then merge default arguments
|
||||
@ -1830,6 +1834,7 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
|
||||
// merged with the local instantiation scope for the function template
|
||||
// itself.
|
||||
LocalInstantiationScope Scope(SemaRef);
|
||||
Sema::ConstraintEvalRAII<TemplateDeclInstantiator> RAII(*this);
|
||||
|
||||
TemplateParameterList *TempParams = D->getTemplateParameters();
|
||||
TemplateParameterList *InstParams = SubstTemplateParams(TempParams);
|
||||
@ -2069,19 +2074,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// FIXME: Concepts: Do not substitute into constraint expressions
|
||||
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
|
||||
if (TrailingRequiresClause) {
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
|
||||
ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
|
||||
TemplateArgs);
|
||||
if (SubstRC.isInvalid())
|
||||
return nullptr;
|
||||
TrailingRequiresClause = SubstRC.get();
|
||||
if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If we're instantiating a local function declaration, put the result
|
||||
// in the enclosing namespace; otherwise we need to find the instantiated
|
||||
@ -2121,6 +2114,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
|
||||
D->getCanonicalDecl()->getStorageClass(), D->UsesFPIntrin(),
|
||||
D->isInlineSpecified(), D->hasWrittenPrototype(), D->getConstexprKind(),
|
||||
TrailingRequiresClause);
|
||||
Function->setFriendConstraintRefersToEnclosingTemplate(
|
||||
D->FriendConstraintRefersToEnclosingTemplate());
|
||||
Function->setRangeEnd(D->getSourceRange().getEnd());
|
||||
}
|
||||
|
||||
@ -2432,23 +2427,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// FIXME: Concepts: Do not substitute into constraint expressions
|
||||
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
|
||||
if (TrailingRequiresClause) {
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
|
||||
auto *ThisContext = dyn_cast_or_null<CXXRecordDecl>(Owner);
|
||||
Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext,
|
||||
D->getMethodQualifiers(), ThisContext);
|
||||
ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
|
||||
TemplateArgs);
|
||||
if (SubstRC.isInvalid())
|
||||
return nullptr;
|
||||
TrailingRequiresClause = SubstRC.get();
|
||||
if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DeclContext *DC = Owner;
|
||||
if (isFriend) {
|
||||
if (QualifierLoc) {
|
||||
@ -2466,6 +2444,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
|
||||
if (!DC) return nullptr;
|
||||
}
|
||||
|
||||
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
|
||||
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
|
||||
|
||||
DeclarationNameInfo NameInfo
|
||||
= SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
|
||||
|
||||
@ -2473,7 +2454,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
|
||||
adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo);
|
||||
|
||||
// Build the instantiated method declaration.
|
||||
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
|
||||
CXXMethodDecl *Method = nullptr;
|
||||
|
||||
SourceLocation StartLoc = D->getInnerLocStart();
|
||||
@ -2783,13 +2763,11 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
|
||||
Inst->setImplicit(D->isImplicit());
|
||||
if (auto *TC = D->getTypeConstraint()) {
|
||||
if (!D->isImplicit()) {
|
||||
// Invented template parameter type constraints will be instantiated with
|
||||
// the corresponding auto-typed parameter as it might reference other
|
||||
// parameters.
|
||||
|
||||
// TODO: Concepts: do not instantiate the constraint (delayed constraint
|
||||
// substitution)
|
||||
if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs))
|
||||
// Invented template parameter type constraints will be instantiated
|
||||
// with the corresponding auto-typed parameter as it might reference
|
||||
// other parameters.
|
||||
if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs,
|
||||
EvaluateConstraints))
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -4033,18 +4011,7 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
|
||||
if (Invalid)
|
||||
return nullptr;
|
||||
|
||||
// FIXME: Concepts: Substitution into requires clause should only happen when
|
||||
// checking satisfaction.
|
||||
Expr *InstRequiresClause = nullptr;
|
||||
if (Expr *E = L->getRequiresClause()) {
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
|
||||
ExprResult Res = SemaRef.SubstExpr(E, TemplateArgs);
|
||||
if (Res.isInvalid() || !Res.isUsable()) {
|
||||
return nullptr;
|
||||
}
|
||||
InstRequiresClause = Res.get();
|
||||
}
|
||||
Expr *InstRequiresClause = L->getRequiresClause();
|
||||
|
||||
TemplateParameterList *InstL
|
||||
= TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(),
|
||||
@ -4338,11 +4305,9 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
|
||||
ThisTypeQuals = Method->getMethodQualifiers();
|
||||
}
|
||||
|
||||
TypeSourceInfo *NewTInfo
|
||||
= SemaRef.SubstFunctionDeclType(OldTInfo, TemplateArgs,
|
||||
D->getTypeSpecStartLoc(),
|
||||
D->getDeclName(),
|
||||
ThisContext, ThisTypeQuals);
|
||||
TypeSourceInfo *NewTInfo = SemaRef.SubstFunctionDeclType(
|
||||
OldTInfo, TemplateArgs, D->getTypeSpecStartLoc(), D->getDeclName(),
|
||||
ThisContext, ThisTypeQuals, EvaluateConstraints);
|
||||
if (!NewTInfo)
|
||||
return nullptr;
|
||||
|
||||
|
@ -13077,13 +13077,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
||||
NewCallOpType);
|
||||
}
|
||||
|
||||
// Transform the trailing requires clause
|
||||
ExprResult NewTrailingRequiresClause;
|
||||
if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause())
|
||||
// FIXME: Concepts: Substitution into requires clause should only happen
|
||||
// when checking satisfaction.
|
||||
NewTrailingRequiresClause = getDerived().TransformExpr(TRC);
|
||||
|
||||
// Create the local class that will describe the lambda.
|
||||
|
||||
// FIXME: DependencyKind below is wrong when substituting inside a templated
|
||||
@ -13118,7 +13111,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
||||
E->getCallOperator()->getEndLoc(),
|
||||
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(),
|
||||
E->getCallOperator()->getConstexprKind(),
|
||||
NewTrailingRequiresClause.get());
|
||||
E->getCallOperator()->getTrailingRequiresClause());
|
||||
|
||||
LSI->CallOperator = NewCallOperator;
|
||||
|
||||
|
@ -930,6 +930,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
|
||||
FD->setHasSkippedBody(Record.readInt());
|
||||
FD->setIsMultiVersion(Record.readInt());
|
||||
FD->setLateTemplateParsed(Record.readInt());
|
||||
FD->setFriendConstraintRefersToEnclosingTemplate(Record.readInt());
|
||||
|
||||
FD->setCachedLinkage(static_cast<Linkage>(Record.readInt()));
|
||||
FD->EndRangeLoc = readSourceLocation();
|
||||
|
@ -802,7 +802,7 @@ void ASTStmtReader::VisitConceptSpecializationExpr(
|
||||
E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo();
|
||||
llvm::SmallVector<TemplateArgument, 4> Args;
|
||||
for (unsigned I = 0; I < NumTemplateArgs; ++I)
|
||||
Args.push_back(Record.readTemplateArgument());
|
||||
Args.push_back(Record.readTemplateArgument(/*Canonicalize*/ true));
|
||||
E->setTemplateArguments(Args);
|
||||
E->Satisfaction = E->isValueDependent() ? nullptr :
|
||||
ASTConstraintSatisfaction::Create(Record.getContext(),
|
||||
|
@ -563,6 +563,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
|
||||
Record.push_back(D->hasSkippedBody());
|
||||
Record.push_back(D->isMultiVersion());
|
||||
Record.push_back(D->isLateTemplateParsed());
|
||||
Record.push_back(D->FriendConstraintRefersToEnclosingTemplate());
|
||||
Record.push_back(D->getLinkageInternal());
|
||||
Record.AddSourceLocation(D->getEndLoc());
|
||||
|
||||
@ -2285,6 +2286,7 @@ void ASTWriter::WriteDeclAbbrevs() {
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // SkippedBody
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // MultiVersion
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // LateParsed
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // FriendConstraintRefersToEnclosingTemplate
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LocEnd
|
||||
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // ODRHash
|
||||
|
@ -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
|
||||
|
@ -52,6 +52,30 @@ static_assert(F<unsigned>::value == 2);
|
||||
static_assert(F<char[10]>::value == 3);
|
||||
static_assert(F<char>::value == 1);
|
||||
|
||||
template <unsigned I>
|
||||
struct S {
|
||||
template <typename T>
|
||||
struct F {
|
||||
enum { value = 1 };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires C1<T> && C2<T>
|
||||
struct F<T> {
|
||||
enum { value = 2 };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires C1<T> || C2<T>
|
||||
struct F<T> {
|
||||
enum { value = 3 };
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(S<1>::F<unsigned>::value == 2);
|
||||
static_assert(S<1>::F<char[10]>::value == 3);
|
||||
static_assert(S<1>::F<char>::value == 1);
|
||||
|
||||
// Make sure atomic constraints subsume each other only if their parameter
|
||||
// mappings are identical.
|
||||
|
||||
|
@ -51,5 +51,20 @@ static_assert(f<unsigned> == 2);
|
||||
static_assert(f<char[10]> == 3);
|
||||
static_assert(f<char> == 1);
|
||||
|
||||
template <int I>
|
||||
struct S {
|
||||
template <typename T>
|
||||
static constexpr int f = 1;
|
||||
|
||||
template <typename T>
|
||||
requires C1<T> && C2<T>
|
||||
static constexpr int f<T> = 2;
|
||||
|
||||
template <typename T>
|
||||
requires C1<T> || C2<T>
|
||||
static constexpr int f<T> = 3;
|
||||
};
|
||||
|
||||
static_assert(S<1>::f<unsigned> == 2);
|
||||
static_assert(S<1>::f<char[10]> == 3);
|
||||
static_assert(S<1>::f<char> == 1);
|
||||
|
27
clang/test/Modules/concept_serialization.cpp
Normal file
27
clang/test/Modules/concept_serialization.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
// RUN: rm -rf %t
|
||||
// RUN: %clang_cc1 -std=c++20 -fmodules-cache-path=%t -x c++ %s -verify
|
||||
// expected-no-diagnostics
|
||||
#pragma clang module build std
|
||||
module std [system] { module concepts [system] {} }
|
||||
#pragma clang module contents
|
||||
|
||||
#pragma clang module begin std.concepts
|
||||
template <class T>
|
||||
T declval();
|
||||
template<class T, class U>
|
||||
concept common_reference_with = T::val;
|
||||
template<class T>
|
||||
concept input_or_output_iterator = true;
|
||||
template <class T>
|
||||
concept input_iterator = input_or_output_iterator<T> &&
|
||||
common_reference_with<decltype(declval<T&>)&&, T&>;
|
||||
#pragma clang module end /*std.concepts*/
|
||||
#pragma clang module endbuild /*std*/
|
||||
|
||||
#pragma clang module import std.concepts
|
||||
template<input_or_output_iterator>
|
||||
struct iter_value_or_void{};
|
||||
// ensure that we don't assert on a subsumption check due to improper
|
||||
// deserialization.
|
||||
template<input_iterator I>
|
||||
struct iter_value_or_void<I>{};
|
@ -196,17 +196,13 @@ static_assert(!__is_trivial(ComplexConstraints<int>), "");
|
||||
|
||||
// This is evaluated at the completion of CRTPBase, while `T` is not yet completed.
|
||||
// This is probably correct behavior.
|
||||
// FIXME: We should not throw an error, instead SFINAE should make the constraint
|
||||
// silently unsatisfied. See [temp.constr.constr]p5
|
||||
template <class T>
|
||||
struct CRTPBase {
|
||||
CRTPBase() requires (sizeof(T) > 0); // expected-error {{to an incomplete type}}
|
||||
CRTPBase() requires (sizeof(T) > 0);
|
||||
CRTPBase() = default;
|
||||
};
|
||||
|
||||
struct Child : CRTPBase<Child> { int x; };
|
||||
// expected-note@-1 {{definition of 'Child' is not complete until}}
|
||||
// expected-note@-2 {{in instantiation of template class 'CRTPBase<Child>' requested here}}
|
||||
static Child c;
|
||||
|
||||
|
||||
|
396
clang/test/SemaTemplate/concepts-friends.cpp
Normal file
396
clang/test/SemaTemplate/concepts-friends.cpp
Normal file
@ -0,0 +1,396 @@
|
||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
|
||||
|
||||
template <typename T>
|
||||
concept constraint = false;
|
||||
namespace temp_friend_9 {
|
||||
// A non-template friend declaration with a requires-clause shall be a
|
||||
// definition. ...Such a constrained friend function ... does not declare the
|
||||
// same function or function template as a declaration in any other scope.
|
||||
template <typename T>
|
||||
struct NonTemplateFriend {
|
||||
friend void foo()
|
||||
requires true
|
||||
{}
|
||||
};
|
||||
|
||||
// A friend function template with a constraint that depends on a template
|
||||
// parameter from an enclosing template shall be a definition. Such a ...
|
||||
// function template declaration does not declare the same function or
|
||||
// function template as a declaration in any other scope.
|
||||
template <typename T>
|
||||
struct TemplateFromEnclosing {
|
||||
template <typename U>
|
||||
friend void foo()
|
||||
requires constraint<T>
|
||||
{}
|
||||
|
||||
T variable;
|
||||
template <typename U>
|
||||
friend void foo2()
|
||||
requires constraint<decltype(variable)>
|
||||
{}
|
||||
|
||||
template <typename U>
|
||||
friend void foo3(T parmvar)
|
||||
requires constraint<decltype(parmvar)>
|
||||
{}
|
||||
|
||||
template <typename U>
|
||||
friend void foo4()
|
||||
requires requires(T &req) { (void)req; }
|
||||
{}
|
||||
|
||||
using Alias = T;
|
||||
template <typename U>
|
||||
friend void foo5()
|
||||
requires constraint<Alias>
|
||||
{}
|
||||
|
||||
// All of these refer to a parent, so these are not duplicate definitions.
|
||||
struct ChildOfEnclosing {
|
||||
template <typename U>
|
||||
friend void foo6()
|
||||
requires constraint<T>
|
||||
{}
|
||||
template <typename U>
|
||||
friend void foo7()
|
||||
requires constraint<decltype(variable)>
|
||||
{}
|
||||
template <typename U>
|
||||
friend void foo8(T parmvar)
|
||||
requires constraint<decltype(parmvar)>
|
||||
{}
|
||||
// This is NOT a duplicate since it itself is not a template.
|
||||
friend void foo9()
|
||||
requires true
|
||||
{}
|
||||
};
|
||||
template <typename T2>
|
||||
struct TemplChildOfEnclosing {
|
||||
template <typename U>
|
||||
friend void foo10()
|
||||
requires constraint<T>
|
||||
{}
|
||||
};
|
||||
};
|
||||
|
||||
// Doesn't meet either of the requirements in the above as they don't refer to
|
||||
// an enclosing scope.
|
||||
template <typename T>
|
||||
struct Redefinition {
|
||||
template <typename U>
|
||||
friend void foo() // #REDEF
|
||||
requires constraint<U>
|
||||
{}
|
||||
|
||||
struct ChildOfRedef {
|
||||
template <typename U>
|
||||
friend void foo2() // #REDEF2
|
||||
requires constraint<U>
|
||||
{}
|
||||
};
|
||||
template <typename T2>
|
||||
struct ChildOfRedef2 {
|
||||
template <typename U>
|
||||
friend void foo3() // #REDEF3
|
||||
requires constraint<U>
|
||||
{}
|
||||
};
|
||||
};
|
||||
|
||||
void bar() {
|
||||
NonTemplateFriend<int> S1;
|
||||
NonTemplateFriend<float> S2;
|
||||
TemplateFromEnclosing<int> S3;
|
||||
TemplateFromEnclosing<int>::ChildOfEnclosing S3b;
|
||||
TemplateFromEnclosing<float> S4;
|
||||
TemplateFromEnclosing<float>::ChildOfEnclosing S4b;
|
||||
Redefinition<int> S5;
|
||||
Redefinition<float> S6;
|
||||
// expected-error@#REDEF {{redefinition of 'foo'}}
|
||||
// expected-note@-2{{in instantiation of template class }}
|
||||
// expected-note@#REDEF {{previous definition is here}}
|
||||
Redefinition<int>::ChildOfRedef S7;
|
||||
Redefinition<float>::ChildOfRedef S8;
|
||||
// expected-error@#REDEF2 {{redefinition of 'foo2'}}
|
||||
// expected-note@-2{{in instantiation of member class }}
|
||||
// expected-note@#REDEF2 {{previous definition is here}}
|
||||
|
||||
Redefinition<int>::ChildOfRedef2<int> S9;
|
||||
Redefinition<float>::ChildOfRedef2<float> S10;
|
||||
// expected-error@#REDEF3 {{redefinition of 'foo3'}}
|
||||
// expected-note@-2{{in instantiation of template class }}
|
||||
// expected-note@#REDEF3 {{previous definition is here}}
|
||||
}
|
||||
} // namespace temp_friend_9
|
||||
|
||||
namespace SameScopeRedefs {
|
||||
template <typename T>
|
||||
struct NonTemplateFriend {
|
||||
friend void foo() // #NTF1
|
||||
requires true
|
||||
{}
|
||||
friend void foo() // #NTF2
|
||||
requires true
|
||||
{}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TemplateFromEnclosing {
|
||||
template <typename U>
|
||||
friend void foo() // #TFE1
|
||||
requires constraint<T>
|
||||
{}
|
||||
template <typename U>
|
||||
friend void foo() // #TFE2
|
||||
requires constraint<T>
|
||||
{}
|
||||
};
|
||||
// Same as above, but doesn't require an instantiation pair to cause.
|
||||
template <typename T>
|
||||
struct Redefinition {
|
||||
template <typename U>
|
||||
friend void foo() // #RD1
|
||||
requires constraint<U>
|
||||
{}
|
||||
template <typename U>
|
||||
friend void foo() // #RD2
|
||||
requires constraint<U>
|
||||
{}
|
||||
};
|
||||
void bar() {
|
||||
NonTemplateFriend<int> S1;
|
||||
// expected-error@#NTF2 {{redefinition of 'foo'}}
|
||||
// expected-note@-2{{in instantiation of template class}}
|
||||
// expected-note@#NTF1 {{previous definition is here}}
|
||||
|
||||
TemplateFromEnclosing<int> S2;
|
||||
// expected-error@#TFE2 {{redefinition of 'foo'}}
|
||||
// expected-note@-2{{in instantiation of template class}}
|
||||
// expected-note@#TFE1 {{previous definition is here}}
|
||||
|
||||
Redefinition<int> S3;
|
||||
// expected-error@#RD2 {{redefinition of 'foo'}}
|
||||
// expected-note@-2{{in instantiation of template class}}
|
||||
// expected-note@#RD1 {{previous definition is here}}
|
||||
}
|
||||
} // namespace SameScopeRedefs
|
||||
|
||||
namespace LibCXXOperatorRedef {
|
||||
template <typename T, typename U> struct is_same {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
template <typename T> struct is_same<T, T> {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
concept same_as = is_same<T, U>::value;
|
||||
|
||||
// An issue found from libcxx when trying to commit the deferred concepts patch.
|
||||
// This caused an error of 'redefinition of funcN'.
|
||||
template <class _Tp> struct __range_adaptor_closure {
|
||||
template <typename _View, typename _Closure>
|
||||
requires same_as<_Tp, _Closure>
|
||||
friend constexpr decltype(auto) R1func1(_View &&__view,
|
||||
_Closure &&__closure){};
|
||||
template <typename _View, typename _Closure>
|
||||
friend constexpr decltype(auto) R1func2(_View &&__view,
|
||||
_Closure &&__closure)
|
||||
requires same_as<_Tp, _Closure>
|
||||
{};
|
||||
template <same_as<_Tp> _View, typename _Closure>
|
||||
friend constexpr decltype(auto) R1func3(_View &&__view,
|
||||
_Closure &&__closure){};
|
||||
};
|
||||
|
||||
struct A : __range_adaptor_closure<A> {};
|
||||
struct B : __range_adaptor_closure<B> {};
|
||||
|
||||
// These three fail because after the 1st pass of instantiation, they are still
|
||||
// identical.
|
||||
template <class _Tp> struct __range_adaptor_closure2 {
|
||||
template <typename _View, typename _Closure>
|
||||
requires same_as<_View, _Closure>
|
||||
friend constexpr decltype(auto) R2func1(_View &&__view, // #FUNC1
|
||||
_Closure &&__closure){};
|
||||
template <typename _View, typename _Closure>
|
||||
friend constexpr decltype(auto) R2func2(_View &&__view, // #FUNC2
|
||||
_Closure &&__closure)
|
||||
requires same_as<_View, _Closure>
|
||||
{};
|
||||
template <typename _View, same_as<_View> _Closure>
|
||||
friend constexpr decltype(auto) R2func3(_View &&__view, // #FUNC3
|
||||
_Closure &&__closure){};
|
||||
};
|
||||
|
||||
struct A2 : __range_adaptor_closure2<A2> {};
|
||||
struct B2 : __range_adaptor_closure2<B2> {};
|
||||
// expected-error@#FUNC1{{redefinition of 'R2func1'}}
|
||||
// expected-note@-2{{in instantiation of template class}}
|
||||
// expected-note@#FUNC1{{previous definition is here}}
|
||||
// expected-error@#FUNC2{{redefinition of 'R2func2'}}
|
||||
// expected-note@#FUNC2{{previous definition is here}}
|
||||
// expected-error@#FUNC3{{redefinition of 'R2func3'}}
|
||||
// expected-note@#FUNC3{{previous definition is here}}
|
||||
|
||||
// These three are fine, they all depend on the parent template parameter, so
|
||||
// are different despite ::type not being valid.
|
||||
template <class _Tp> struct __range_adaptor_closure3 {
|
||||
template <typename _View, typename _Closure>
|
||||
requires same_as<typename _Tp::type, _Closure>
|
||||
friend constexpr decltype(auto) R3func1(_View &&__view,
|
||||
_Closure &&__closure){};
|
||||
template <typename _View, typename _Closure>
|
||||
friend constexpr decltype(auto) R3func2(_View &&__view,
|
||||
_Closure &&__closure)
|
||||
requires same_as<typename _Tp::type, _Closure>
|
||||
{};
|
||||
template <same_as<typename _Tp::type> _View, typename _Closure>
|
||||
friend constexpr decltype(auto) R3func3(_View &&__view,
|
||||
_Closure &&__closure){};
|
||||
};
|
||||
|
||||
struct A3 : __range_adaptor_closure3<A3> {};
|
||||
struct B3 : __range_adaptor_closure3<B3> {};
|
||||
|
||||
template <class _Tp> struct __range_adaptor_closure4 {
|
||||
template <typename _View, typename _Closure>
|
||||
requires same_as<_Tp, _View>
|
||||
// expected-note@+1{{previous definition is here}}
|
||||
void foo1(_View &&, _Closure &&) {}
|
||||
template <typename _View, typename _Closure>
|
||||
requires same_as<_Tp, _View>
|
||||
// expected-error@+1{{class member cannot be redeclared}}
|
||||
void foo1(_View &&, _Closure &&) {}
|
||||
|
||||
template <typename _View, typename _Closure>
|
||||
// expected-note@+1{{previous definition is here}}
|
||||
void foo2(_View &&, _Closure &&)
|
||||
requires same_as<_Tp, _View>
|
||||
{}
|
||||
template <typename _View, typename _Closure>
|
||||
// expected-error@+1{{class member cannot be redeclared}}
|
||||
void foo2(_View &&, _Closure &&)
|
||||
requires same_as<_Tp, _View>
|
||||
{}
|
||||
|
||||
template <same_as<_Tp> _View, typename _Closure>
|
||||
// expected-note@+1{{previous definition is here}}
|
||||
void foo3(_View &&, _Closure &&) {}
|
||||
template <same_as<_Tp> _View, typename _Closure>
|
||||
// expected-error@+1{{class member cannot be redeclared}}
|
||||
void foo3(_View &&, _Closure &&) {}
|
||||
};
|
||||
|
||||
// Requires instantiation to fail, so no errors here.
|
||||
template <class _Tp> struct __range_adaptor_closure5 {
|
||||
template <same_as<_Tp> U>
|
||||
friend void foo() {}
|
||||
template <same_as<_Tp> U>
|
||||
friend void foo() {}
|
||||
};
|
||||
|
||||
template <class _Tp> struct __range_adaptor_closure6 {
|
||||
template <same_as<_Tp> U>
|
||||
friend void foo() {} // #RAC6FOO1
|
||||
template <same_as<_Tp> U>
|
||||
friend void foo() {} // #RAC6FOO2
|
||||
};
|
||||
struct A6 : __range_adaptor_closure6<A6> {};
|
||||
// expected-error@#RAC6FOO2{{redefinition of 'foo'}}
|
||||
// expected-note@-2{{in instantiation of template class}}
|
||||
// expected-note@#RAC6FOO1{{previous definition is here}}
|
||||
|
||||
template <class T> struct S1 {
|
||||
template <typename U>
|
||||
friend void dupe() {} // #S1DUPE
|
||||
|
||||
template <typename U>
|
||||
requires same_as<U, U>
|
||||
friend void dupe2() {} // #S1DUPE2
|
||||
};
|
||||
template <class T> struct S2 {
|
||||
template <typename U>
|
||||
friend void dupe() {} // #S2DUPE
|
||||
|
||||
template <typename U>
|
||||
requires same_as<U, U>
|
||||
friend void dupe2() {} // #S2DUPE2
|
||||
};
|
||||
|
||||
template <class T> struct S3 {
|
||||
template <typename U>
|
||||
requires same_as<T, U>
|
||||
friend void dupe() {}
|
||||
};
|
||||
template <class T> struct S4 {
|
||||
template <typename U>
|
||||
requires same_as<T, U>
|
||||
friend void dupe() {}
|
||||
};
|
||||
|
||||
// Same as S3 and S4, but aren't instantiated with the same T.
|
||||
template <class T> struct S5 {
|
||||
template <typename U>
|
||||
requires same_as<T, U>
|
||||
friend void not_dupe() {}
|
||||
};
|
||||
template <class T> struct S6 {
|
||||
template <typename U>
|
||||
requires same_as<T, U>
|
||||
friend void not_dupe() {}
|
||||
};
|
||||
|
||||
template <class T> struct S7 {
|
||||
void not_dupe()
|
||||
requires same_as<T, T>
|
||||
{}
|
||||
};
|
||||
|
||||
void useS() {
|
||||
S1<int> s1;
|
||||
S2<double> s2;
|
||||
// expected-error@#S2DUPE{{redefinition}}
|
||||
// expected-note@-2{{in instantiation of template class}}
|
||||
// expected-note@#S1DUPE{{previous definition is here}}
|
||||
// expected-error@#S2DUPE2{{redefinition}}
|
||||
// expected-note@#S1DUPE2{{previous definition is here}}
|
||||
|
||||
// OK, they have different 'scopes'.
|
||||
S3<int> s3;
|
||||
S4<int> s4;
|
||||
|
||||
// OK, because only instantiated with different T.
|
||||
S5<int> s5;
|
||||
S6<double> s6;
|
||||
|
||||
S7<int> s7;
|
||||
}
|
||||
|
||||
} // namespace LibCXXOperatorRedef
|
||||
|
||||
namespace NamedDeclRefs {
|
||||
namespace my_std {
|
||||
template<typename T, typename U>
|
||||
concept Outer = true;
|
||||
template<typename T>
|
||||
using Inner = T;
|
||||
}
|
||||
template<typename T>
|
||||
struct Proxy {
|
||||
template<class U>
|
||||
friend constexpr void RefOuter()
|
||||
requires my_std::Outer<my_std::Inner<T>, my_std::Inner<U>>{}
|
||||
template<class U>
|
||||
friend constexpr void NoRefOuter() // #NOREFOUTER
|
||||
requires my_std::Outer<my_std::Inner<U>, my_std::Inner<U>>{}
|
||||
};
|
||||
void use() {
|
||||
Proxy<int> p;
|
||||
Proxy<float> p2;
|
||||
// expected-error@#NOREFOUTER {{redefinition of 'NoRefOuter'}}
|
||||
// expected-note@-2{{in instantiation of template class}}
|
||||
// expected-note@#NOREFOUTER{{previous definition is here}}
|
||||
}
|
||||
} // namespace NamedDeclRefs
|
@ -262,3 +262,449 @@ template<class, template <class> class> concept C = true;
|
||||
template <class> struct S {};
|
||||
void f(C<GH55567::S> auto);
|
||||
} // namespace GH55567
|
||||
|
||||
namespace SubConstraintChecks {
|
||||
template <typename T>
|
||||
concept TrueConstraint = true;
|
||||
template <typename T>
|
||||
concept FalseConstraint = false;
|
||||
|
||||
template <typename T, typename... Us>
|
||||
class ContainsConstrainedFuncTrue {
|
||||
public:
|
||||
template <typename V, TrueConstraint Constrained>
|
||||
static void func(V &&, Constrained &&C);
|
||||
};
|
||||
template <typename T, typename... Us>
|
||||
class ContainsConstrainedFuncFalse {
|
||||
public:
|
||||
template <typename V, FalseConstraint Constrained>
|
||||
static void func(V &&, Constrained &&C);
|
||||
};
|
||||
|
||||
template <typename... Us>
|
||||
concept TrueConstraint2 =
|
||||
requires(float &&t) {
|
||||
ContainsConstrainedFuncTrue<float, Us...>::func(5, 0.0);
|
||||
};
|
||||
template <typename... Us>
|
||||
concept FalseConstraint2 =
|
||||
requires(float &&t) {
|
||||
ContainsConstrainedFuncFalse<float, Us...>::func(5, 0.0); // #FC2_CONSTR
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void useTrue(int F)
|
||||
requires TrueConstraint2<int>
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
void useFalse(int F) // #USE_FALSE
|
||||
requires FalseConstraint2<int> // #USE_FALSE_CONSTR
|
||||
{}
|
||||
|
||||
// Should only diagnose 'false' once instantiated.
|
||||
void UseUse() {
|
||||
useTrue<int>(5);
|
||||
useFalse<int>(5);
|
||||
// expected-error@-1{{no matching function for call to 'useFalse'}}
|
||||
// expected-note@#USE_FALSE{{constraints not satisfied}}
|
||||
// expected-note@#USE_FALSE_CONSTR{{because 'int' does not satisfy 'FalseConstraint2'}}
|
||||
// expected-note@#FC2_CONSTR {{would be invalid: no matching function for call to 'func'}}
|
||||
}
|
||||
} // namespace SubConstraintChecks
|
||||
|
||||
namespace DeducedTemplateArgs {
|
||||
template <typename Itr> struct ItrTraits {
|
||||
template <typename PtrItr> struct Ptr {
|
||||
};
|
||||
template <typename PtrItr>
|
||||
requires requires { typename PtrItr::pointer; }
|
||||
struct Ptr<PtrItr> {
|
||||
using type = typename Itr::pointer;
|
||||
};
|
||||
using pointer = typename Ptr<Itr>::type; // #TRAITS_PTR
|
||||
};
|
||||
|
||||
struct complete_itr {
|
||||
using pointer = int;
|
||||
};
|
||||
|
||||
template <typename T> class Complete {
|
||||
using ItrType = ItrTraits<complete_itr>;
|
||||
ItrType begin() noexcept { return ItrType(); }
|
||||
};
|
||||
|
||||
// This version doesn't have 'pointer', so error confirms we are in the first
|
||||
// verison of 'Ptr'.
|
||||
struct not_complete_itr {
|
||||
};
|
||||
|
||||
template <typename T> class NotComplete {
|
||||
using ItrType = ItrTraits<not_complete_itr>;
|
||||
ItrType begin() noexcept { return ItrType(); }
|
||||
// expected-error@#TRAITS_PTR{{no type named 'type' in }}
|
||||
// expected-note@-2{{in instantiation of template class }}
|
||||
};
|
||||
} // namespace DeducedTemplateArgs
|
||||
|
||||
namespace DeferredInstantiationInstScope {
|
||||
template <typename T>
|
||||
struct remove_ref {
|
||||
using type = T;
|
||||
};
|
||||
template <typename T>
|
||||
struct remove_ref<T &> {
|
||||
using type = T;
|
||||
};
|
||||
template <typename T>
|
||||
struct remove_ref<T &&> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsInt = PR54443::is_same<typename remove_ref<T>::type,
|
||||
int>::value;
|
||||
|
||||
template <typename U>
|
||||
void SingleDepthReferencesTop(U &&u) {
|
||||
struct lc {
|
||||
void operator()() // #SDRT_OP
|
||||
requires IsInt<decltype(u)> // #SDRT_REQ
|
||||
{}
|
||||
};
|
||||
lc lv;
|
||||
lv(); // #SDRT_CALL
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void SingleDepthReferencesTopNotCalled(U &&u) {
|
||||
struct lc {
|
||||
void operator()()
|
||||
requires IsInt<typename decltype(u)::FOO>
|
||||
{}
|
||||
};
|
||||
lc lv;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void SingleDepthReferencesTopCalled(U &&u) {
|
||||
struct lc {
|
||||
void operator()() // #CALLOP
|
||||
requires IsInt<typename decltype(u)::FOO> // #CONSTR
|
||||
{}
|
||||
};
|
||||
lc lv;
|
||||
lv();
|
||||
// expected-error@-1{{no matching function for call to object of type 'lc'}}
|
||||
// expected-note@#SDRTC{{in instantiation of function template}}
|
||||
// expected-note@#CALLOP{{constraints not satisfied}}
|
||||
// expected-note@#CONSTR{{substituted constraint expression is ill-formed}}
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void SingleDepthReferencesTopLambda(U &&u) {
|
||||
[]()
|
||||
requires IsInt<decltype(u)>
|
||||
{}();
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void DoubleDepthReferencesTop(U &&u) {
|
||||
struct lc { // #DDRT_STRCT
|
||||
void operator()() {
|
||||
struct lc2 {
|
||||
void operator()() // #DDRT_OP
|
||||
requires IsInt<decltype(u)> // #DDRT_REQ
|
||||
{}
|
||||
};
|
||||
lc2 lv2;
|
||||
lv2(); // #DDRT_CALL
|
||||
}
|
||||
};
|
||||
lc lv;
|
||||
lv();
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void DoubleDepthReferencesTopLambda(U &&u) {
|
||||
[]() { []()
|
||||
requires IsInt<decltype(u)>
|
||||
{}(); }();
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void DoubleDepthReferencesAll(U &&u) {
|
||||
struct lc { // #DDRA_STRCT
|
||||
void operator()(U &&u2) {
|
||||
struct lc2 {
|
||||
void operator()(U &&u3) // #DDRA_OP
|
||||
requires IsInt<decltype(u)> && // #DDRA_REQ
|
||||
IsInt<decltype(u2)> && IsInt<decltype(u3)>
|
||||
{}
|
||||
};
|
||||
lc2 lv2;
|
||||
lv2(u2); // #DDRA_CALL
|
||||
}
|
||||
};
|
||||
lc lv;
|
||||
lv(u);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void DoubleDepthReferencesAllLambda(U &&u) {
|
||||
[](U &&u2) {
|
||||
[](U && u3)
|
||||
requires IsInt<decltype(u)> &&
|
||||
IsInt<decltype(u2)> && IsInt<decltype(u3)>
|
||||
{}(u2);
|
||||
}(u);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
struct CausesFriendConstraint {
|
||||
template <typename V>
|
||||
friend void FriendFunc(CausesFriendConstraint, V) // #FF_DECL
|
||||
requires IsInt<U> &&
|
||||
IsInt<V> // #FF_REQ
|
||||
{}
|
||||
};
|
||||
// FIXME: Re-enable this test when constraints are allowed to refer to captures.
|
||||
// template<typename T>
|
||||
// void ChecksCapture(T x) {
|
||||
// [y = x]() requires(IsInt<decltype(y)>){}();
|
||||
// }
|
||||
|
||||
template <typename T>
|
||||
void ChecksLocalVar(T x) {
|
||||
T Local;
|
||||
[]()
|
||||
requires(IsInt<decltype(Local)>)
|
||||
{}();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LocalStructMemberVar(T x) {
|
||||
struct S {
|
||||
T local;
|
||||
void foo()
|
||||
requires(IsInt<decltype(local)>) // #LSMV_REQ
|
||||
{}
|
||||
} s;
|
||||
s.foo(); // #LSMV_CALL
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct ChecksMemberVar {
|
||||
T t;
|
||||
void foo()
|
||||
requires(IsInt<decltype(t)>) // #CMV_FOO
|
||||
{}
|
||||
template <typename U>
|
||||
void foo2() // #CMV_FOO2
|
||||
requires(IsInt<decltype(t)>) // #CMV_FOO2_REQ
|
||||
{}
|
||||
};
|
||||
|
||||
void test_dependent() {
|
||||
int v = 0;
|
||||
float will_fail;
|
||||
SingleDepthReferencesTop(v);
|
||||
SingleDepthReferencesTop(will_fail);
|
||||
// expected-error@#SDRT_CALL{{no matching function for call to object of type 'lc'}}
|
||||
// expected-note@-2{{in instantiation of function template specialization}}
|
||||
// expected-note@#SDRT_OP{{candidate function not viable}}
|
||||
// expected-note@#SDRT_REQ{{'IsInt<decltype(u)>' evaluated to false}}
|
||||
|
||||
SingleDepthReferencesTopNotCalled(v);
|
||||
// Won't error unless we try to call it.
|
||||
SingleDepthReferencesTopNotCalled(will_fail);
|
||||
SingleDepthReferencesTopCalled(v); // #SDRTC
|
||||
SingleDepthReferencesTopLambda(v);
|
||||
// FIXME: This should error on constraint failure! (Lambda!)
|
||||
SingleDepthReferencesTopLambda(will_fail);
|
||||
DoubleDepthReferencesTop(v);
|
||||
DoubleDepthReferencesTop(will_fail);
|
||||
// expected-error@#DDRT_CALL{{no matching function for call to object of type 'lc2'}}
|
||||
// expected-note@-2{{in instantiation of function template specialization}}
|
||||
// expected-note@#DDRT_STRCT{{in instantiation of member function}}
|
||||
// expected-note@#DDRT_OP{{candidate function not viable}}
|
||||
// expected-note@#DDRT_REQ{{'IsInt<decltype(u)>' evaluated to false}}
|
||||
|
||||
DoubleDepthReferencesTopLambda(v);
|
||||
// FIXME: This should error on constraint failure! (Lambda!)
|
||||
DoubleDepthReferencesTopLambda(will_fail);
|
||||
DoubleDepthReferencesAll(v);
|
||||
DoubleDepthReferencesAll(will_fail);
|
||||
// expected-error@#DDRA_CALL{{no matching function for call to object of type 'lc2'}}
|
||||
// expected-note@-2{{in instantiation of function template specialization}}
|
||||
// expected-note@#DDRA_STRCT{{in instantiation of member function}}
|
||||
// expected-note@#DDRA_OP{{candidate function not viable}}
|
||||
// expected-note@#DDRA_REQ{{'IsInt<decltype(u)>' evaluated to false}}
|
||||
|
||||
DoubleDepthReferencesAllLambda(v);
|
||||
// FIXME: This should error on constraint failure! (Lambda!)
|
||||
DoubleDepthReferencesAllLambda(will_fail);
|
||||
|
||||
CausesFriendConstraint<int> CFC;
|
||||
FriendFunc(CFC, 1);
|
||||
FriendFunc(CFC, 1.0);
|
||||
// expected-error@-1{{no matching function for call to 'FriendFunc'}}
|
||||
// expected-note@#FF_DECL{{constraints not satisfied}}
|
||||
// expected-note@#FF_REQ{{because 'IsInt<double>' evaluated to false}}
|
||||
|
||||
// FIXME: Re-enable this test when constraints are allowed to refer to captures.
|
||||
// ChecksCapture(v);
|
||||
|
||||
ChecksLocalVar(v);
|
||||
// FIXME: This should error on constraint failure! (Lambda!)
|
||||
ChecksLocalVar(will_fail);
|
||||
|
||||
LocalStructMemberVar(v);
|
||||
LocalStructMemberVar(will_fail);
|
||||
// expected-error@#LSMV_CALL{{invalid reference to function 'foo'}}
|
||||
// expected-note@-2{{in instantiation of function template specialization}}
|
||||
// expected-note@#LSMV_REQ{{because 'IsInt<decltype(this->local)>' evaluated to false}}
|
||||
|
||||
ChecksMemberVar<int> CMV;
|
||||
CMV.foo();
|
||||
CMV.foo2<int>();
|
||||
|
||||
ChecksMemberVar<float> CMV2;
|
||||
CMV2.foo();
|
||||
// expected-error@-1{{invalid reference to function 'foo'}}
|
||||
// expected-note@#CMV_FOO{{because 'IsInt<decltype(this->t)>' evaluated to false}}
|
||||
CMV2.foo2<float>();
|
||||
// expected-error@-1{{no matching member function for call to 'foo2'}}
|
||||
// expected-note@#CMV_FOO2{{constraints not satisfied}}
|
||||
// expected-note@#CMV_FOO2_REQ{{because 'IsInt<decltype(this->t)>' evaluated to false}}
|
||||
}
|
||||
} // namespace DeferredInstantiationInstScope
|
||||
|
||||
// Ane example of evaluating a concept at two different depths in the same
|
||||
// evaluation. No diagnostic is expected.
|
||||
namespace SameConceptDifferentDepth {
|
||||
template <class _Ip>
|
||||
concept sentinel_for =
|
||||
requires(_Ip __i) {
|
||||
__i++;
|
||||
};
|
||||
|
||||
template <class _Ip>
|
||||
concept bidirectional_iterator =
|
||||
sentinel_for<_Ip>;
|
||||
|
||||
template <class _Iter>
|
||||
class move_iterator {
|
||||
public:
|
||||
auto operator++(int)
|
||||
requires sentinel_for<_Iter>{}
|
||||
};
|
||||
|
||||
static_assert(bidirectional_iterator<move_iterator<int>>);
|
||||
} // namespace SameConceptDifferentDepth
|
||||
|
||||
namespace VarInit {
|
||||
template <class _Tp>
|
||||
concept __can_reference = true;
|
||||
|
||||
template <class _Iter>
|
||||
class common_iterator {
|
||||
public:
|
||||
common_iterator() {
|
||||
constexpr auto x = requires(_Iter & __i) { { __i } -> __can_reference; };
|
||||
}
|
||||
};
|
||||
|
||||
void test() {
|
||||
auto commonIter1 = common_iterator<int>();
|
||||
}
|
||||
} // namespace VarInit
|
||||
|
||||
|
||||
namespace InlineFriendOperator {
|
||||
template <typename T>
|
||||
concept C = true;
|
||||
template <class _Iter>
|
||||
class counted_iterator {
|
||||
_Iter I;
|
||||
public:
|
||||
constexpr counted_iterator() = default;
|
||||
friend constexpr auto operator+( // expected-note {{candidate function not viable}}
|
||||
int __n, const counted_iterator &__x)
|
||||
requires C<decltype(I)>
|
||||
{
|
||||
return __x + __n; // expected-error{{invalid operands to binary expression}}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
counted_iterator<int> iter;
|
||||
auto x = 2 + iter; // expected-note{{in instantiation of member function 'InlineFriendOperator::operator+'}}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace InlineFriendOperator
|
||||
|
||||
namespace ClassTemplateInstantiation {
|
||||
struct Type;
|
||||
template < typename A, typename B, typename C>
|
||||
concept ConstraintF = false; // #ConstraintF
|
||||
template < typename A, typename B, typename C>
|
||||
concept ConstraintT = true;
|
||||
|
||||
template < typename T > struct Parent {
|
||||
template < typename U, ConstraintT<T, U> > struct ChildT{};
|
||||
ChildT<Type, Type> CauseInstT;
|
||||
template < typename U, ConstraintF<T, U> > struct ChildF{};// #ChildF
|
||||
ChildF<Type, Type> CauseInstF; //#CauseInstF
|
||||
};
|
||||
|
||||
// expected-error@#CauseInstF{{constraints not satisfied for class template}}
|
||||
// expected-note@+3{{in instantiation of template class}}
|
||||
// expected-note@#ChildF{{evaluated to false}}
|
||||
// expected-note@#ConstraintF{{because 'false' evaluated to false}}
|
||||
Parent<int> Inst;
|
||||
} // namespace ClassTemplateInstantiation
|
||||
|
||||
namespace SelfFriend {
|
||||
template<class T>
|
||||
concept Constraint = requires (T i) { (*i); };
|
||||
template<class T>
|
||||
concept Constraint2 = requires (T i) { (*i); };
|
||||
|
||||
template<Constraint T>
|
||||
struct Iterator {
|
||||
template <Constraint>
|
||||
friend class Iterator;
|
||||
void operator*();
|
||||
};
|
||||
|
||||
template<Constraint T> // #ITER_BAD
|
||||
struct IteratorBad {
|
||||
template <Constraint2>//#ITER_BAD_FRIEND
|
||||
friend class IteratorBad;
|
||||
void operator*();
|
||||
};
|
||||
|
||||
Iterator<int*> I;
|
||||
Iterator<char*> I2;
|
||||
IteratorBad<int*> I3; // expected-error@#ITER_BAD_FRIEND{{constraint differs}}
|
||||
// expected-note@-1{{in instantiation of template class}}
|
||||
// expected-note@#ITER_BAD{{previous template declaration}}
|
||||
} // namespace SelfFriend
|
||||
|
||||
|
||||
namespace ConstrainedMemberVarTemplate {
|
||||
template <long Size> struct Container {
|
||||
static constexpr long arity = Size;
|
||||
template <typename U>
|
||||
requires(sizeof(U) == arity) // #CMVT_REQ
|
||||
using var_templ = int;
|
||||
};
|
||||
Container<4>::var_templ<int> inst;
|
||||
Container<5>::var_templ<int> inst_fail;
|
||||
// expected-error@-1{{constraints not satisfied for alias template 'var_templ'}}
|
||||
// expected-note@#CMVT_REQ{{because 'sizeof(int) == arity' (4 == 5) evaluated to false}}
|
||||
} // namespace ConstrainedMemberVarTemplate
|
||||
|
||||
|
23
clang/test/SemaTemplate/deferred-concept-inst.cpp
Normal file
23
clang/test/SemaTemplate/deferred-concept-inst.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify -fsyntax-only -Wno-unused-value
|
||||
// expected-no-diagnostics
|
||||
|
||||
namespace GithubBug44178 {
|
||||
template <typename D>
|
||||
struct CRTP {
|
||||
void call_foo()
|
||||
requires requires(D &v) { v.foo(); }
|
||||
{
|
||||
static_cast<D *>(this)->foo();
|
||||
}
|
||||
};
|
||||
|
||||
struct Test : public CRTP<Test> {
|
||||
void foo() {}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Test t;
|
||||
t.call_foo();
|
||||
return 0;
|
||||
}
|
||||
} // namespace GithubBug44178
|
@ -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>
|
||||
|
62
clang/test/SemaTemplate/trailing-return-short-circuit.cpp
Normal file
62
clang/test/SemaTemplate/trailing-return-short-circuit.cpp
Normal 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}}
|
||||
}
|
Loading…
Reference in New Issue
Block a user