[clang] 2f20743 - Deferred Concept Instantiation Implementation
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 30 06:47:51 PDT 2022
Author: Erich Keane
Date: 2022-06-30T06:47:11-07:00
New Revision: 2f207439521d62d9551b2884158368e8b34084e5
URL: https://github.com/llvm/llvm-project/commit/2f207439521d62d9551b2884158368e8b34084e5
DIFF: https://github.com/llvm/llvm-project/commit/2f207439521d62d9551b2884158368e8b34084e5.diff
LOG: Deferred Concept Instantiation Implementation
This is a continuation of D119544. Based on @rsmith 's feed back
showing me https://eel.is/c++draft/temp#friend-9, We should properly
handle friend functions now.
Differential Revision: https://reviews.llvm.org/D126907
Added:
clang/test/SemaTemplate/concepts-friends.cpp
clang/test/SemaTemplate/deferred-concept-inst.cpp
clang/test/SemaTemplate/trailing-return-short-circuit.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/Decl.h
clang/include/clang/AST/DeclBase.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Sema/Template.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Decl.cpp
clang/lib/AST/DeclBase.cpp
clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Sema/TreeTransform.h
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp
clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp
clang/test/SemaTemplate/concepts.cpp
clang/test/SemaTemplate/instantiate-requires-clause.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e6353de934153..5585467769b03 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -451,10 +451,11 @@ C++20 Feature Support
- No longer attempt to evaluate a consteval UDL function call at runtime when
it is called through a template instantiation. This fixes
`Issue 54578 <https://github.com/llvm/llvm-project/issues/54578>`_.
-
-- Implemented ``__builtin_source_location()``, which enables library support
- for ``std::source_location``.
-
+- Implemented `__builtin_source_location()` which enables library support for std::source_location.
+- Clang now correctly delays the instantiation of function constraints until
+ the time of checking, which should now allow the libstdc++ ranges implementation
+ to work for at least trivial examples. This fixes
+ `Issue 44178 <https://github.com/llvm/llvm-project/issues/44178>`_.
- The mangling scheme for C++20 modules has incompatibly changed. The
initial mangling was discovered not to be reversible, and the weak
ownership design decision did not give the backwards compatibility
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 66fab94b45b8a..0a7dc63d1c25d 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1890,7 +1890,9 @@ class FunctionDecl : public DeclaratorDecl,
TK_FunctionTemplateSpecialization,
// A function template specialization that hasn't yet been resolved to a
// particular specialized function template.
- TK_DependentFunctionTemplateSpecialization
+ TK_DependentFunctionTemplateSpecialization,
+ // A non templated function which is in a dependent scope.
+ TK_DependentNonTemplate
};
/// Stashed information about a defaulted function definition whose body has
@@ -1939,20 +1941,21 @@ class FunctionDecl : public DeclaratorDecl,
/// The template or declaration that this declaration
/// describes or was instantiated from, respectively.
///
- /// For non-templates, this value will be NULL. For function
- /// declarations that describe a function template, this will be a
- /// pointer to a FunctionTemplateDecl. For member functions
- /// of class template specializations, this will be a MemberSpecializationInfo
- /// pointer containing information about the specialization.
- /// For function template specializations, this will be a
- /// FunctionTemplateSpecializationInfo, which contains information about
- /// the template being specialized and the template arguments involved in
- /// that specialization.
- llvm::PointerUnion<FunctionTemplateDecl *,
- MemberSpecializationInfo *,
+ /// For non-templates this value will be NULL, unless this non-template
+ /// function declaration was declared directly inside of a function template,
+ /// in which case this will have a pointer to a FunctionDecl, stored in the
+ /// NamedDecl. For function declarations that describe a function template,
+ /// this will be a pointer to a FunctionTemplateDecl, stored in the NamedDecl.
+ /// For member functions of class template specializations, this will be a
+ /// MemberSpecializationInfo pointer containing information about the
+ /// specialization. For function template specializations, this will be a
+ /// FunctionTemplateSpecializationInfo, which contains information about the
+ /// template being specialized and the template arguments involved in that
+ /// specialization.
+ llvm::PointerUnion<NamedDecl *, MemberSpecializationInfo *,
FunctionTemplateSpecializationInfo *,
DependentFunctionTemplateSpecializationInfo *>
- TemplateOrSpecialization;
+ TemplateOrSpecialization;
/// Provides source/type location info for the declaration name embedded in
/// the DeclaratorDecl base class.
@@ -2695,6 +2698,11 @@ class FunctionDecl : public DeclaratorDecl,
setInstantiationOfMemberFunction(getASTContext(), FD, TSK);
}
+ /// Specify that this function declaration was instantiated from FunctionDecl
+ /// FD. This is only used if this is a function declaration declared locally
+ /// inside of a function template.
+ void setInstantiatedFromDecl(FunctionDecl *FD);
+
/// Retrieves the function template that is described by this
/// function declaration.
///
@@ -2709,6 +2717,8 @@ class FunctionDecl : public DeclaratorDecl,
/// FunctionTemplateDecl from a FunctionDecl.
FunctionTemplateDecl *getDescribedFunctionTemplate() const;
+ FunctionDecl *getInstantiatedFromDecl() const;
+
void setDescribedFunctionTemplate(FunctionTemplateDecl *Template);
/// Determine whether this function is a function template
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 52fe8dd6b1e57..3a5cb5cb7b723 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -920,10 +920,10 @@ class alignas(8) Decl {
/// If this decl is defined inside a function/method/block it returns
/// the corresponding DeclContext, otherwise it returns null.
- const DeclContext *getParentFunctionOrMethod() const;
- DeclContext *getParentFunctionOrMethod() {
- return const_cast<DeclContext*>(
- const_cast<const Decl*>(this)->getParentFunctionOrMethod());
+ const DeclContext *getParentFunctionOrMethod(bool Lexical = false) const;
+ DeclContext *getParentFunctionOrMethod(bool Lexical = false) {
+ return const_cast<DeclContext *>(
+ const_cast<const Decl *>(this)->getParentFunctionOrMethod(Lexical));
}
/// Retrieves the "canonical" declaration of the given declaration.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 07f3d2c654c1b..1c7e7cb2e734c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3622,6 +3622,11 @@ class Sema final {
bool ConsiderCudaAttrs = true,
bool ConsiderRequiresClauses = true);
+ // Calculates whether the expression Constraint depends on an enclosing
+ // template, for the purposes of [temp.friend] p9.
+ bool ConstraintExpressionDependsOnEnclosingTemplate(unsigned TemplateDepth,
+ const Expr *Constraint);
+
enum class AllowedExplicit {
/// Allow no explicit functions to be used.
None,
@@ -7091,6 +7096,21 @@ class Sema final {
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(
@@ -7121,8 +7141,10 @@ class Sema final {
/// check (either a concept or a constrained entity).
/// \param ConstraintExprs a list of constraint expressions, treated as if
/// they were 'AND'ed together.
- /// \param TemplateArgs the list of template arguments to substitute into the
- /// constraint expression.
+ /// \param TemplateArgList the multi-level list of template arguments to
+ /// substitute into the constraint expression. This should be relative to the
+ /// top-level (hence multi-level), since we need to instantiate fully at the
+ /// time of checking.
/// \param TemplateIDRange The source range of the template id that
/// caused the constraints check.
/// \param Satisfaction if true is returned, will contain details of the
@@ -7132,7 +7154,40 @@ class Sema final {
/// false otherwise.
bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs,
+ const MultiLevelTemplateArgumentList &TemplateArgList,
+ SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
+ llvm::SmallVector<Expr *, 4> Converted;
+ return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted,
+ TemplateArgList, TemplateIDRange,
+ Satisfaction);
+ }
+
+ /// \brief Check whether the given list of constraint expressions are
+ /// satisfied (as if in a 'conjunction') given template arguments.
+ /// Additionally, takes an empty list of Expressions which is populated with
+ /// the instantiated versions of the ConstraintExprs.
+ /// \param Template the template-like entity that triggered the constraints
+ /// check (either a concept or a constrained entity).
+ /// \param ConstraintExprs a list of constraint expressions, treated as if
+ /// they were 'AND'ed together.
+ /// \param ConvertedConstraints a out parameter that will get populated with
+ /// the instantiated version of the ConstraintExprs if we successfully checked
+ /// satisfaction.
+ /// \param TemplateArgList the multi-level list of template arguments to
+ /// substitute into the constraint expression. This should be relative to the
+ /// top-level (hence multi-level), since we need to instantiate fully at the
+ /// time of checking.
+ /// \param TemplateIDRange The source range of the template id that
+ /// caused the constraints check.
+ /// \param Satisfaction if true is returned, will contain details of the
+ /// satisfaction, with enough information to diagnose an unsatisfied
+ /// expression.
+ /// \returns true if an error occurred and satisfaction could not be checked,
+ /// false otherwise.
+ bool CheckConstraintSatisfaction(
+ const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
+ llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
+ const MultiLevelTemplateArgumentList &TemplateArgList,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
/// \brief Check whether the given non-dependent constraint expression is
@@ -7168,9 +7223,9 @@ class Sema final {
///
/// \returns true if the constrains are not satisfied or could not be checked
/// for satisfaction, false if the constraints are satisfied.
- bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template,
- ArrayRef<TemplateArgument> TemplateArgs,
- SourceRange TemplateIDRange);
+ bool EnsureTemplateArgumentListConstraints(
+ TemplateDecl *Template, MultiLevelTemplateArgumentList TemplateArgs,
+ SourceRange TemplateIDRange);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
@@ -8904,7 +8959,8 @@ class Sema final {
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
@@ -9637,6 +9693,11 @@ class Sema final {
ExtParameterInfoBuilder &ParamInfos);
ExprResult SubstExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);
+ // Unlike the above, this evaluates constraints, which should only happen at
+ // 'constraint checking' time.
+ ExprResult
+ SubstConstraintExpr(Expr *E,
+ const MultiLevelTemplateArgumentList &TemplateArgs);
/// Substitute the given template arguments into a list of
/// expressions, expanding pack expansions if required.
@@ -9660,13 +9721,14 @@ class Sema final {
TemplateParameterList *
SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
- const MultiLevelTemplateArgumentList &TemplateArgs);
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ bool InstantiateConstraints = false);
bool
SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
const MultiLevelTemplateArgumentList &TemplateArgs,
- TemplateArgumentListInfo &Outputs);
-
+ TemplateArgumentListInfo &Outputs,
+ bool InstantiateConstraints = false);
Decl *SubstDecl(Decl *D, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs);
@@ -9758,7 +9820,8 @@ class Sema final {
const MultiLevelTemplateArgumentList &TemplateArgs);
bool SubstTypeConstraint(TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
- const MultiLevelTemplateArgumentList &TemplateArgs);
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ bool isEvaluatingAConstraint);
bool InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
ParmVarDecl *Param);
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 5dcde77b5dd37..602faf8607412 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -75,6 +75,8 @@ enum class TemplateSubstitutionKind : char {
class MultiLevelTemplateArgumentList {
/// The template argument list at a certain template depth
using ArgList = ArrayRef<TemplateArgument>;
+ using ArgListsIterator = SmallVector<ArgList, 4>::iterator;
+ using ConstArgListsIterator = SmallVector<ArgList, 4>::const_iterator;
/// The template argument lists, stored from the innermost template
/// argument list (first) to the outermost template argument list (last).
@@ -121,6 +123,12 @@ enum class TemplateSubstitutionKind : char {
return TemplateArgumentLists.size();
}
+ /// Determine the number of substituted args at 'Depth'.
+ unsigned getNumSubstitutedArgs(unsigned Depth) const {
+ assert(NumRetainedOuterLevels <= Depth && Depth < getNumLevels());
+ return TemplateArgumentLists[getNumLevels() - Depth - 1].size();
+ }
+
unsigned getNumRetainedOuterLevels() const {
return NumRetainedOuterLevels;
}
@@ -158,6 +166,14 @@ enum class TemplateSubstitutionKind : char {
return !(*this)(Depth, Index).isNull();
}
+ bool isAnyArgInstantiationDependent() const {
+ for (ArgList List : TemplateArgumentLists)
+ for (const TemplateArgument &TA : List)
+ if (TA.isInstantiationDependent())
+ return true;
+ return false;
+ }
+
/// Clear out a specific template argument.
void setArgument(unsigned Depth, unsigned Index,
TemplateArgument Arg) {
@@ -183,6 +199,14 @@ enum class TemplateSubstitutionKind : char {
TemplateArgumentLists.push_back(Args);
}
+ /// Replaces the current 'innermost' level with the provided argument list.
+ /// This is useful for type deduction cases where we need to get the entire
+ /// list from the AST, but then add the deduced innermost list.
+ void replaceInnermostTemplateArguments(ArgList Args) {
+ assert(TemplateArgumentLists.size() > 0 && "Replacing in an empty list?");
+ TemplateArgumentLists[0] = Args;
+ }
+
/// Add an outermost level that we are not substituting. We have no
/// arguments at this level, and do not remove it from the depth of inner
/// template parameters that we instantiate.
@@ -197,6 +221,16 @@ enum class TemplateSubstitutionKind : char {
const ArgList &getInnermost() const {
return TemplateArgumentLists.front();
}
+
+ /// Retrieve the outermost template argument list.
+ const ArgList &getOutermost() const { return TemplateArgumentLists.back(); }
+
+ ArgListsIterator begin() { return TemplateArgumentLists.begin(); }
+ ConstArgListsIterator begin() const {
+ return TemplateArgumentLists.begin();
+ }
+ ArgListsIterator end() { return TemplateArgumentLists.end(); }
+ ConstArgListsIterator end() const { return TemplateArgumentLists.end(); }
};
/// The context in which partial ordering of function templates occurs.
@@ -469,6 +503,7 @@ enum class TemplateSubstitutionKind : char {
const MultiLevelTemplateArgumentList &TemplateArgs;
Sema::LateInstantiatedAttrVec* LateAttrs = nullptr;
LocalInstantiationScope *StartingScope = nullptr;
+ bool EvaluatingAConstraint = false;
/// A list of out-of-line class template partial
/// specializations that will need to be instantiated after the
@@ -487,10 +522,12 @@ enum class TemplateSubstitutionKind : char {
public:
TemplateDeclInstantiator(Sema &SemaRef, DeclContext *Owner,
- const MultiLevelTemplateArgumentList &TemplateArgs)
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ bool EvaluatingConstraint = false)
: SemaRef(SemaRef),
SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex),
- Owner(Owner), TemplateArgs(TemplateArgs) {}
+ Owner(Owner), TemplateArgs(TemplateArgs),
+ EvaluatingAConstraint(EvaluatingConstraint) {}
// Define all the decl visitors using DeclNodes.inc
#define DECL(DERIVED, BASE) \
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 8d930ead97d3e..7a20807aa1f90 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -3109,6 +3109,11 @@ Error ASTNodeImporter::ImportTemplateInformation(
case FunctionDecl::TK_FunctionTemplate:
return Error::success();
+ case FunctionDecl::TK_DependentNonTemplate:
+ if (Expected<FunctionDecl *> InstFDOrErr =
+ import(FromFD->getInstantiatedFromDecl()))
+ ToFD->setInstantiatedFromDecl(*InstFDOrErr);
+ return Error::success();
case FunctionDecl::TK_MemberSpecialization: {
TemplateSpecializationKind TSK = FromFD->getTemplateSpecializationKind();
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 5e5101203e6cf..3bf3bd66ff326 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3732,8 +3732,12 @@ const IdentifierInfo *FunctionDecl::getLiteralIdentifier() const {
FunctionDecl::TemplatedKind FunctionDecl::getTemplatedKind() const {
if (TemplateOrSpecialization.isNull())
return TK_NonTemplate;
- if (TemplateOrSpecialization.is<FunctionTemplateDecl *>())
+ if (auto *ND = TemplateOrSpecialization.dyn_cast<NamedDecl *>()) {
+ if (isa<FunctionDecl>(ND))
+ return TK_DependentNonTemplate;
+ assert(isa<FunctionTemplateDecl>(ND) && "No other types it could be?");
return TK_FunctionTemplate;
+ }
if (TemplateOrSpecialization.is<MemberSpecializationInfo *>())
return TK_MemberSpecialization;
if (TemplateOrSpecialization.is<FunctionTemplateSpecializationInfo *>())
@@ -3774,15 +3778,28 @@ FunctionDecl::setInstantiationOfMemberFunction(ASTContext &C,
}
FunctionTemplateDecl *FunctionDecl::getDescribedFunctionTemplate() const {
- return TemplateOrSpecialization.dyn_cast<FunctionTemplateDecl *>();
+ return dyn_cast_or_null<FunctionTemplateDecl>(
+ TemplateOrSpecialization.dyn_cast<NamedDecl *>());
}
-void FunctionDecl::setDescribedFunctionTemplate(FunctionTemplateDecl *Template) {
+void FunctionDecl::setDescribedFunctionTemplate(
+ FunctionTemplateDecl *Template) {
assert(TemplateOrSpecialization.isNull() &&
"Member function is already a specialization");
TemplateOrSpecialization = Template;
}
+void FunctionDecl::setInstantiatedFromDecl(FunctionDecl *FD) {
+ assert(TemplateOrSpecialization.isNull() &&
+ "function is already a specialization");
+ TemplateOrSpecialization = FD;
+}
+
+FunctionDecl *FunctionDecl::getInstantiatedFromDecl() const {
+ return dyn_cast_or_null<FunctionDecl>(
+ TemplateOrSpecialization.dyn_cast<NamedDecl *>());
+}
+
bool FunctionDecl::isImplicitlyInstantiable() const {
// If the function is invalid, it can't be implicitly instantiated.
if (isInvalidDecl())
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 13dd6da3f24fe..9c1deaedf6b07 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -283,8 +283,9 @@ unsigned Decl::getTemplateDepth() const {
return cast<Decl>(DC)->getTemplateDepth();
}
-const DeclContext *Decl::getParentFunctionOrMethod() const {
- for (const DeclContext *DC = getDeclContext();
+const DeclContext *Decl::getParentFunctionOrMethod(bool Lexical) const {
+ for (const DeclContext *DC = Lexical ? getLexicalDeclContext()
+ : getDeclContext();
DC && !DC->isTranslationUnit() && !DC->isNamespace();
DC = DC->getParent())
if (DC->isFunctionOrMethod())
diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index bffa66c2d9448..1a785182e3634 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -303,6 +303,7 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
// Skip templated functions.
switch (Decl->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
+ case FunctionDecl::TK_DependentNonTemplate:
break;
case FunctionDecl::TK_MemberSpecialization:
case FunctionDecl::TK_FunctionTemplateSpecialization:
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 239e5dc4394c3..9edba27bea5ea 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -30,6 +30,7 @@ using namespace sema;
namespace {
class LogicalBinOp {
+ SourceLocation Loc;
OverloadedOperatorKind Op = OO_None;
const Expr *LHS = nullptr;
const Expr *RHS = nullptr;
@@ -40,12 +41,14 @@ class LogicalBinOp {
Op = BinaryOperator::getOverloadedOperator(BO->getOpcode());
LHS = BO->getLHS();
RHS = BO->getRHS();
+ Loc = BO->getExprLoc();
} else if (auto *OO = dyn_cast<CXXOperatorCallExpr>(E)) {
// If OO is not || or && it might not have exactly 2 arguments.
if (OO->getNumArgs() == 2) {
Op = OO->getOperator();
LHS = OO->getArg(0);
RHS = OO->getArg(1);
+ Loc = OO->getOperatorLoc();
}
}
}
@@ -56,6 +59,26 @@ class LogicalBinOp {
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 +145,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 +167,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 +176,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 +198,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 +219,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
<< SubstitutedAtomicExpr.get()->getSourceRange();
for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
S.Diag(PDiag.first, PDiag.second);
- return true;
+ return ExprError();
}
assert(EvalResult.Val.isInt() &&
@@ -195,13 +229,13 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
Satisfaction.Details.emplace_back(ConstraintExpr,
SubstitutedAtomicExpr.get());
- return false;
+ return SubstitutedAtomicExpr;
}
-static bool calculateConstraintSatisfaction(
- Sema &S, const NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
- SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL,
- const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) {
+static ExprResult calculateConstraintSatisfaction(
+ Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
+ const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
+ ConstraintSatisfaction &Satisfaction) {
return calculateConstraintSatisfaction(
S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
EnterExpressionEvaluationContext ConstantEvaluated(
@@ -219,8 +253,8 @@ static bool calculateConstraintSatisfaction(
return ExprError();
// We do not want error diagnostics escaping here.
Sema::SFINAETrap Trap(S);
- SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr),
- MLTAL);
+ SubstitutedExpression =
+ S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
// Substitution might have stripped off a contextual conversion to
// bool if this is the operand of an '&&' or '||'. For example, we
// might lose an lvalue-to-rvalue conversion here. If so, put it back
@@ -268,72 +302,92 @@ static bool calculateConstraintSatisfaction(
});
}
-static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template,
- ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs,
- SourceRange TemplateIDRange,
- ConstraintSatisfaction &Satisfaction) {
+static bool CheckConstraintSatisfaction(
+ Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
+ llvm::SmallVectorImpl<Expr *> &Converted,
+ const MultiLevelTemplateArgumentList &TemplateArgsList,
+ SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
if (ConstraintExprs.empty()) {
Satisfaction.IsSatisfied = true;
return false;
}
- for (auto& Arg : TemplateArgs)
- if (Arg.isInstantiationDependent()) {
- // No need to check satisfaction for dependent constraint expressions.
- Satisfaction.IsSatisfied = true;
- return false;
- }
+ if (TemplateArgsList.isAnyArgInstantiationDependent()) {
+ // No need to check satisfaction for dependent constraint expressions.
+ Satisfaction.IsSatisfied = true;
+ return false;
+ }
+
+ ArrayRef<TemplateArgument> TemplateArgs =
+ TemplateArgsList.getNumSubstitutedLevels() > 0
+ ? TemplateArgsList.getOutermost()
+ : ArrayRef<TemplateArgument>{};
- Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
+ Sema::InstantiatingTemplate Inst(
+ S, TemplateIDRange.getBegin(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
if (Inst.isInvalid())
return true;
- MultiLevelTemplateArgumentList MLTAL;
- MLTAL.addOuterTemplateArguments(TemplateArgs);
-
for (const Expr *ConstraintExpr : ConstraintExprs) {
- if (calculateConstraintSatisfaction(S, Template, TemplateArgs,
- TemplateIDRange.getBegin(), MLTAL,
- ConstraintExpr, Satisfaction))
+ ExprResult Res = calculateConstraintSatisfaction(
+ S, Template, TemplateIDRange.getBegin(), TemplateArgsList,
+ ConstraintExpr, Satisfaction);
+ if (Res.isInvalid())
return true;
- if (!Satisfaction.IsSatisfied)
+
+ Converted.push_back(Res.get());
+ if (!Satisfaction.IsSatisfied) {
+ // Backfill the 'converted' list with nulls so we can keep the Converted
+ // and unconverted lists in sync.
+ Converted.append(ConstraintExprs.size() - Converted.size(), nullptr);
// [temp.constr.op] p2
- // [...] To determine if a conjunction is satisfied, the satisfaction
- // of the first operand is checked. If that is not satisfied, the
- // conjunction is not satisfied. [...]
+ // [...] To determine if a conjunction is satisfied, the satisfaction
+ // of the first operand is checked. If that is not satisfied, the
+ // conjunction is not satisfied. [...]
return false;
+ }
}
return false;
}
bool Sema::CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
- ArrayRef<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange,
- ConstraintSatisfaction &OutSatisfaction) {
+ llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
+ const MultiLevelTemplateArgumentList &TemplateArgsList,
+ SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
if (ConstraintExprs.empty()) {
OutSatisfaction.IsSatisfied = true;
return false;
}
if (!Template) {
return ::CheckConstraintSatisfaction(*this, nullptr, ConstraintExprs,
- TemplateArgs, TemplateIDRange,
- OutSatisfaction);
+ ConvertedConstraints, TemplateArgsList,
+ TemplateIDRange, OutSatisfaction);
}
+
+ // A list of the template argument list flattened in a predictible manner for
+ // the purposes of caching. The ConstraintSatisfaction type is in AST so it
+ // has no access to the MultiLevelTemplateArgumentList, so this has to happen
+ // here.
+ llvm::SmallVector<TemplateArgument, 4> FlattenedArgs;
+ for (ArrayRef<TemplateArgument> List : TemplateArgsList)
+ FlattenedArgs.insert(FlattenedArgs.end(), List.begin(), List.end());
+
llvm::FoldingSetNodeID ID;
- ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs);
+ ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
+
void *InsertPos;
if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
OutSatisfaction = *Cached;
return false;
}
auto Satisfaction =
- std::make_unique<ConstraintSatisfaction>(Template, TemplateArgs);
+ std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs,
- TemplateArgs, TemplateIDRange,
- *Satisfaction)) {
+ ConvertedConstraints, TemplateArgsList,
+ TemplateIDRange, *Satisfaction)) {
return true;
}
OutSatisfaction = *Satisfaction;
@@ -347,21 +401,118 @@ 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()) {
+ // Don't check constraints if the function is dependent. Also don't check if
+ // this is a function template specialization, as the call to
+ // CheckinstantiatedFunctionTemplateConstraints after this will check it
+ // better.
+ if (FD->isDependentContext() ||
+ FD->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization) {
Satisfaction.IsSatisfied = true;
return false;
}
+
+ ContextRAII SavedContext{
+ *this, cast<DeclContext>(
+ const_cast<FunctionDecl *>(FD)->getNonClosureContext())};
+ LocalInstantiationScope Scope(*this, true);
+ llvm::Optional<MultiLevelTemplateArgumentList> MLTAL =
+ SetupConstraintCheckingTemplateArgumentsAndScope(
+ const_cast<FunctionDecl *>(FD), {}, Scope);
+
Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) {
@@ -372,14 +523,27 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
// We substitute with empty arguments in order to rebuild the atomic
// constraint in a constant-evaluated context.
// FIXME: Should this be a dedicated TreeTransform?
- return CheckConstraintSatisfaction(
- FD, {RC}, /*TemplateArgs=*/{},
- SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
- Satisfaction);
+ const Expr *RC = FD->getTrailingRequiresClause();
+ llvm::SmallVector<Expr *, 1> Converted;
+
+ if (CheckConstraintSatisfaction(
+ FD, {RC}, Converted, *MLTAL,
+ SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
+ Satisfaction))
+ return true;
+
+ // FIXME: we need to do this for the function constraints for
+ // comparison of constraints to work, but do we also need to do it for
+ // CheckInstantiatedFunctionConstraints? That one is more
diff icult, but we
+ // seem to always just pick up the constraints from the primary template.
+ assert(Converted.size() <= 1 && "Got more expressions converted?");
+ if (!Converted.empty() && Converted[0] != nullptr)
+ const_cast<FunctionDecl *>(FD)->setTrailingRequiresClause(Converted[0]);
+ return false;
}
bool Sema::EnsureTemplateArgumentListConstraints(
- TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs,
+ TemplateDecl *TD, MultiLevelTemplateArgumentList TemplateArgs,
SourceRange TemplateIDRange) {
ConstraintSatisfaction Satisfaction;
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
@@ -392,7 +556,8 @@ bool Sema::EnsureTemplateArgumentListConstraints(
SmallString<128> TemplateArgString;
TemplateArgString = " ";
TemplateArgString += getTemplateArgumentBindingsText(
- TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size());
+ TD->getTemplateParameters(), TemplateArgs.getInnermost().data(),
+ TemplateArgs.getInnermost().size());
Diag(TemplateIDRange.getBegin(),
diag::err_template_arg_list_constraints_not_satisfied)
@@ -424,21 +589,13 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Sema::ContextRAII savedContext(*this, Decl);
LocalInstantiationScope Scope(*this);
- // If this is not an explicit specialization - we need to get the instantiated
- // version of the template arguments and add them to scope for the
- // substitution.
- if (Decl->isTemplateInstantiation()) {
- InstantiatingTemplate Inst(*this, Decl->getPointOfInstantiation(),
- InstantiatingTemplate::ConstraintsCheck{}, Decl->getPrimaryTemplate(),
- TemplateArgs, SourceRange());
- if (Inst.isInvalid())
- return true;
- MultiLevelTemplateArgumentList MLTAL(
- *Decl->getTemplateSpecializationArgs());
- if (addInstantiatedParametersToScope(
- Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), Scope, MLTAL))
- return true;
- }
+ Optional<MultiLevelTemplateArgumentList> MLTAL =
+ SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
+ Scope);
+
+ if (!MLTAL)
+ return true;
+
Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
if (auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
@@ -446,7 +603,8 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
Record = Method->getParent();
}
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
- return CheckConstraintSatisfaction(Template, TemplateAC, TemplateArgs,
+ llvm::SmallVector<Expr *, 1> Converted;
+ return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL,
PointOfInstantiation, Satisfaction);
}
@@ -721,22 +879,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,
@@ -766,7 +924,8 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, Concept,
SourceRange(ArgsAsWritten->arguments()[0].getSourceRange().getBegin(),
ArgsAsWritten->arguments().back().getSourceRange().getEnd()));
- if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
+ if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs,
+ /*InstantiateConstraints=*/true))
return true;
Atomic.ParameterMapping.emplace(
MutableArrayRef<TemplateArgumentLoc>(
@@ -777,6 +936,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) {
@@ -839,9 +1012,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;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c226ed6254790..644114c9485ee 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -992,6 +992,78 @@ static bool checkArgPlaceholdersForOverload(Sema &S, MultiExprArg Args,
return false;
}
+// Figure out the to-translation-unit depth for this function declaration for
+// the purpose of seeing if they
diff er by constraints. This isn't the same as
+// getTemplateDepth, because it includes already instantiated parents.
+static unsigned CalculateTemplateDepthForConstraints(Sema &S,
+ FunctionDecl *FD) {
+ MultiLevelTemplateArgumentList MLTAL =
+ S.getTemplateInstantiationArgs(FD, nullptr, /*RelativeToPrimary*/ true,
+ /*Pattern*/ nullptr,
+ /*LookBeyondLambda*/ true);
+ return MLTAL.getNumSubstitutedLevels();
+}
+
+// Friend definitions can appear identical but be
diff erent declarations based
+// on the last sentence of the rule below (others included for clarification):
+// 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.
+static bool FriendsDifferByConstraints(Sema &S, DeclContext *CurContext,
+ FunctionDecl *Old, FunctionDecl *New,
+ Scope *Scope) {
+ // If these aren't friends, than they aren't friends that
diff ere by
+ // constraints.
+ if (!Old->getFriendObjectKind() || !New->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 (New->getLexicalDeclContext() == Old->getLexicalDeclContext())
+ return false;
+
+ if (!Old->getDescribedFunctionTemplate()) {
+ assert(!New->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 Old->getTrailingRequiresClause();
+ }
+
+ SmallVector<const Expr *, 3> OldAC;
+ Old->getDescribedFunctionTemplate()->getAssociatedConstraints(OldAC);
+
+#ifndef NDEBUG
+ SmallVector<const Expr *, 3> NewAC;
+ New->getDescribedFunctionTemplate()->getAssociatedConstraints(NewAC);
+ assert(OldAC.size() == NewAC.size() &&
+ "Difference should have been noticed earlier if sizes of constraints "
+ "aren't the same");
+#endif
+ // If there are no constraints, these are not constrained friend function or
+ // friend function templates.
+ if (OldAC.size() == 0)
+ return false;
+
+ unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(S, Old);
+
+ // At this point, if the constrained function template declaration depends on
+ // a template parameter from an enclosing template, they are not the same
+ // function. Since these were deemed identical before we got here, we only
+ // have to look into 1 side to see if they refer to a containing template.
+ for (const Expr *Constraint : OldAC)
+ if (S.ConstraintExpressionDependsOnEnclosingTemplate(OldTemplateDepth,
+ Constraint))
+ return true;
+
+ return false;
+}
+
/// Determine whether the given New declaration is an overload of the
/// declarations in Old. This routine returns Ovl_Match or Ovl_NonFunction if
/// New and Old cannot be overloaded, e.g., if New has the same signature as
@@ -1068,6 +1140,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 (FriendsDifferByConstraints(*this, CurContext, OldF, New, S))
+ continue;
+
Match = *I;
return Ovl_Match;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index dbfe6164bda2c..1cf3843fd346f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1685,6 +1685,57 @@ 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
+ 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.
@@ -2286,7 +2337,8 @@ struct ConvertConstructorToDeductionGuideTransform {
TTP->isExpandedParameterPack() ?
llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None);
if (const auto *TC = TTP->getTypeConstraint())
- SemaRef.SubstTypeConstraint(NewTTP, TC, Args);
+ SemaRef.SubstTypeConstraint(NewTTP, TC, Args,
+ /*IsEvaluatingAConstraint*/ false);
if (TTP->hasDefaultArgument()) {
TypeSourceInfo *InstantiatedDefaultArg =
SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
@@ -4703,9 +4755,11 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
bool AreArgsDependent =
TemplateSpecializationType::anyDependentTemplateArguments(*TemplateArgs,
Converted);
+ MultiLevelTemplateArgumentList MLTAL;
+ MLTAL.addOuterTemplateArguments(Converted);
if (!AreArgsDependent &&
CheckConstraintSatisfaction(
- NamedConcept, {NamedConcept->getConstraintExpr()}, Converted,
+ NamedConcept, {NamedConcept->getConstraintExpr()}, MLTAL,
SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
TemplateArgs->getRAngleLoc()),
Satisfaction))
@@ -5567,7 +5621,8 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,
TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, Converted);
Params = SubstTemplateParams(Params, CurContext,
- MultiLevelTemplateArgumentList(TemplateArgs));
+ MultiLevelTemplateArgumentList(TemplateArgs),
+ /*InstantiateConstraints*/ true);
if (!Params)
return true;
}
@@ -5922,13 +5977,20 @@ bool Sema::CheckTemplateArgumentList(
if (UpdateArgsWithConversions)
TemplateArgs = std::move(NewArgs);
- if (!PartialTemplateArgs &&
- EnsureTemplateArgumentListConstraints(
- Template, Converted, SourceRange(TemplateLoc,
- TemplateArgs.getRAngleLoc()))) {
- if (ConstraintsNotSatisfied)
- *ConstraintsNotSatisfied = true;
- return true;
+ if (!PartialTemplateArgs) {
+ TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack,
+ Converted);
+ MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
+ Template, &StackTemplateArgs, /*RelativeToPrimary*/ true,
+ /*Pattern*/ nullptr,
+ /*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true);
+ if (EnsureTemplateArgumentListConstraints(
+ Template, MLTAL,
+ SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
+ if (ConstraintsNotSatisfied)
+ *ConstraintsNotSatisfied = true;
+ return true;
+ }
}
return false;
@@ -7458,7 +7520,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))
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 9ec33e8981983..fb7a13a21deb0 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2783,6 +2783,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
@@ -2791,8 +2805,25 @@ CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template,
TemplateDeductionInfo& Info) {
llvm::SmallVector<const Expr *, 3> AssociatedConstraints;
Template->getAssociatedConstraints(AssociatedConstraints);
- if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints,
- DeducedArgs, Info.getLocation(),
+ MultiLevelTemplateArgumentList MLTAL;
+
+ 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));
@@ -4572,8 +4603,11 @@ CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs,
/*PartialTemplateArgs=*/false, Converted))
return Sema::DAR_FailedAlreadyDiagnosed;
+
+ MultiLevelTemplateArgumentList MLTAL;
+ MLTAL.addOuterTemplateArguments(Converted);
if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()},
- Converted, TypeLoc.getLocalSourceRange(),
+ MLTAL, TypeLoc.getLocalSourceRange(),
Satisfaction))
return Sema::DAR_FailedAlreadyDiagnosed;
if (!Satisfaction.IsSatisfied) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index f09b3473c0749..4da1e0a2db3d3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -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,18 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
}
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(Ctx)) {
if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) {
- assert(Result.getNumSubstitutedLevels() == 0 &&
+ assert((IncludeContainingStructArgs ||
+ Result.getNumSubstitutedLevels() == 0) &&
"Outer template not instantiated?");
if (ClassTemplate->isMemberSpecialization())
break;
+ if (IncludeContainingStructArgs) {
+ QualType RecordType = Context.getTypeDeclType(Rec);
+ QualType Injected = cast<InjectedClassNameType>(RecordType)
+ ->getInjectedSpecializationType();
+ const auto *InjectedType = cast<TemplateSpecializationType>(Injected);
+ Result.addOuterTemplateArguments(InjectedType->template_arguments());
+ }
}
}
@@ -930,16 +949,17 @@ namespace {
const MultiLevelTemplateArgumentList &TemplateArgs;
SourceLocation Loc;
DeclarationName Entity;
+ bool EvaluatingAConstraint = false;
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,
+ bool EvaluatingConstraint = false)
+ : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
+ Entity(Entity), EvaluatingAConstraint(EvaluatingConstraint) {}
/// Determine whether the given type \p T has already been
/// transformed.
@@ -1194,6 +1214,18 @@ namespace {
return DeclInstantiator.SubstTemplateParams(OrigTPL);
}
+ TemplateParameterList *
+ TransformRequiresTemplateParameterList(TemplateParameterList *OrigTPL) {
+ if (!OrigTPL || !OrigTPL->size())
+ return OrigTPL;
+
+ DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
+ TemplateDeclInstantiator DeclInstantiator(
+ getSema(),
+ /* DeclContext *Owner */ Owner, TemplateArgs, EvaluatingAConstraint);
+ return DeclInstantiator.SubstTemplateParams(OrigTPL);
+ }
+
concepts::TypeRequirement *
TransformTypeRequirement(concepts::TypeRequirement *Req);
concepts::ExprRequirement *
@@ -1986,7 +2018,7 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
if (TPLInst.isInvalid())
return nullptr;
TemplateParameterList *TPL =
- TransformTemplateParameterList(OrigTPL);
+ TransformRequiresTemplateParameterList(OrigTPL);
if (!TPL)
TransRetReq.emplace(createSubstDiag(SemaRef, Info,
[&] (llvm::raw_ostream& OS) {
@@ -2349,9 +2381,21 @@ namespace {
bool Sema::SubstTypeConstraint(
TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
- const MultiLevelTemplateArgumentList &TemplateArgs) {
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ bool isEvaluatingAConstraint) {
const ASTTemplateArgumentListInfo *TemplArgInfo =
TC->getTemplateArgsAsWritten();
+
+ // If we're not checking a constraint, we shouldn't be instantiating the type
+ // constraint, so we should just create a copy of the previous one.
+ if (!isEvaluatingAConstraint) {
+ Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(),
+ TC->getConceptNameInfo(), TC->getNamedConcept(),
+ TC->getNamedConcept(), TemplArgInfo,
+ TC->getImmediatelyDeclaredConstraint());
+ return false;
+ }
+
TemplateArgumentListInfo InstArgs;
if (TemplArgInfo) {
@@ -2432,9 +2476,8 @@ 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,
+ /*isEvaluatingAConstraint*/ false))
return nullptr;
}
}
@@ -3520,10 +3563,10 @@ Sema::SubstStmt(Stmt *S, const MultiLevelTemplateArgumentList &TemplateArgs) {
bool Sema::SubstTemplateArguments(
ArrayRef<TemplateArgumentLoc> Args,
const MultiLevelTemplateArgumentList &TemplateArgs,
- TemplateArgumentListInfo &Out) {
- TemplateInstantiator Instantiator(*this, TemplateArgs,
- SourceLocation(),
- DeclarationName());
+ TemplateArgumentListInfo &Out, bool InstantiateConstraints) {
+ TemplateInstantiator Instantiator(
+ *this, TemplateArgs, SourceLocation(), DeclarationName(),
+ InstantiateConstraints);
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(),
Out);
}
@@ -3539,6 +3582,18 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
return Instantiator.TransformExpr(E);
}
+ExprResult
+Sema::SubstConstraintExpr(Expr *E,
+ const MultiLevelTemplateArgumentList &TemplateArgs) {
+ if (!E)
+ return E;
+
+ TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
+ DeclarationName(),
+ /*EvaluatingConstraint*/ true);
+ return Instantiator.TransformExpr(E);
+}
+
ExprResult Sema::SubstInitializer(Expr *Init,
const MultiLevelTemplateArgumentList &TemplateArgs,
bool CXXDirectInit) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index d7558017948a2..be099f5ccf6c2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2062,19 +2062,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
return nullptr;
}
- // FIXME: Concepts: Do not substitute into constraint expressions
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
- if (TrailingRequiresClause) {
- EnterExpressionEvaluationContext ConstantEvaluated(
- SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
- ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
- TemplateArgs);
- if (SubstRC.isInvalid())
- return nullptr;
- TrailingRequiresClause = SubstRC.get();
- if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause))
- return nullptr;
- }
// If we're instantiating a local function declaration, put the result
// in the enclosing namespace; otherwise we need to find the instantiated
@@ -2182,6 +2170,11 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
// definition. We don't want non-template functions to be marked as being
// template instantiations.
Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
+ } else if (!isFriend) {
+ // If this is not a function template, and this is not a friend (that is,
+ // this is a locally declared function), save the instantiation relationship
+ // for the purposes of constraint instantiation.
+ Function->setInstantiatedFromDecl(D);
}
if (isFriend) {
@@ -2420,23 +2413,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
return nullptr;
}
- // FIXME: Concepts: Do not substitute into constraint expressions
- Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
- if (TrailingRequiresClause) {
- EnterExpressionEvaluationContext ConstantEvaluated(
- SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
- auto *ThisContext = dyn_cast_or_null<CXXRecordDecl>(Owner);
- Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext,
- D->getMethodQualifiers(), ThisContext);
- ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
- TemplateArgs);
- if (SubstRC.isInvalid())
- return nullptr;
- TrailingRequiresClause = SubstRC.get();
- if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause))
- return nullptr;
- }
-
DeclContext *DC = Owner;
if (isFriend) {
if (QualifierLoc) {
@@ -2454,6 +2430,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
if (!DC) return nullptr;
}
+ CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
+ Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
+
DeclarationNameInfo NameInfo
= SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);
@@ -2461,7 +2440,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo);
// Build the instantiated method declaration.
- CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
CXXMethodDecl *Method = nullptr;
SourceLocation StartLoc = D->getInnerLocStart();
@@ -2769,10 +2747,8 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
// Invented template parameter type constraints will be instantiated with
// the corresponding auto-typed parameter as it might reference other
// parameters.
-
- // TODO: Concepts: do not instantiate the constraint (delayed constraint
- // substitution)
- if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs))
+ if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs,
+ EvaluatingAConstraint))
return nullptr;
}
}
@@ -4016,18 +3992,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(),
@@ -4038,8 +4003,10 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
TemplateParameterList *
Sema::SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
- const MultiLevelTemplateArgumentList &TemplateArgs) {
- TemplateDeclInstantiator Instantiator(*this, Owner, TemplateArgs);
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ bool InstantiateConstraints) {
+ TemplateDeclInstantiator Instantiator(*this, Owner, TemplateArgs,
+ InstantiateConstraints);
return Instantiator.SubstTemplateParams(Params);
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index b2612e25f40a7..5da255293943a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -13056,13 +13056,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
@@ -13097,7 +13090,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;
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index b4506da2bb2ba..d70e824224dfe 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -953,6 +953,10 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
case FunctionDecl::TK_NonTemplate:
mergeRedeclarable(FD, Redecl);
break;
+ case FunctionDecl::TK_DependentNonTemplate:
+ mergeRedeclarable(FD, Redecl);
+ FD->setInstantiatedFromDecl(readDeclAs<FunctionDecl>());
+ break;
case FunctionDecl::TK_FunctionTemplate:
// Merged when we merge the template.
FD->setDescribedFunctionTemplate(readDeclAs<FunctionTemplateDecl>());
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 01f692c9611b7..35b8db27bd0eb 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -585,6 +585,9 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
switch (D->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
break;
+ case FunctionDecl::TK_DependentNonTemplate:
+ Record.AddDeclRef(D->getInstantiatedFromDecl());
+ break;
case FunctionDecl::TK_FunctionTemplate:
Record.AddDeclRef(D->getDescribedFunctionTemplate());
break;
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
index dd6bfe8011ddf..15e00e4481e75 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
@@ -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
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp
index ceb5af87b8f02..7772eecc69be8 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp
@@ -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.
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp
index 98bfaead773aa..972a4fd0c5c82 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp
@@ -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);
diff --git a/clang/test/SemaTemplate/concepts-friends.cpp b/clang/test/SemaTemplate/concepts-friends.cpp
new file mode 100644
index 0000000000000..650376732339e
--- /dev/null
+++ b/clang/test/SemaTemplate/concepts-friends.cpp
@@ -0,0 +1,371 @@
+// 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 at -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 at -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 at -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 at -2{{in instantiation of template class}}
+ // expected-note@#NTF1 {{previous definition is here}}
+
+ TemplateFromEnclosing<int> S2;
+ // expected-error@#TFE2 {{redefinition of 'foo'}}
+ // expected-note at -2{{in instantiation of template class}}
+ // expected-note@#TFE1 {{previous definition is here}}
+
+ Redefinition<int> S3;
+ // expected-error@#RD2 {{redefinition of 'foo'}}
+ // expected-note at -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 at -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
diff erent 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 at +1{{previous definition is here}}
+ void foo1(_View &&, _Closure &&) {}
+ template <typename _View, typename _Closure>
+ requires same_as<_Tp, _View>
+ // expected-error at +1{{class member cannot be redeclared}}
+ void foo1(_View &&, _Closure &&) {}
+
+ template <typename _View, typename _Closure>
+ // expected-note at +1{{previous definition is here}}
+ void foo2(_View &&, _Closure &&)
+ requires same_as<_Tp, _View>
+ {}
+ template <typename _View, typename _Closure>
+ // expected-error at +1{{class member cannot be redeclared}}
+ void foo2(_View &&, _Closure &&)
+ requires same_as<_Tp, _View>
+ {}
+
+ template <same_as<_Tp> _View, typename _Closure>
+ // expected-note at +1{{previous definition is here}}
+ void foo3(_View &&, _Closure &&) {}
+ template <same_as<_Tp> _View, typename _Closure>
+ // expected-error at +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 at -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 at -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
diff erent 'scopes'.
+ S3<int> s3;
+ S4<int> s4;
+
+ // OK, because only instantiated with
diff erent T.
+ S5<int> s5;
+ S6<double> s6;
+
+ S7<int> s7;
+}
+
+} // namespace LibCXXOperatorRedef
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 23c79421bb6f8..390d0e4562725 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -256,3 +256,321 @@ C auto **j1 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'
C auto **&j2 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'}}
C auto **&&j3 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'}}
}
+
+namespace 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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -1{{invalid reference to function 'foo'}}
+ // expected-note@#CMV_FOO{{because 'IsInt<decltype(this->t)>' evaluated to false}}
+ CMV2.foo2<float>();
+ // expected-error at -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
diff --git a/clang/test/SemaTemplate/deferred-concept-inst.cpp b/clang/test/SemaTemplate/deferred-concept-inst.cpp
new file mode 100644
index 0000000000000..dd9a4086a42ed
--- /dev/null
+++ b/clang/test/SemaTemplate/deferred-concept-inst.cpp
@@ -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
diff --git a/clang/test/SemaTemplate/instantiate-requires-clause.cpp b/clang/test/SemaTemplate/instantiate-requires-clause.cpp
index 73dd20d27d226..3f438d8885a20 100644
--- a/clang/test/SemaTemplate/instantiate-requires-clause.cpp
+++ b/clang/test/SemaTemplate/instantiate-requires-clause.cpp
@@ -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 at -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 at +3 {{candidate template ignored: constraints not satisfied [with U = int]}}
+ // expected-note at +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 at +1 {{no matching function for call to 'f'}}
+static_assert(S1<void>::f(1));
+
constexpr auto value = 0;
template<typename T>
diff --git a/clang/test/SemaTemplate/trailing-return-short-circuit.cpp b/clang/test/SemaTemplate/trailing-return-short-circuit.cpp
new file mode 100644
index 0000000000000..0d1c9b52b0e85
--- /dev/null
+++ b/clang/test/SemaTemplate/trailing-return-short-circuit.cpp
@@ -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 at -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 at -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 at -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 at -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}}
+}
More information about the cfe-commits
mailing list