[Clang] Normalize constraints before checking for satisfaction (#161671)

In the standard, constraint satisfaction checking is done on the
normalized form of a constraint.

Clang instead substitutes on the non-normalized form, which causes us to
report substitution failures in template arguments or concept ids, which
is non-conforming but unavoidable without a parameter mapping

This patch normalizes before satisfaction checking. However, we preserve
concept-id nodes in the normalized form, solely for diagnostics
purposes.

This addresses https://github.com/llvm/llvm-project/issues/61811 and
related concepts conformance bugs, ideally to make the remaining
implementation of concept template parameters easier

Fixes https://github.com/llvm/llvm-project/issues/135190
Fixes https://github.com/llvm/llvm-project/issues/61811

Co-authored-by: Younan Zhang
[zyn7109@gmail.com](mailto:zyn7109@gmail.com)

---------

Co-authored-by: Younan Zhang <zyn7109@gmail.com>
This commit is contained in:
Corentin Jabot 2025-10-03 09:29:23 +02:00 committed by GitHub
parent fa57ce980a
commit e9972debc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 2651 additions and 1208 deletions

View File

@ -2859,6 +2859,67 @@ This library is called by the :ref:`Parser library <Parser>` during parsing to
do semantic analysis of the input. For valid programs, Sema builds an AST for
parsed constructs.
Concept Satisfaction Checking and Subsumption
---------------------------------------------
As per the C++ standard, constraints are `normalized <https://eel.is/c++draft/temp.constr.normal>`_
and the normal form is used both for subsumption, and constraint checking.
Both depend on a parameter mapping that substitutes lazily. In particular,
we should not substitute in unused arguments.
Clang follows the order of operations prescribed by the standard.
Normalization happens prior to satisfaction and subsumption
and is handled by ``NormalizedConstraint``.
Clang preserves in the normalized form intermediate concept-ids
(``ConceptIdConstraint``) This is used for diagnostics only and no substitution
happens in a ConceptIdConstraint if its expression is satisfied.
The normal form of the associated constraints of a declaration is cached in
Sema::NormalizationCache such that it is only computed once.
A ``NormalizedConstraint`` is a recursive data structure, where each node
contains a parameter mapping, represented by the indexes of all parameter
being used.
Checking satisfaction is done by ``ConstraintSatisfactionChecker``, recursively
walking ``NormalizedConstraint``. At each level, we substitute the outermost
level of the template arguments referenced in the parameter mapping of a
normalized expression (``MultiLevelTemplateArgumentList``).
For the following example,
.. code-block:: c++
template <typename T>
concept A = __is_same(T, int);
template <typename U>
concept B = A<U> && __is_same(U, int);
The normal form of B is
.. code-block:: c++
__is_same(T, int) /*T->U, innermost level*/
&& __is_same(U, int) {U->U} /*T->U, outermost level*/
After substitution in the mapping, we substitute in the constraint expression
using that copy of the ``MultiLevelTemplateArgumentList``, and then evaluate it.
Because this is expensive, it is cached in
``UnsubstitutedConstraintSatisfactionCache``.
Any error during satisfaction is recorded in ``ConstraintSatisfaction``.
for nested requirements, ``ConstraintSatisfaction`` is stored (including
diagnostics) in the AST, which is something we might want to improve.
When an atomic constraint is not satified, we try to substitute into any
enclosing concept-id using the same mechanism described above, for
diagnostics purpose, and inject that in the ``ConstraintSatisfaction``.
.. _CodeGen:
The CodeGen Library

View File

@ -160,6 +160,10 @@ C++23 Feature Support
C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Clang now normalizes constraints before checking whether they are satisfied, as mandated by the standard.
As a result, Clang no longer incorrectly diagnoses substitution failures in template arguments only
used in concept-ids, and produces better diagnostics for satisfaction failure. (#GH61811) (#GH135190)
C++17 Feature Support
^^^^^^^^^^^^^^^^^^^^^

View File

@ -28,10 +28,20 @@ namespace clang {
class ConceptDecl;
class TemplateDecl;
class ConceptReference;
class Expr;
class NamedDecl;
struct PrintingPolicy;
/// Unsatisfied constraint expressions if the template arguments could be
/// substituted into them, or a diagnostic if substitution resulted in
/// an invalid expression.
///
using ConstraintSubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
using UnsatisfiedConstraintRecord =
llvm::PointerUnion<const Expr *, const ConceptReference *,
const ConstraintSubstitutionDiagnostic *>;
/// The result of a constraint satisfaction check, containing the necessary
/// information to diagnose an unsatisfied constraint.
class ConstraintSatisfaction : public llvm::FoldingSetNode {
@ -48,16 +58,13 @@ public:
ArrayRef<TemplateArgument> TemplateArgs)
: ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {}
using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;
bool IsSatisfied = false;
bool ContainsErrors = false;
/// \brief The substituted constraint expr, if the template arguments could be
/// substituted into them, or a diagnostic if substitution resulted in an
/// invalid expression.
llvm::SmallVector<Detail, 4> Details;
llvm::SmallVector<UnsatisfiedConstraintRecord, 4> Details;
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) {
Profile(ID, C, ConstraintOwner, TemplateArgs);
@ -69,19 +76,12 @@ public:
bool HasSubstitutionFailure() {
for (const auto &Detail : Details)
if (Detail.dyn_cast<SubstitutionDiagnostic *>())
if (Detail.dyn_cast<const ConstraintSubstitutionDiagnostic *>())
return true;
return false;
}
};
/// Pairs of unsatisfied atomic constraint expressions along with the
/// substituted constraint expr, if the template arguments could be
/// substituted into them, or a diagnostic if substitution resulted in
/// an invalid expression.
using UnsatisfiedConstraintRecord =
llvm::PointerUnion<Expr *, std::pair<SourceLocation, StringRef> *>;
/// \brief The result of a constraint satisfaction check, containing the
/// necessary information to diagnose an unsatisfied constraint.
///
@ -101,6 +101,10 @@ struct ASTConstraintSatisfaction final :
return getTrailingObjects() + NumRecords;
}
ArrayRef<UnsatisfiedConstraintRecord> records() const {
return {begin(), end()};
}
ASTConstraintSatisfaction(const ASTContext &C,
const ConstraintSatisfaction &Satisfaction);
ASTConstraintSatisfaction(const ASTContext &C,
@ -282,6 +286,11 @@ public:
}
};
/// Insertion operator for diagnostics. This allows sending ConceptReferences's
/// into a diagnostic with <<.
const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
const ConceptReference *C);
} // clang
#endif // LLVM_CLANG_AST_ASTCONCEPT_H

View File

@ -3877,7 +3877,6 @@ typename clang::LazyGenerationalUpdatePtr<Owner, T, Update>::ValueType
return new (Ctx) LazyData(Source, Value);
return Value;
}
template <> struct llvm::DenseMapInfo<llvm::FoldingSetNodeID> {
static FoldingSetNodeID getEmptyKey() { return FoldingSetNodeID{}; }

View File

@ -65,6 +65,7 @@
#include "clang/Sema/Redeclaration.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaBase.h"
#include "clang/Sema/SemaConcept.h"
#include "clang/Sema/TypoCorrection.h"
#include "clang/Sema/Weak.h"
#include "llvm/ADT/APInt.h"
@ -11694,8 +11695,9 @@ public:
ExprResult
CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const TemplateArgumentListInfo *TemplateArgs);
NamedDecl *FoundDecl, TemplateDecl *NamedConcept,
const TemplateArgumentListInfo *TemplateArgs,
bool DoCheckConstraintSatisfaction = true);
void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc);
void diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
@ -12025,6 +12027,13 @@ public:
bool UpdateArgsWithConversions = true,
bool *ConstraintsNotSatisfied = nullptr);
bool CheckTemplateArgumentList(
TemplateDecl *Template, TemplateParameterList *Params,
SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs,
const DefaultArguments &DefaultArgs, bool PartialTemplateArgs,
CheckTemplateArgumentInfo &CTAI, bool UpdateArgsWithConversions = true,
bool *ConstraintsNotSatisfied = nullptr);
bool CheckTemplateTypeArgument(
TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg,
SmallVectorImpl<TemplateArgument> &SugaredConverted,
@ -12783,6 +12792,18 @@ public:
void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced,
unsigned Depth, llvm::SmallBitVector &Used);
/// Mark which template parameters are named in a given expression.
///
/// Unlike MarkUsedTemplateParameters, this excludes parameter that
/// are used but not directly named by an expression - i.e. it excludes
/// any template parameter that denotes the type of a referenced NTTP.
///
/// \param Used a bit vector whose elements will be set to \c true
/// to indicate when the corresponding template parameter will be
/// deduced.
void MarkUsedTemplateParametersForSubsumptionParameterMapping(
const Expr *E, unsigned Depth, llvm::SmallBitVector &Used);
/// Mark which template parameters can be deduced from a given
/// template argument list.
///
@ -12799,6 +12820,9 @@ public:
void MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
unsigned Depth, llvm::SmallBitVector &Used);
void MarkUsedTemplateParameters(ArrayRef<TemplateArgumentLoc> TemplateArgs,
unsigned Depth, llvm::SmallBitVector &Used);
void
MarkDeducedTemplateParameters(const FunctionTemplateDecl *FunctionTemplate,
llvm::SmallBitVector &Deduced) {
@ -13096,6 +13120,9 @@ public:
/// Whether we're substituting into constraints.
bool InConstraintSubstitution;
/// Whether we're substituting into the parameter mapping of a constraint.
bool InParameterMappingSubstitution;
/// The point of instantiation or synthesis within the source code.
SourceLocation PointOfInstantiation;
@ -13146,8 +13173,10 @@ public:
CodeSynthesisContext()
: Kind(TemplateInstantiation),
SavedInNonInstantiationSFINAEContext(false),
InConstraintSubstitution(false), Entity(nullptr), Template(nullptr),
TemplateArgs(nullptr), NumTemplateArgs(0), DeductionInfo(nullptr) {}
InConstraintSubstitution(false),
InParameterMappingSubstitution(false), Entity(nullptr),
Template(nullptr), TemplateArgs(nullptr), NumTemplateArgs(0),
DeductionInfo(nullptr) {}
/// Determines whether this template is an actual instantiation
/// that should be counted toward the maximum instantiation depth.
@ -13359,6 +13388,11 @@ public:
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Outputs);
bool SubstTemplateArgumentsInParameterMapping(
ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Out, bool BuildPackExpansionTypes);
/// Retrieve the template argument list(s) that should be used to
/// instantiate the definition of the given declaration.
///
@ -13820,6 +13854,12 @@ public:
CodeSynthesisContexts.back().InConstraintSubstitution;
}
bool inParameterMappingSubstitution() const {
return !CodeSynthesisContexts.empty() &&
CodeSynthesisContexts.back().InParameterMappingSubstitution &&
!inConstraintSubstitution();
}
using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>;
/// \brief create a Requirement::SubstitutionDiagnostic with only a
@ -14704,6 +14744,10 @@ public:
SatisfactionStack.swap(NewSS);
}
using ConstrainedDeclOrNestedRequirement =
llvm::PointerUnion<const NamedDecl *,
const concepts::NestedRequirement *>;
/// Check whether the given expression is a valid constraint expression.
/// A diagnostic is emitted if it is not, false is returned, and
/// PossibleNonPrimary will be set to true if the failure might be due to a
@ -14728,44 +14772,12 @@ public:
/// \returns true if an error occurred and satisfaction could not be checked,
/// false otherwise.
bool CheckConstraintSatisfaction(
const NamedDecl *Template,
ConstrainedDeclOrNestedRequirement Entity,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
llvm::SmallVector<Expr *, 4> Converted;
return CheckConstraintSatisfaction(Template, AssociatedConstraints,
Converted, TemplateArgLists,
TemplateIDRange, Satisfaction);
}
/// \brief Check whether the given list of constraint expressions are
/// satisfied (as if in a 'conjunction') given template arguments.
/// Additionally, takes an empty list of Expressions which is populated with
/// the instantiated versions of the ConstraintExprs.
/// \param Template the template-like entity that triggered the constraints
/// check (either a concept or a constrained entity).
/// \param ConstraintExprs a list of constraint expressions, treated as if
/// they were 'AND'ed together.
/// \param ConvertedConstraints a out parameter that will get populated with
/// the instantiated version of the ConstraintExprs if we successfully checked
/// satisfaction.
/// \param TemplateArgList the multi-level list of template arguments to
/// substitute into the constraint expression. This should be relative to the
/// top-level (hence multi-level), since we need to instantiate fully at the
/// time of checking.
/// \param TemplateIDRange The source range of the template id that
/// caused the constraints check.
/// \param Satisfaction if true is returned, will contain details of the
/// satisfaction, with enough information to diagnose an unsatisfied
/// expression.
/// \returns true if an error occurred and satisfaction could not be checked,
/// false otherwise.
bool CheckConstraintSatisfaction(
const NamedDecl *Template,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgList,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
const ConceptReference *TopLevelConceptId = nullptr,
Expr **ConvertedExpr = nullptr);
/// \brief Check whether the given non-dependent constraint expression is
/// satisfied. Returns false and updates Satisfaction with the satisfaction
@ -14831,16 +14843,17 @@ public:
/// \param First whether this is the first time an unsatisfied constraint is
/// diagnosed for this error.
void DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction,
SourceLocation Loc = {},
bool First = true);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
void
DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction,
DiagnoseUnsatisfiedConstraint(const ConceptSpecializationExpr *ConstraintExpr,
bool First = true);
const NormalizedConstraint *getNormalizedAssociatedConstraints(
const NamedDecl *ConstrainedDecl,
ConstrainedDeclOrNestedRequirement Entity,
ArrayRef<AssociatedConstraint> AssociatedConstraints);
/// \brief Check whether the given declaration's associated constraints are
@ -14865,6 +14878,15 @@ public:
const NamedDecl *D1, ArrayRef<AssociatedConstraint> AC1,
const NamedDecl *D2, ArrayRef<AssociatedConstraint> AC2);
/// Cache the satisfaction of an atomic constraint.
/// The key is based on the unsubstituted expression and the parameter
/// mapping. This lets us not substituting the mapping more than once,
/// which is (very!) expensive.
/// FIXME: this should be private.
llvm::DenseMap<llvm::FoldingSetNodeID,
UnsubstitutedConstraintSatisfactionCacheResult>
UnsubstitutedConstraintSatisfactionCache;
private:
/// Caches pairs of template-like decls whose associated constraints were
/// checked for subsumption and whether or not the first's constraints did in
@ -14875,8 +14897,11 @@ private:
/// constrained declarations). If an error occurred while normalizing the
/// associated constraints of the template or concept, nullptr will be cached
/// here.
llvm::DenseMap<const NamedDecl *, NormalizedConstraint *> NormalizationCache;
llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint *>
NormalizationCache;
/// Cache whether the associated constraint of a declaration
/// is satisfied.
llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &>
SatisfactionCache;

View File

@ -16,130 +16,406 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Ownership.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallVector.h"
#include <optional>
#include <utility>
namespace clang {
class Sema;
class MultiLevelTemplateArgumentList;
enum { ConstraintAlignment = 8 };
/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
/// either an atomic constraint, a conjunction of normalized constraints or a
/// disjunction of normalized constraints.
struct NormalizedConstraint {
struct alignas(ConstraintAlignment) AtomicConstraint {
const Expr *ConstraintExpr;
const NamedDecl *ConstraintDecl;
std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping;
enum class ConstraintKind : unsigned char {
Atomic = 0,
ConceptId,
FoldExpanded,
Compound,
};
AtomicConstraint(const Expr *ConstraintExpr, const NamedDecl *ConstraintDecl)
: ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {};
enum CompoundConstraintKind : unsigned char {
CCK_Conjunction,
CCK_Disjunction
};
enum class FoldOperatorKind : unsigned char { And, Or };
using OccurenceList = llvm::SmallBitVector;
protected:
using ExprOrConcept =
llvm::PointerUnion<const Expr *, const ConceptReference *>;
struct AtomicConstraintBits {
// Kind is the first member of all union members,
// as we rely on their initial common sequence.
LLVM_PREFERRED_TYPE(ConstraintKind)
unsigned Kind : 5;
unsigned Placeholder : 1;
unsigned PackSubstitutionIndex : 26;
// Indexes, IndexesForSubsumption, and Args are part of the common initial
// sequences of constraints that do have a mapping.
// Indexes of the parameters used in a constraint expression.
OccurenceList Indexes;
// Indexes of the parameters named directly in a constraint expression.
// FIXME: we should try to reduce the size of this struct?
OccurenceList IndexesForSubsumption;
TemplateArgumentLoc *Args;
TemplateParameterList *ParamList;
ExprOrConcept ConstraintExpr;
const NamedDecl *ConstraintDecl;
};
struct FoldExpandedConstraintBits {
LLVM_PREFERRED_TYPE(ConstraintKind)
unsigned Kind : 5;
LLVM_PREFERRED_TYPE(FoldOperatorKind)
unsigned FoldOperator : 1;
unsigned Placeholder : 26;
OccurenceList Indexes;
OccurenceList IndexesForSubsumption;
TemplateArgumentLoc *Args;
TemplateParameterList *ParamList;
const Expr *Pattern;
const NamedDecl *ConstraintDecl;
NormalizedConstraint *Constraint;
};
struct ConceptIdBits : AtomicConstraintBits {
NormalizedConstraint *Sub;
// Only used for parameter mapping.
const ConceptSpecializationExpr *CSE;
};
struct CompoundConstraintBits {
LLVM_PREFERRED_TYPE(ConstraintKind)
unsigned Kind : 5;
LLVM_PREFERRED_TYPE(CompoundConstraintKind)
unsigned CCK : 1;
NormalizedConstraint *LHS;
NormalizedConstraint *RHS;
};
union {
AtomicConstraintBits Atomic;
FoldExpandedConstraintBits FoldExpanded;
ConceptIdBits ConceptId;
CompoundConstraintBits Compound;
};
~NormalizedConstraint() {
if (getKind() != ConstraintKind::Compound)
Atomic.Indexes.llvm::SmallBitVector::~SmallBitVector();
}
NormalizedConstraint(const Expr *ConstraintExpr,
const NamedDecl *ConstraintDecl,
UnsignedOrNone PackIndex)
: Atomic{llvm::to_underlying(ConstraintKind::Atomic),
/*Placeholder=*/0,
PackIndex.toInternalRepresentation(),
/*Indexes=*/{},
/*IndexesForSubsumption=*/{},
/*Args=*/nullptr,
/*ParamList=*/nullptr,
ConstraintExpr,
ConstraintDecl} {}
NormalizedConstraint(const Expr *Pattern, FoldOperatorKind OpKind,
NormalizedConstraint *Constraint,
const NamedDecl *ConstraintDecl)
: FoldExpanded{llvm::to_underlying(ConstraintKind::FoldExpanded),
llvm::to_underlying(OpKind),
/*Placeholder=*/0,
/*Indexes=*/{},
/*IndexesForSubsumption=*/{},
/*Args=*/nullptr,
/*ParamList=*/nullptr,
Pattern,
ConstraintDecl,
Constraint} {}
NormalizedConstraint(const ConceptReference *ConceptId,
const NamedDecl *ConstraintDecl,
NormalizedConstraint *SubConstraint,
const ConceptSpecializationExpr *CSE,
UnsignedOrNone PackIndex)
: ConceptId{{llvm::to_underlying(ConstraintKind::ConceptId),
/*Placeholder=*/0, PackIndex.toInternalRepresentation(),
/*Indexes=*/{},
/*IndexesForSubsumption=*/{},
/*Args=*/nullptr, /*ParamList=*/nullptr, ConceptId,
ConstraintDecl},
SubConstraint,
CSE} {}
NormalizedConstraint(NormalizedConstraint *LHS, CompoundConstraintKind CCK,
NormalizedConstraint *RHS)
: Compound{llvm::to_underlying(ConstraintKind::Compound),
llvm::to_underlying(CCK), LHS, RHS} {}
bool hasParameterMapping() const {
// compound constraints do not have a mapping
// and Args is not part of their common initial sequence.
return getKind() != ConstraintKind::Compound && Atomic.Args != nullptr;
}
const OccurenceList &mappingOccurenceList() const {
assert(hasParameterMapping() && "This constraint has no parameter mapping");
return Atomic.Indexes;
}
const OccurenceList &mappingOccurenceListForSubsumption() const {
assert(hasParameterMapping() && "This constraint has no parameter mapping");
return Atomic.IndexesForSubsumption;
}
llvm::MutableArrayRef<TemplateArgumentLoc> getParameterMapping() const {
return {Atomic.Args, Atomic.Indexes.count()};
}
TemplateParameterList *getUsedTemplateParamList() const {
return Atomic.ParamList;
}
void updateParameterMapping(OccurenceList Indexes,
OccurenceList IndexesForSubsumption,
llvm::MutableArrayRef<TemplateArgumentLoc> Args,
TemplateParameterList *ParamList) {
assert(getKind() != ConstraintKind::Compound);
assert(Indexes.count() == Args.size());
assert(IndexesForSubsumption.size() == Indexes.size());
assert((Indexes | IndexesForSubsumption) == Indexes);
Atomic.IndexesForSubsumption = std::move(IndexesForSubsumption);
Atomic.Indexes = std::move(Indexes);
Atomic.Args = Args.data();
Atomic.ParamList = ParamList;
}
bool hasMatchingParameterMapping(ASTContext &C,
const AtomicConstraint &Other) const {
if (!ParameterMapping != !Other.ParameterMapping)
return false;
if (!ParameterMapping)
return true;
if (ParameterMapping->size() != Other.ParameterMapping->size())
return false;
const NormalizedConstraint &Other) const {
assert(getKind() != ConstraintKind::Compound);
for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) {
if (hasParameterMapping() != Other.hasParameterMapping())
return false;
if (!hasParameterMapping())
return true;
llvm::ArrayRef<TemplateArgumentLoc> ParameterMapping =
getParameterMapping();
llvm::ArrayRef<TemplateArgumentLoc> OtherParameterMapping =
Other.getParameterMapping();
const OccurenceList &Indexes = mappingOccurenceListForSubsumption();
const OccurenceList &OtherIndexes =
Other.mappingOccurenceListForSubsumption();
if (ParameterMapping.size() != OtherParameterMapping.size())
return false;
for (unsigned I = 0, S = ParameterMapping.size(); I < S; ++I) {
if (Indexes[I] != OtherIndexes[I])
return false;
if (!Indexes[I])
continue;
llvm::FoldingSetNodeID IDA, IDB;
C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
C.getCanonicalTemplateArgument(ParameterMapping[I].getArgument())
.Profile(IDA, C);
C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument())
C.getCanonicalTemplateArgument(OtherParameterMapping[I].getArgument())
.Profile(IDB, C);
if (IDA != IDB)
return false;
}
return true;
}
};
struct alignas(ConstraintAlignment) NormalizedConstraintPair;
struct alignas(ConstraintAlignment) FoldExpandedConstraint;
/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
/// either an atomic constraint, a conjunction of normalized constraints or a
/// disjunction of normalized constraints.
struct NormalizedConstraint {
friend class Sema;
enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1,
CompoundConstraintKind>;
llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
CompoundConstraint>
Constraint;
NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {};
NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
NormalizedConstraint RHS, CompoundConstraintKind Kind);
NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other);
NormalizedConstraint(NormalizedConstraint &&Other):
Constraint(Other.Constraint) {
Other.Constraint = nullptr;
public:
ConstraintKind getKind() const {
return static_cast<ConstraintKind>(Atomic.Kind);
}
NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete;
NormalizedConstraint &operator=(NormalizedConstraint &&Other) {
if (&Other != this) {
NormalizedConstraint Temp(std::move(Other));
std::swap(Constraint, Temp.Constraint);
SourceLocation getBeginLoc() const {
switch (getKind()) {
case ConstraintKind::Atomic:
return cast<const Expr *>(Atomic.ConstraintExpr)->getBeginLoc();
case ConstraintKind::ConceptId:
return cast<const ConceptReference *>(Atomic.ConstraintExpr)
->getBeginLoc();
case ConstraintKind::Compound:
return Compound.LHS->getBeginLoc();
case ConstraintKind::FoldExpanded:
return FoldExpanded.Pattern->getBeginLoc();
}
return *this;
}
bool isAtomic() const { return llvm::isa<AtomicConstraint *>(Constraint); }
bool isFoldExpanded() const {
return llvm::isa<FoldExpandedConstraint *>(Constraint);
SourceLocation getEndLoc() const {
switch (getKind()) {
case ConstraintKind::Atomic:
return cast<const Expr *>(Atomic.ConstraintExpr)->getEndLoc();
case ConstraintKind::ConceptId:
return cast<const ConceptReference *>(Atomic.ConstraintExpr)->getEndLoc();
case ConstraintKind::Compound:
return Compound.RHS->getEndLoc();
case ConstraintKind::FoldExpanded:
return FoldExpanded.Pattern->getEndLoc();
}
}
bool isCompound() const { return llvm::isa<CompoundConstraint>(Constraint); }
CompoundConstraintKind getCompoundKind() const;
NormalizedConstraint &getLHS() const;
NormalizedConstraint &getRHS() const;
AtomicConstraint *getAtomicConstraint() const;
FoldExpandedConstraint *getFoldExpandedConstraint() const;
SourceRange getSourceRange() const { return {getBeginLoc(), getEndLoc()}; }
private:
static std::optional<NormalizedConstraint>
friend class Sema;
static NormalizedConstraint *
fromAssociatedConstraints(Sema &S, const NamedDecl *D,
ArrayRef<AssociatedConstraint> ACs);
static std::optional<NormalizedConstraint>
fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E);
static NormalizedConstraint *fromConstraintExpr(Sema &S, const NamedDecl *D,
const Expr *E,
UnsignedOrNone SubstIndex);
};
struct alignas(ConstraintAlignment) NormalizedConstraintPair {
NormalizedConstraint LHS, RHS;
class CompoundConstraint : public NormalizedConstraint {
using NormalizedConstraint::NormalizedConstraint;
public:
static CompoundConstraint *Create(ASTContext &Ctx, NormalizedConstraint *LHS,
CompoundConstraintKind CCK,
NormalizedConstraint *RHS) {
return new (Ctx) CompoundConstraint(LHS, CCK, RHS);
}
static CompoundConstraint *CreateConjunction(ASTContext &Ctx,
NormalizedConstraint *LHS,
NormalizedConstraint *RHS) {
return new (Ctx) CompoundConstraint(LHS, CCK_Conjunction, RHS);
}
const NormalizedConstraint &getLHS() const { return *Compound.LHS; }
NormalizedConstraint &getLHS() { return *Compound.LHS; }
const NormalizedConstraint &getRHS() const { return *Compound.RHS; }
NormalizedConstraint &getRHS() { return *Compound.RHS; }
CompoundConstraintKind getCompoundKind() const {
return static_cast<CompoundConstraintKind>(Compound.CCK);
}
};
struct alignas(ConstraintAlignment) FoldExpandedConstraint {
enum class FoldOperatorKind { And, Or } Kind;
NormalizedConstraint Constraint;
const Expr *Pattern;
class NormalizedConstraintWithParamMapping : public NormalizedConstraint {
protected:
using NormalizedConstraint::NormalizedConstraint;
FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C,
const Expr *Pattern)
: Kind(K), Constraint(std::move(C)), Pattern(Pattern) {};
public:
using NormalizedConstraint::getParameterMapping;
using NormalizedConstraint::getUsedTemplateParamList;
using NormalizedConstraint::hasMatchingParameterMapping;
using NormalizedConstraint::hasParameterMapping;
using NormalizedConstraint::mappingOccurenceList;
using NormalizedConstraint::mappingOccurenceListForSubsumption;
using NormalizedConstraint::updateParameterMapping;
const NamedDecl *getConstraintDecl() const { return Atomic.ConstraintDecl; }
UnsignedOrNone getPackSubstitutionIndex() const {
return UnsignedOrNone::fromInternalRepresentation(
Atomic.PackSubstitutionIndex);
}
};
class AtomicConstraint : public NormalizedConstraintWithParamMapping {
using NormalizedConstraintWithParamMapping::
NormalizedConstraintWithParamMapping;
public:
static AtomicConstraint *Create(ASTContext &Ctx, const Expr *ConstraintExpr,
const NamedDecl *ConstraintDecl,
UnsignedOrNone PackIndex) {
return new (Ctx)
AtomicConstraint(ConstraintExpr, ConstraintDecl, PackIndex);
}
const Expr *getConstraintExpr() const {
return cast<const Expr *>(Atomic.ConstraintExpr);
}
};
class FoldExpandedConstraint : public NormalizedConstraintWithParamMapping {
using NormalizedConstraintWithParamMapping::
NormalizedConstraintWithParamMapping;
public:
static FoldExpandedConstraint *Create(ASTContext &Ctx, const Expr *Pattern,
const NamedDecl *ConstraintDecl,
FoldOperatorKind OpKind,
NormalizedConstraint *Constraint) {
return new (Ctx)
FoldExpandedConstraint(Pattern, OpKind, Constraint, ConstraintDecl);
}
using NormalizedConstraint::hasMatchingParameterMapping;
FoldOperatorKind getFoldOperator() const {
return static_cast<FoldOperatorKind>(FoldExpanded.FoldOperator);
}
const Expr *getPattern() const { return FoldExpanded.Pattern; }
const NormalizedConstraint &getNormalizedPattern() const {
return *FoldExpanded.Constraint;
}
NormalizedConstraint &getNormalizedPattern() {
return *FoldExpanded.Constraint;
}
static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A,
const FoldExpandedConstraint &B);
};
const NormalizedConstraint *getNormalizedAssociatedConstraints(
Sema &S, const NamedDecl *ConstrainedDecl,
ArrayRef<AssociatedConstraint> AssociatedConstraints);
class ConceptIdConstraint : public NormalizedConstraintWithParamMapping {
using NormalizedConstraintWithParamMapping::
NormalizedConstraintWithParamMapping;
public:
static ConceptIdConstraint *
Create(ASTContext &Ctx, const ConceptReference *ConceptId,
NormalizedConstraint *SubConstraint, const NamedDecl *ConstraintDecl,
const ConceptSpecializationExpr *CSE, UnsignedOrNone PackIndex) {
return new (Ctx) ConceptIdConstraint(ConceptId, ConstraintDecl,
SubConstraint, CSE, PackIndex);
}
const ConceptSpecializationExpr *getConceptSpecializationExpr() const {
return ConceptId.CSE;
}
const ConceptReference *getConceptId() const {
return cast<const ConceptReference *>(ConceptId.ConstraintExpr);
}
const NormalizedConstraint &getNormalizedConstraint() const {
return *ConceptId.Sub;
}
NormalizedConstraint &getNormalizedConstraint() { return *ConceptId.Sub; }
};
struct UnsubstitutedConstraintSatisfactionCacheResult {
ExprResult SubstExpr;
ConstraintSatisfaction Satisfaction;
};
/// \brief SubsumptionChecker establishes subsumption
/// between two set of constraints.
@ -189,13 +465,13 @@ private:
};
struct MappedAtomicConstraint {
AtomicConstraint *Constraint;
const AtomicConstraint *Constraint;
Literal ID;
};
struct FoldExpendedConstraintKey {
FoldExpandedConstraint::FoldOperatorKind Kind;
AtomicConstraint *Constraint;
const AtomicConstraint *Constraint;
Literal ID;
};
@ -207,7 +483,7 @@ private:
// A map from a literal to a corresponding associated constraint.
// We do not have enough bits left for a pointer union here :(
llvm::DenseMap<uint16_t, void *> ReverseMap;
llvm::DenseMap<uint16_t, const void *> ReverseMap;
// Fold expanded constraints ask us to recursively establish subsumption.
// This caches the result.
@ -234,12 +510,12 @@ private:
FormulaType Normalize(const NormalizedConstraint &C);
void AddUniqueClauseToFormula(Formula &F, Clause C);
Literal find(AtomicConstraint *);
Literal find(FoldExpandedConstraint *);
Literal find(const AtomicConstraint *);
Literal find(const FoldExpandedConstraint *);
uint16_t getNewLiteralId();
};
} // clang
} // namespace clang
#endif // LLVM_CLANG_SEMA_SEMACONCEPT_H

View File

@ -234,21 +234,25 @@ enum class TemplateSubstitutionKind : char {
/// 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(Decl *AssociatedDecl, ArgList Args) {
void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args,
bool Final = false) {
assert((!TemplateArgumentLists.empty() || NumRetainedOuterLevels) &&
"Replacing in an empty list?");
if (!TemplateArgumentLists.empty()) {
assert((TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ||
TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ==
AssociatedDecl) &&
"Trying to change incorrect declaration?");
TemplateArgumentLists[0].Args = Args;
} else {
--NumRetainedOuterLevels;
TemplateArgumentLists.push_back(
{{AssociatedDecl, /*Final=*/false}, Args});
return;
}
--NumRetainedOuterLevels;
TemplateArgumentLists.push_back(
{{AssociatedDecl, /*Final=*/Final}, Args});
}
void replaceOutermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) {
assert((!TemplateArgumentLists.empty()) && "Replacing in an empty list?");
TemplateArgumentLists.back().AssociatedDeclAndFinal.setPointer(
AssociatedDecl);
TemplateArgumentLists.back().Args = Args;
}
/// Add an outermost level that we are not substituting. We have no

View File

@ -24,13 +24,18 @@ static void
CreateUnsatisfiedConstraintRecord(const ASTContext &C,
const UnsatisfiedConstraintRecord &Detail,
UnsatisfiedConstraintRecord *TrailingObject) {
if (auto *E = dyn_cast<Expr *>(Detail))
if (Detail.isNull())
new (TrailingObject) UnsatisfiedConstraintRecord(nullptr);
else if (const auto *E = llvm::dyn_cast<const Expr *>(Detail))
new (TrailingObject) UnsatisfiedConstraintRecord(E);
else if (const auto *Concept =
llvm::dyn_cast<const ConceptReference *>(Detail))
new (TrailingObject) UnsatisfiedConstraintRecord(Concept);
else {
auto &SubstitutionDiagnostic =
*cast<std::pair<SourceLocation, StringRef> *>(Detail);
*cast<const clang::ConstraintSubstitutionDiagnostic *>(Detail);
StringRef Message = C.backupStr(SubstitutionDiagnostic.second);
auto *NewSubstDiag = new (C) std::pair<SourceLocation, StringRef>(
auto *NewSubstDiag = new (C) clang::ConstraintSubstitutionDiagnostic(
SubstitutionDiagnostic.first, Message);
new (TrailingObject) UnsatisfiedConstraintRecord(NewSubstDiag);
}
@ -74,9 +79,10 @@ ASTConstraintSatisfaction *ASTConstraintSatisfaction::Rebuild(
return new (Mem) ASTConstraintSatisfaction(C, Satisfaction);
}
void ConstraintSatisfaction::Profile(
llvm::FoldingSetNodeID &ID, const ASTContext &C,
const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> TemplateArgs) {
void ConstraintSatisfaction::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &C,
const NamedDecl *ConstraintOwner,
ArrayRef<TemplateArgument> TemplateArgs) {
ID.AddPointer(ConstraintOwner);
ID.AddInteger(TemplateArgs.size());
for (auto &Arg : TemplateArgs)
@ -116,6 +122,19 @@ void ConceptReference::print(llvm::raw_ostream &OS,
}
}
const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
const ConceptReference *C) {
std::string NameStr;
llvm::raw_string_ostream OS(NameStr);
LangOptions LO;
LO.CPlusPlus = true;
LO.Bool = true;
OS << '\'';
C->print(OS, PrintingPolicy(LO));
OS << '\'';
return DB << NameStr;
}
concepts::ExprRequirement::ExprRequirement(
Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
ReturnTypeRequirement Req, SatisfactionStatus Status,

View File

@ -1069,22 +1069,22 @@ Error ASTNodeImporter::ImportConstraintSatisfaction(
ToSat.ContainsErrors = FromSat.ContainsErrors;
if (!ToSat.IsSatisfied) {
for (auto Record = FromSat.begin(); Record != FromSat.end(); ++Record) {
if (Expr *E = Record->dyn_cast<Expr *>()) {
if (const Expr *E = Record->dyn_cast<const Expr *>()) {
ExpectedExpr ToSecondExpr = import(E);
if (!ToSecondExpr)
return ToSecondExpr.takeError();
ToSat.Details.emplace_back(ToSecondExpr.get());
} else {
auto Pair = Record->dyn_cast<std::pair<SourceLocation, StringRef> *>();
auto Pair =
Record->dyn_cast<const ConstraintSubstitutionDiagnostic *>();
ExpectedSLoc ToPairFirst = import(Pair->first);
if (!ToPairFirst)
return ToPairFirst.takeError();
StringRef ToPairSecond = ImportASTStringRef(Pair->second);
ToSat.Details.emplace_back(
new (Importer.getToContext())
ConstraintSatisfaction::SubstitutionDiagnostic{
ToPairFirst.get(), ToPairSecond});
ToSat.Details.emplace_back(new (Importer.getToContext())
ConstraintSubstitutionDiagnostic{
ToPairFirst.get(), ToPairSecond});
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -17876,13 +17876,15 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
findFailedBooleanCondition(Converted.get());
if (const auto *ConceptIDExpr =
dyn_cast_or_null<ConceptSpecializationExpr>(InnerCond)) {
// Drill down into concept specialization expressions to see why they
// weren't satisfied.
Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
<< !HasMessage << Msg.str() << AssertExpr->getSourceRange();
ConstraintSatisfaction Satisfaction;
if (!CheckConstraintSatisfaction(ConceptIDExpr, Satisfaction))
DiagnoseUnsatisfiedConstraint(Satisfaction);
const ASTConstraintSatisfaction &Satisfaction =
ConceptIDExpr->getSatisfaction();
if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) {
Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
<< !HasMessage << Msg.str() << AssertExpr->getSourceRange();
// Drill down into concept specialization expressions to see why they
// weren't satisfied.
DiagnoseUnsatisfiedConstraint(ConceptIDExpr);
}
} else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) &&
!isa<IntegerLiteral>(InnerCond)) {
Diag(InnerCond->getBeginLoc(),

View File

@ -7935,21 +7935,27 @@ Sema::BuildExprRequirement(
// be satisfied.
TemplateParameterList *TPL =
ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
QualType MatchedType =
Context.getReferenceQualifiedType(E).getCanonicalType();
QualType MatchedType = Context.getReferenceQualifiedType(E);
llvm::SmallVector<TemplateArgument, 1> Args;
Args.push_back(TemplateArgument(MatchedType));
auto *Param = cast<TemplateTypeParmDecl>(TPL->getParam(0));
MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false);
MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/true);
MLTAL.addOuterRetainedLevels(TPL->getDepth());
const TypeConstraint *TC = Param->getTypeConstraint();
assert(TC && "Type Constraint cannot be null here");
auto *IDC = TC->getImmediatelyDeclaredConstraint();
assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
ExprResult Constraint = SubstExpr(IDC, MLTAL);
if (Constraint.isInvalid()) {
bool HasError = Constraint.isInvalid();
if (!HasError) {
SubstitutedConstraintExpr =
cast<ConceptSpecializationExpr>(Constraint.get());
if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors)
HasError = true;
}
if (HasError) {
return new (Context) concepts::ExprRequirement(
createSubstDiagAt(IDC->getExprLoc(),
[&](llvm::raw_ostream &OS) {
@ -7958,8 +7964,6 @@ Sema::BuildExprRequirement(
}),
IsSimple, NoexceptLoc, ReturnTypeRequirement);
}
SubstitutedConstraintExpr =
cast<ConceptSpecializationExpr>(Constraint.get());
if (!SubstitutedConstraintExpr->isSatisfied())
Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
}

View File

@ -804,7 +804,7 @@ clang::MakeDeductionFailureInfo(ASTContext &Context,
case TemplateDeductionResult::ConstraintsNotSatisfied: {
CNSInfo *Saved = new (Context) CNSInfo;
Saved->TemplateArgs = Info.takeSugared();
Saved->Satisfaction = Info.AssociatedConstraintsSatisfaction;
Saved->Satisfaction = std::move(Info.AssociatedConstraintsSatisfaction);
Result.Data = Saved;
break;
}
@ -852,6 +852,7 @@ void DeductionFailureInfo::Destroy() {
case TemplateDeductionResult::ConstraintsNotSatisfied:
// FIXME: Destroy the template argument list?
static_cast<CNSInfo *>(Data)->Satisfaction.~ConstraintSatisfaction();
Data = nullptr;
if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) {
Diag->~PartialDiagnosticAt();
@ -12739,7 +12740,8 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
<< (unsigned)FnKindPair.first << (unsigned)ocs_non_template
<< FnDesc /* Ignored */;
ConstraintSatisfaction Satisfaction;
if (S.CheckFunctionConstraints(Fn, Satisfaction))
if (S.CheckFunctionConstraints(Fn, Satisfaction, SourceLocation(),
/*ForOverloadResolution=*/true))
break;
S.DiagnoseUnsatisfiedConstraint(Satisfaction);
}

View File

@ -9,6 +9,7 @@
//===----------------------------------------------------------------------===//
#include "TreeTransform.h"
#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@ -1222,8 +1223,9 @@ static ExprResult formImmediatelyDeclaredConstraint(
if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) {
ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
/*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD,
&ConstraintArgs);
/*FoundDecl=*/FoundDecl ? FoundDecl : CD, CD, &ConstraintArgs,
/*DoCheckConstraintSatisfaction=*/
!S.inParameterMappingSubstitution());
}
// We have a template template parameter
else {
@ -4850,13 +4852,11 @@ void Sema::diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
diagnoseMissingTemplateArguments(Name, Loc);
}
ExprResult
Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
const DeclarationNameInfo &ConceptNameInfo,
NamedDecl *FoundDecl,
ConceptDecl *NamedConcept,
const TemplateArgumentListInfo *TemplateArgs) {
ExprResult Sema::CheckConceptTemplateId(
const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl,
TemplateDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs,
bool DoCheckConstraintSatisfaction) {
assert(NamedConcept && "A concept template id without a template?");
if (NamedConcept->isInvalidDecl())
@ -4873,33 +4873,48 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc());
// There's a bug with CTAI.CanonicalConverted.
// If the template argument contains a DependentDecltypeType that includes a
// TypeAliasType, and the same written type had occurred previously in the
// source, then the DependentDecltypeType would be canonicalized to that
// previous type which would mess up the substitution.
// FIXME: Reland https://github.com/llvm/llvm-project/pull/101782 properly!
auto *CSD = ImplicitConceptSpecializationDecl::Create(
Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(),
CTAI.CanonicalConverted);
CTAI.SugaredConverted);
ConstraintSatisfaction Satisfaction;
bool AreArgsDependent =
TemplateSpecializationType::anyDependentTemplateArguments(
*TemplateArgs, CTAI.CanonicalConverted);
MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.CanonicalConverted,
*TemplateArgs, CTAI.SugaredConverted);
MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.SugaredConverted,
/*Final=*/false);
LocalInstantiationScope Scope(*this);
EnterExpressionEvaluationContext EECtx{
*this, ExpressionEvaluationContext::Unevaluated, CSD};
if (!AreArgsDependent &&
CheckConstraintSatisfaction(
NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
MLTAL,
SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
TemplateArgs->getRAngleLoc()),
Satisfaction))
return ExprError();
auto *CL = ConceptReference::Create(
Context,
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept,
ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs));
bool Error = false;
if (const auto *Concept = dyn_cast<ConceptDecl>(NamedConcept);
Concept && Concept->getConstraintExpr() && !AreArgsDependent &&
DoCheckConstraintSatisfaction) {
LocalInstantiationScope Scope(*this);
EnterExpressionEvaluationContext EECtx{
*this, ExpressionEvaluationContext::Unevaluated, CSD};
Error = CheckConstraintSatisfaction(
NamedConcept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL,
SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
TemplateArgs->getRAngleLoc()),
Satisfaction, CL);
Satisfaction.ContainsErrors = Error;
}
if (Error)
return ExprError();
return ConceptSpecializationExpr::Create(
Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction);
}
@ -5217,10 +5232,11 @@ bool Sema::CheckTemplateTypeArgument(
}
default: {
// We allow instantiating a template with template argument packs when
// building deduction guides.
// building deduction guides or mapping constraint template parameters.
if (Arg.getKind() == TemplateArgument::Pack &&
CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::BuildingDeductionGuides) {
(CodeSynthesisContexts.back().Kind ==
Sema::CodeSynthesisContext::BuildingDeductionGuides ||
inParameterMappingSubstitution())) {
SugaredConverted.push_back(Arg);
CanonicalConverted.push_back(Arg);
return false;
@ -5813,6 +5829,20 @@ bool Sema::CheckTemplateArgumentList(
TemplateArgumentListInfo &TemplateArgs, const DefaultArguments &DefaultArgs,
bool PartialTemplateArgs, CheckTemplateArgumentInfo &CTAI,
bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied) {
return CheckTemplateArgumentList(
Template, GetTemplateParameterList(Template), TemplateLoc, TemplateArgs,
DefaultArgs, PartialTemplateArgs, CTAI, UpdateArgsWithConversions,
ConstraintsNotSatisfied);
}
/// Check that the given template argument list is well-formed
/// for specializing the given template.
bool Sema::CheckTemplateArgumentList(
TemplateDecl *Template, TemplateParameterList *Params,
SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs,
const DefaultArguments &DefaultArgs, bool PartialTemplateArgs,
CheckTemplateArgumentInfo &CTAI, bool UpdateArgsWithConversions,
bool *ConstraintsNotSatisfied) {
if (ConstraintsNotSatisfied)
*ConstraintsNotSatisfied = false;
@ -5822,8 +5852,6 @@ bool Sema::CheckTemplateArgumentList(
// template.
TemplateArgumentListInfo NewArgs = TemplateArgs;
TemplateParameterList *Params = GetTemplateParameterList(Template);
SourceLocation RAngleLoc = NewArgs.getRAngleLoc();
// C++23 [temp.arg.general]p1:
@ -6163,11 +6191,12 @@ bool Sema::CheckTemplateArgumentList(
CXXThisScopeRAII Scope(*this, RD, ThisQuals, RD != nullptr);
MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
Template, NewContext, /*Final=*/false, CTAI.CanonicalConverted,
Template, NewContext, /*Final=*/true, CTAI.SugaredConverted,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConceptInstantiation=*/true);
if (EnsureTemplateArgumentListConstraints(
if (!isa<ConceptDecl>(Template) &&
EnsureTemplateArgumentListConstraints(
Template, MLTAL,
SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
if (ConstraintsNotSatisfied)

View File

@ -3206,7 +3206,7 @@ CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template,
// If we don't need to replace the deduced template arguments,
// we can add them immediately as the inner-most argument list.
if (!DeducedArgsNeedReplacement)
Innermost = CanonicalDeducedArgs;
Innermost = SugaredDeducedArgs;
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
Template, Template->getDeclContext(), /*Final=*/false, Innermost,
@ -3218,7 +3218,7 @@ CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template,
// not class-scope explicit specialization, so replace with Deduced Args
// instead of adding to inner-most.
if (!Innermost)
MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs);
MLTAL.replaceInnermostTemplateArguments(Template, SugaredDeducedArgs);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
Info.getLocation(),
@ -3995,11 +3995,12 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
if (CheckFunctionTemplateConstraints(
Info.getLocation(),
FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction))
CTAI.SugaredConverted, Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy(
Context, CTAI.CanonicalConverted));
Info.reset(
TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted),
Info.takeCanonical());
return TemplateDeductionResult::ConstraintsNotSatisfied;
}
}
@ -5167,8 +5168,8 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
/*DefaultArgs=*/{},
/*PartialTemplateArgs=*/false, CTAI))
return true;
MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.CanonicalConverted,
/*Final=*/false);
MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.SugaredConverted,
/*Final=*/true);
// Build up an EvaluationContext with an ImplicitConceptSpecializationDecl so
// that the template arguments of the constraint can be preserved. For
// example:
@ -5182,7 +5183,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
S, Sema::ExpressionEvaluationContext::Unevaluated,
ImplicitConceptSpecializationDecl::Create(
S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(),
CTAI.CanonicalConverted));
CTAI.SugaredConverted));
if (S.CheckConstraintSatisfaction(
Concept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL,
TypeLoc.getLocalSourceRange(), Satisfaction))
@ -6676,10 +6677,11 @@ namespace {
struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor {
llvm::SmallBitVector &Used;
unsigned Depth;
bool VisitDeclRefTypes = true;
MarkUsedTemplateParameterVisitor(llvm::SmallBitVector &Used,
unsigned Depth)
: Used(Used), Depth(Depth) { }
MarkUsedTemplateParameterVisitor(llvm::SmallBitVector &Used, unsigned Depth,
bool VisitDeclRefTypes = true)
: Used(Used), Depth(Depth), VisitDeclRefTypes(VisitDeclRefTypes) {}
bool VisitTemplateTypeParmType(TemplateTypeParmType *T) override {
if (T->getDepth() == Depth)
@ -6700,6 +6702,8 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor {
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(E->getDecl()))
if (NTTP->getDepth() == Depth)
Used[NTTP->getIndex()] = true;
if (VisitDeclRefTypes)
DynamicRecursiveASTVisitor::TraverseType(E->getType());
return true;
}
@ -7043,10 +7047,13 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
break;
case Type::UnaryTransform:
if (!OnlyDeduced)
MarkUsedTemplateParameters(Ctx,
cast<UnaryTransformType>(T)->getUnderlyingType(),
OnlyDeduced, Depth, Used);
if (!OnlyDeduced) {
auto *UTT = cast<UnaryTransformType>(T);
auto Next = UTT->getUnderlyingType();
if (Next.isNull())
Next = UTT->getBaseType();
MarkUsedTemplateParameters(Ctx, Next, OnlyDeduced, Depth, Used);
}
break;
case Type::PackExpansion:
@ -7146,6 +7153,12 @@ Sema::MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced,
::MarkUsedTemplateParameters(Context, E, OnlyDeduced, Depth, Used);
}
void Sema::MarkUsedTemplateParametersForSubsumptionParameterMapping(
const Expr *E, unsigned Depth, llvm::SmallBitVector &Used) {
MarkUsedTemplateParameterVisitor(Used, Depth, /*VisitDeclRefTypes=*/false)
.TraverseStmt(const_cast<Expr *>(E));
}
void
Sema::MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs,
bool OnlyDeduced, unsigned Depth,
@ -7171,6 +7184,14 @@ void Sema::MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
/*OnlyDeduced=*/false, Depth, Used);
}
void Sema::MarkUsedTemplateParameters(
ArrayRef<TemplateArgumentLoc> TemplateArgs, unsigned Depth,
llvm::SmallBitVector &Used) {
for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
::MarkUsedTemplateParameters(Context, TemplateArgs[I].getArgument(),
/*OnlyDeduced=*/false, Depth, Used);
}
void Sema::MarkDeducedTemplateParameters(
ASTContext &Ctx, const FunctionTemplateDecl *FunctionTemplate,
llvm::SmallBitVector &Deduced) {

View File

@ -628,9 +628,14 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
Inst.InstantiationRange = InstantiationRange;
Inst.InConstraintSubstitution =
Inst.Kind == CodeSynthesisContext::ConstraintSubstitution;
if (!SemaRef.CodeSynthesisContexts.empty())
Inst.InParameterMappingSubstitution =
Inst.Kind == CodeSynthesisContext::ParameterMappingSubstitution;
if (!SemaRef.CodeSynthesisContexts.empty()) {
Inst.InConstraintSubstitution |=
SemaRef.CodeSynthesisContexts.back().InConstraintSubstitution;
Inst.InParameterMappingSubstitution |=
SemaRef.CodeSynthesisContexts.back().InParameterMappingSubstitution;
}
Invalid = SemaRef.pushCodeSynthesisContext(Inst);
if (!Invalid) {
@ -1375,6 +1380,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
// Template Instantiation for Types
//===----------------------------------------------------------------------===/
namespace {
class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
const MultiLevelTemplateArgumentList &TemplateArgs;
SourceLocation Loc;
@ -1387,7 +1393,11 @@ namespace {
// Whether an incomplete substituion should be treated as an error.
bool BailOutOnIncomplete;
private:
// Whether to rebuild pack expansion types; We don't do that when
// rebuilding the parameter mapping of a fold expression appearing
// in a constraint expression.
bool BuildPackExpansionTypes = true;
// CWG2770: Function parameters should be instantiated when they are
// needed by a satisfaction check of an atomic constraint or
// (recursively) by another function parameter.
@ -1410,6 +1420,17 @@ namespace {
return EvaluateConstraints;
}
inline static struct ForParameterMappingSubstitution_t {
} ForParameterMappingSubstitution;
TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
SourceLocation Loc,
const MultiLevelTemplateArgumentList &TemplateArgs,
bool BuildPackExpansionTypes)
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
BailOutOnIncomplete(false),
BuildPackExpansionTypes(BuildPackExpansionTypes) {}
/// Determine whether the given type \p T has already been
/// transformed.
///
@ -1444,7 +1465,8 @@ namespace {
bool &ShouldExpand, bool &RetainExpansion,
UnsignedOrNone &NumExpansions) {
if (SemaRef.CurrentInstantiationScope &&
SemaRef.inConstraintSubstitution()) {
(SemaRef.inConstraintSubstitution() ||
SemaRef.inParameterMappingSubstitution())) {
for (UnexpandedParameterPack ParmPack : Unexpanded) {
NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(VD);
@ -1465,10 +1487,10 @@ namespace {
TemplateArgument ForgetPartiallySubstitutedPack() {
TemplateArgument Result;
if (NamedDecl *PartialPack
= SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
MultiLevelTemplateArgumentList &TemplateArgs
= const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
if (NamedDecl *PartialPack = SemaRef.CurrentInstantiationScope
->getPartiallySubstitutedPack()) {
MultiLevelTemplateArgumentList &TemplateArgs =
const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
unsigned Depth, Index;
std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
@ -1488,10 +1510,10 @@ namespace {
if (Arg.isNull())
return;
if (NamedDecl *PartialPack
= SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
MultiLevelTemplateArgumentList &TemplateArgs
= const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
if (NamedDecl *PartialPack = SemaRef.CurrentInstantiationScope
->getPartiallySubstitutedPack()) {
MultiLevelTemplateArgumentList &TemplateArgs =
const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
unsigned Depth, Index;
std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
TemplateArgs.setArgument(Depth, Index, Arg);
@ -1508,9 +1530,9 @@ namespace {
std::move(New);
return Old;
}
void RememberSubstitution(MultiLevelTemplateArgumentList Old) {
const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs) =
std::move(Old);
const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs) = Old;
}
TemplateArgument
@ -1691,6 +1713,24 @@ namespace {
return inherited::TransformTemplateArgument(Input, Output, Uneval);
}
// This has to be here to allow its overload.
ExprResult RebuildPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc,
UnsignedOrNone NumExpansions) {
return inherited::RebuildPackExpansion(Pattern, EllipsisLoc,
NumExpansions);
}
TemplateArgumentLoc RebuildPackExpansion(TemplateArgumentLoc Pattern,
SourceLocation EllipsisLoc,
UnsignedOrNone NumExpansions) {
// We don't rewrite a PackExpansion type when we want to normalize a
// CXXFoldExpr constraint. We'll expand it when evaluating the constraint.
if (BuildPackExpansionTypes)
return inherited::RebuildPackExpansion(Pattern, EllipsisLoc,
NumExpansions);
return Pattern;
}
using TreeTransform::TransformTemplateSpecializationType;
QualType
TransformTemplateSpecializationType(TypeLocBuilder &TLB,
@ -1961,7 +2001,8 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(D);
PVD && SemaRef.CurrentInstantiationScope &&
SemaRef.inConstraintSubstitution() &&
(SemaRef.inConstraintSubstitution() ||
SemaRef.inParameterMappingSubstitution()) &&
maybeInstantiateFunctionParameterToScope(PVD))
return nullptr;
@ -2759,18 +2800,29 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
concepts::NestedRequirement *
TemplateInstantiator::TransformNestedRequirement(
concepts::NestedRequirement *Req) {
if (!Req->isDependent() && !AlwaysRebuild())
return Req;
ASTContext &C = SemaRef.Context;
Expr *Constraint = Req->getConstraintExpr();
ConstraintSatisfaction Satisfaction;
auto NestedReqWithDiag = [&C, this](Expr *E,
ConstraintSatisfaction Satisfaction) {
Satisfaction.IsSatisfied = false;
SmallString<128> Entity;
llvm::raw_svector_ostream OS(Entity);
E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
return new (C) concepts::NestedRequirement(
SemaRef.Context, C.backupStr(Entity), std::move(Satisfaction));
};
if (Req->hasInvalidConstraint()) {
if (AlwaysRebuild())
return RebuildNestedRequirement(Req->getInvalidConstraintEntity(),
Req->getConstraintSatisfaction());
return Req;
}
Sema::InstantiatingTemplate ReqInst(SemaRef,
Req->getConstraintExpr()->getBeginLoc(), Req,
Sema::InstantiatingTemplate::ConstraintsCheck{},
Req->getConstraintExpr()->getSourceRange());
if (!getEvaluateConstraints()) {
ExprResult TransConstraint = TransformExpr(Req->getConstraintExpr());
if (TransConstraint.isInvalid() || !TransConstraint.get())
@ -2783,45 +2835,45 @@ TemplateInstantiator::TransformNestedRequirement(
SemaRef.Context, TransConstraint.get(), Satisfaction);
}
ExprResult TransConstraint;
ConstraintSatisfaction Satisfaction;
TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc());
bool Success;
Expr *NewConstraint;
TemplateDeductionInfo Info(Constraint->getBeginLoc());
{
EnterExpressionEvaluationContext ContextRAII(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
Sema::SFINAETrap Trap(SemaRef);
Sema::InstantiatingTemplate ConstrInst(SemaRef,
Req->getConstraintExpr()->getBeginLoc(), Req, Info,
Req->getConstraintExpr()->getSourceRange());
Sema::InstantiatingTemplate ConstrInst(
SemaRef, Constraint->getBeginLoc(), Req,
Sema::InstantiatingTemplate::ConstraintsCheck(),
Constraint->getSourceRange());
if (ConstrInst.isInvalid())
return nullptr;
llvm::SmallVector<Expr *> Result;
if (!SemaRef.CheckConstraintSatisfaction(
nullptr,
AssociatedConstraint(Req->getConstraintExpr(),
SemaRef.ArgPackSubstIndex),
Result, TemplateArgs, Req->getConstraintExpr()->getSourceRange(),
Satisfaction) &&
!Result.empty())
TransConstraint = Result[0];
assert(!Trap.hasErrorOccurred() && "Substitution failures must be handled "
"by CheckConstraintSatisfaction.");
Sema::SFINAETrap Trap(SemaRef);
Success = !SemaRef.CheckConstraintSatisfaction(
Req, AssociatedConstraint(Constraint, SemaRef.ArgPackSubstIndex),
TemplateArgs, Constraint->getSourceRange(), Satisfaction,
/*TopLevelConceptId=*/nullptr, &NewConstraint);
assert(!Success || !Trap.hasErrorOccurred() &&
"Substitution failures must be handled "
"by CheckConstraintSatisfaction.");
}
ASTContext &C = SemaRef.Context;
if (TransConstraint.isUsable() &&
TransConstraint.get()->isInstantiationDependent())
return new (C) concepts::NestedRequirement(TransConstraint.get());
if (TransConstraint.isInvalid() || !TransConstraint.get() ||
Satisfaction.HasSubstitutionFailure()) {
SmallString<128> Entity;
llvm::raw_svector_ostream OS(Entity);
Req->getConstraintExpr()->printPretty(OS, nullptr,
SemaRef.getPrintingPolicy());
return new (C) concepts::NestedRequirement(
SemaRef.Context, C.backupStr(Entity), Satisfaction);
if (!Success || Satisfaction.HasSubstitutionFailure())
return NestedReqWithDiag(Constraint, Satisfaction);
// FIXME: const correctness
// MLTAL might be dependent.
if (!NewConstraint) {
if (!Satisfaction.IsSatisfied)
return NestedReqWithDiag(Constraint, Satisfaction);
NewConstraint = Constraint;
}
return new (C)
concepts::NestedRequirement(C, TransConstraint.get(), Satisfaction);
return new (C) concepts::NestedRequirement(C, NewConstraint, Satisfaction);
}
TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
@ -3078,7 +3130,7 @@ bool Sema::SubstTypeConstraint(
const ASTTemplateArgumentListInfo *TemplArgInfo =
TC->getTemplateArgsAsWritten();
if (!EvaluateConstraints) {
if (!EvaluateConstraints && !inParameterMappingSubstitution()) {
UnsignedOrNone Index = TC->getArgPackSubstIndex();
if (!Index)
Index = SemaRef.ArgPackSubstIndex;
@ -4378,6 +4430,16 @@ bool Sema::SubstTemplateArguments(
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
}
bool Sema::SubstTemplateArgumentsInParameterMapping(
ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Out, bool BuildPackExpansionTypes) {
TemplateInstantiator Instantiator(
TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc,
TemplateArgs, BuildPackExpansionTypes);
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
}
ExprResult
Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
if (!E)

View File

@ -3722,10 +3722,6 @@ public:
ParentContext);
}
/// Build a new Objective-C boxed expression.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
ExprResult RebuildConceptSpecializationExpr(NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
@ -5110,9 +5106,13 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
typedef TemplateArgumentLocInventIterator<Derived,
TemplateArgument::pack_iterator>
PackLocIterator;
TemplateArgumentListInfo *PackOutput = &Outputs;
TemplateArgumentListInfo New;
if (TransformTemplateArguments(
PackLocIterator(*this, In.getArgument().pack_begin()),
PackLocIterator(*this, In.getArgument().pack_end()), Outputs,
PackLocIterator(*this, In.getArgument().pack_end()), *PackOutput,
Uneval))
return true;
@ -5179,7 +5179,6 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
}
return false;
}
// FIXME: Find ways to reduce code duplication for pack expansions.
@ -6247,7 +6246,7 @@ ParmVarDecl *TreeTransform<Derived>::TransformFunctionTypeParam(
/* DefArg */ nullptr);
newParm->setScopeInfo(OldParm->getFunctionScopeDepth(),
OldParm->getFunctionScopeIndex() + indexAdjustment);
transformedLocalDecl(OldParm, {newParm});
getDerived().transformedLocalDecl(OldParm, {newParm});
return newParm;
}
@ -7082,11 +7081,11 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType(
TypeLocBuilder &TLB,
UnaryTransformTypeLoc TL) {
QualType Result = TL.getType();
TypeSourceInfo *NewBaseTSI = TL.getUnderlyingTInfo();
if (Result->isDependentType()) {
const UnaryTransformType *T = TL.getTypePtr();
TypeSourceInfo *NewBaseTSI =
getDerived().TransformType(TL.getUnderlyingTInfo());
NewBaseTSI = getDerived().TransformType(TL.getUnderlyingTInfo());
if (!NewBaseTSI)
return QualType();
QualType NewBase = NewBaseTSI->getType();
@ -7101,7 +7100,7 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType(
UnaryTransformTypeLoc NewTL = TLB.push<UnaryTransformTypeLoc>(Result);
NewTL.setKWLoc(TL.getKWLoc());
NewTL.setParensRange(TL.getParensRange());
NewTL.setUnderlyingTInfo(TL.getUnderlyingTInfo());
NewTL.setUnderlyingTInfo(NewBaseTSI);
return Result;
}

View File

@ -2424,7 +2424,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
VisitDecl(D);
llvm::SmallVector<TemplateArgument, 4> Args;
for (unsigned I = 0; I < D->NumTemplateArgs; ++I)
Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/true));
Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false));
D->setTemplateArguments(Args);
}

View File

@ -807,15 +807,19 @@ readConstraintSatisfaction(ASTRecordReader &Record) {
if (!Satisfaction.IsSatisfied) {
unsigned NumDetailRecords = Record.readInt();
for (unsigned i = 0; i != NumDetailRecords; ++i) {
if (/* IsDiagnostic */Record.readInt()) {
auto Kind = Record.readInt();
if (Kind == 0) {
SourceLocation DiagLocation = Record.readSourceLocation();
StringRef DiagMessage = C.backupStr(Record.readString());
Satisfaction.Details.emplace_back(
new (C) ConstraintSatisfaction::SubstitutionDiagnostic(
DiagLocation, DiagMessage));
} else
Satisfaction.Details.emplace_back(new (
C) ConstraintSubstitutionDiagnostic(DiagLocation, DiagMessage));
} else if (Kind == 1) {
Satisfaction.Details.emplace_back(Record.readExpr());
} else {
assert(Kind == 2);
Satisfaction.Details.emplace_back(Record.readConceptReference());
}
}
}
return Satisfaction;

View File

@ -482,14 +482,20 @@ addConstraintSatisfaction(ASTRecordWriter &Record,
if (!Satisfaction.IsSatisfied) {
Record.push_back(Satisfaction.NumRecords);
for (const auto &DetailRecord : Satisfaction) {
auto *E = dyn_cast<Expr *>(DetailRecord);
Record.push_back(/* IsDiagnostic */ E == nullptr);
if (E)
Record.AddStmt(E);
else {
auto *Diag = cast<std::pair<SourceLocation, StringRef> *>(DetailRecord);
if (auto *Diag = dyn_cast<const ConstraintSubstitutionDiagnostic *>(
DetailRecord)) {
Record.push_back(/*Kind=*/0);
Record.AddSourceLocation(Diag->first);
Record.AddString(Diag->second);
continue;
}
if (auto *E = dyn_cast<const Expr *>(DetailRecord)) {
Record.push_back(/*Kind=*/1);
Record.AddStmt(const_cast<Expr *>(E));
} else {
Record.push_back(/*Kind=*/2);
auto *CR = cast<const ConceptReference *>(DetailRecord);
Record.AddConceptReference(CR);
}
}
}

View File

@ -20,8 +20,9 @@ struct Foo {
// CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'binary_concept'
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13, col:31> 'bool' Concept {{.*}} 'binary_concept'
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:13:9> col:9
// CHECK-NEXT: | |-TemplateArgument type 'type-parameter-1-0'
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0
// CHECK-NEXT: | |-TemplateArgument type 'R'
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0
// CHECK-NEXT: | | `-TemplateTypeParm {{.*}} 'R'
// CHECK-NEXT: | `-TemplateArgument type 'int'
// CHECK-NEXT: | `-BuiltinType {{.*}} 'int'
// CHECK-NEXT: |-TemplateArgument {{.*}} type 'R'
@ -35,8 +36,9 @@ struct Foo {
// CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'unary_concept'
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13> 'bool'
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:10:9> col:9
// CHECK-NEXT: | `-TemplateArgument type 'type-parameter-1-0'
// CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0
// CHECK-NEXT: | `-TemplateArgument type 'R'
// CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0
// CHECK-NEXT: | `-TemplateTypeParm {{.*}} 'R'
template <unary_concept R>
Foo(R);

View File

@ -185,17 +185,18 @@ void foo() {
// CHECK-NEXT: | |-BinaryOperator {{.*}} 'bool' '&&'
// CHECK-NEXT: | | |-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'invocable'
// CHECK-NEXT: | | | |-ImplicitConceptSpecializationDecl {{.*}}
// CHECK-NEXT: | | | | |-TemplateArgument type 'type-parameter-0-2'
// CHECK-NEXT: | | | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-2' dependent depth 0 index 2
// CHECK-NEXT: | | | | `-TemplateArgument pack '<GH124715::Packs<type-parameter-0-1...>>'
// CHECK-NEXT: | | | | `-TemplateArgument type 'GH124715::Packs<type-parameter-0-1...>'
// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'GH124715::Packs<type-parameter-0-1...>' dependent
// CHECK-NEXT: | | | | |-name: 'GH124715::Packs'
// CHECK-NEXT: | | | | |-TemplateArgument type 'U'
// CHECK-NEXT: | | | | | `-TemplateTypeParmType {{.*}} 'U' dependent depth 0 index 2
// CHECK-NEXT: | | | | | `-TemplateTypeParm {{.*}} 'U'
// CHECK-NEXT: | | | | `-TemplateArgument pack '<Packs<Ts...>>'
// CHECK-NEXT: | | | | `-TemplateArgument type 'Packs<Ts...>'
// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'Packs<Ts...>' dependent
// CHECK-NEXT: | | | | |-name: 'Packs':'GH124715::Packs' qualified
// CHECK-NEXT: | | | | | `-ClassTemplateDecl {{.*}} Packs
// CHECK-NEXT: | | | | `-TemplateArgument pack '<type-parameter-0-1...>'
// CHECK-NEXT: | | | | `-TemplateArgument type 'type-parameter-0-1...'
// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'type-parameter-0-1...' dependent
// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent contains_unexpanded_pack depth 0 index 1 pack
// CHECK-NEXT: | | | | `-TemplateArgument type 'Ts...'
// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'Ts...' dependent
// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack
// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'Ts'
// CHECK-NEXT: | | | |-TemplateArgument {{.*}} type 'U':'type-parameter-0-2'
// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'U' dependent depth 0 index 2
// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'U'

View File

@ -243,19 +243,20 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
// since-cxx20-note@#cwg2565-VC {{because 'b' would be invalid: argument may not have 'void' type}}
template<typename T>
concept ErrorRequires = requires (ErrorRequires auto x) {
concept ErrorRequires = requires (ErrorRequires auto x) { // #cwg2565-expr
// since-cxx20-error@-1 {{a concept definition cannot refer to itself}}
// since-cxx20-note@-2 {{declared here}}
// since-cxx20-error@-3 {{'auto' not allowed in requires expression parameter}}
x;
};
static_assert(ErrorRequires<int>);
// since-cxx20-error@-1 {{static assertion failed}}
// since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// since-cxx20-error@-1 {{static assertion failed}} \
// since-cxx20-note@-1 {{because 'int' does not satisfy 'ErrorRequires'}} \
// since-cxx20-note@#cwg2565-expr {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
template<typename T>
concept NestedErrorInRequires = requires (T x) { // #cwg2565-NEIR
requires requires (NestedErrorInRequires auto y) {
requires requires (NestedErrorInRequires auto y) { // #cwg2565-NEIR-inner
// since-cxx20-error@-1 {{a concept definition cannot refer to itself}}
// since-cxx20-note@#cwg2565-NEIR {{declared here}}
// since-cxx20-error@-3 {{'auto' not allowed in requires expression parameter}}
@ -263,8 +264,9 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
};
};
static_assert(NestedErrorInRequires<int>);
// since-cxx20-error@-1 {{static assertion failed}}
// since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// since-cxx20-error@-1 {{static assertion failed}} \
// since-cxx20-note@-1 {{because 'int' does not satisfy 'NestedErrorInRequires'}} \
// since-cxx20-note-re@#cwg2565-NEIR-inner {{because {{.*}} would be invalid: constraint depends on a previously diagnosed expression}}
#endif
} // namespace cwg2565

View File

@ -140,7 +140,8 @@ concept C7 = sizeof(T) == 1 || sizeof(
::type) == 1;
static_assert(!C6<short>);
static_assert(!C6<char>); // expected-note{{while checking the satisfaction of concept 'C6<char>' requested here}}
static_assert(!C6<char>);
// expected-note@-1 {{while checking the satisfaction of concept 'C6<char>' requested here}}
static_assert(C7<char>);
static_assert(!C7<short>); // expected-note{{while checking the satisfaction of concept 'C7<short>' requested here}}

View File

@ -35,14 +35,14 @@ using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class templ
using r2i3 = r2<D>;
using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}}
template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}}
struct r3 {};
using r3i1 = r3<int>;
using r3i2 = r3<A>;
using r3i3 = r3<A &>;
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}}
// Non-dependent expressions
@ -89,7 +89,7 @@ template<typename T>
concept Large = sizeof(typename remove_reference<T>::type) >= 4;
// expected-note@-1{{because 'sizeof(typename remove_reference<short &>::type) >= 4' (2 >= 4) evaluated to false}}
template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large':}}
template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large'}}
struct r7 {};
using r7i1 = r7<int>;
@ -149,7 +149,7 @@ namespace std_example {
template<typename T> constexpr bool is_same_v<T, T> = true;
template<typename T, typename U> concept same_as = is_same_v<T, U>;
// expected-note@-1 {{because 'is_same_v<int, int *>' evaluated to false}}
// expected-note@-1 {{because 'is_same_v<int, typename std_example::T2::inner>' evaluated to false}}
static_assert(C1<int>);
static_assert(C1<int*>);
@ -160,7 +160,7 @@ namespace std_example {
template<typename T> concept C2 =
requires(T x) {
{*x} -> same_as<typename T::inner>;
// expected-note@-1{{because type constraint 'same_as<int, typename std_example::T2::inner>' was not satisfied:}}
// expected-note@-1{{because 'same_as<int, typename std_example::T2::inner>' evaluated to false}}
// expected-note@-2{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}}
};
@ -173,9 +173,9 @@ namespace std_example {
int operator *() { return 0; }
};
static_assert(C2<T1>);
template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}}
template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'T2' does not satisfy 'C2'}}
using c2c1 = C2_check<int>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}}
using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}}
using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = T2]}}
template<typename T>
void g(T t) noexcept(sizeof(T) == 1) {}

View File

@ -43,11 +43,10 @@ namespace std_example {
requires sizeof(a) == 4; // OK
requires a == 0; // expected-error{{substitution into constraint expression resulted in a non-constant expression}}
// expected-note@-1{{while checking the satisfaction of nested requirement requested here}}
// expected-note@-2{{in instantiation of requirement here}}
// expected-note@-3{{while checking the satisfaction of nested requirement requested here}}
// expected-note@-6{{while substituting template arguments into constraint expression here}}
// expected-note@-5{{function parameter 'a' with unknown value cannot be used in a constant expression}}
// expected-note@-8{{declared here}}
// expected-note@-2{{while checking the satisfaction of nested requirement requested here}}
// expected-note@-5{{while substituting template arguments into constraint expression here}}
// expected-note@-4{{function parameter 'a' with unknown value cannot be used in a constant expression}}
// expected-note@-7{{declared here}}
};
static_assert(C2<int>); // expected-error{{static assertion failed}}
// expected-note@-1{{while checking the satisfaction of concept 'C2<int>' requested here}}
@ -84,31 +83,26 @@ static_assert(Pipes<S>);
static_assert(Pipes<double>);
static_assert(Amps1<S>);
static_assert(!Amps1<double>);
static_assert(Amps1<double>);
static_assert(Amps2<S>);
static_assert(!Amps2<double>);
static_assert(Amps2<double>);
template<class T>
void foo1() requires requires (T x) { // #foo1
void foo1() requires requires (T x) {
requires
True<decltype(x.value)> // #foo1Value
True<decltype(x.value)>
&& True<T>;
} {}
template<class T> void fooPipes() requires Pipes<T> {}
template<class T> void fooAmps1() requires Amps1<T> {} // #fooAmps1
template<class T> void fooAmps1() requires Amps1<T> {}
void foo() {
foo1<S>();
foo1<int>(); // expected-error {{no matching function for call to 'foo1'}}
// expected-note@#foo1Value {{because 'True<decltype(x.value)> && True<T>' would be invalid: member reference base type 'int' is not a structure or union}}
// expected-note@#foo1 {{candidate template ignored: constraints not satisfied [with T = int]}}
foo1<int>();
fooPipes<S>();
fooPipes<int>();
fooAmps1<S>();
fooAmps1<int>(); // expected-error {{no matching function for call to 'fooAmps1'}}
// expected-note@#fooAmps1 {{candidate template ignored: constraints not satisfied [with T = int]}}
// expected-note@#fooAmps1 {{because 'int' does not satisfy 'Amps1'}}
// expected-note@#Amps1 {{because 'True<decltype(x.value)> && True<T> && !False<T>' would be invalid: member reference base type 'int' is not a structure or union}}
fooAmps1<int>();
}
template<class T>
@ -158,15 +152,16 @@ void func() {
// expected-note@#bar {{while substituting template arguments into constraint expression here}}
// expected-note@#bar {{while checking the satisfaction of nested requirement requested here}}
// expected-note@#bar {{candidate template ignored: constraints not satisfied [with T = False]}}
// expected-note@#bar {{because 'X<SubstitutionFailureNestedRequires::ErrorExpressions_NotSF::False>::value' evaluated to false}}
// expected-note@#bar {{because 'X<False>::value' evaluated to false}}
bar<int>();
// expected-error@-1 {{no matching function for call to 'bar'}} \
// expected-note@-1 {{while checking constraint satisfaction for template 'bar<int>' required here}} \
// expected-note@-1 {{while substituting deduced template arguments into function template 'bar' [with T = int]}}
// expected-note@-1 {{while substituting deduced template arguments into function template 'bar' [with T = int]}} \
// expected-note@#bar {{in instantiation of static data member}}
// expected-note@#bar {{in instantiation of requirement here}}
// expected-note@#bar {{while checking the satisfaction of nested requirement requested here}}
// expected-note@#bar {{while substituting template arguments into constraint expression here}}
// expected-note@#bar {{candidate template ignored}}
// expected-error@#X_Value {{type 'int' cannot be used prior to '::' because it has no members}}
}
}

View File

@ -39,14 +39,14 @@ using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class
template<typename T> requires requires { sizeof(T); }
// expected-note@-1{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}}
// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}}
struct r3 {};
using r3i1 = r3<int>;
using r3i2 = r3<A>;
using r3i3 = r3<A &>;
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}}
template<typename T> requires requires (T t) { 0; "a"; (void)'a'; }
struct r4 {};

View File

@ -182,14 +182,14 @@ namespace std_example {
static_assert(C1<has_inner_and_type> && C2<has_inner_and_type> && C3<has_inner_and_type>);
template<C1 T> struct C1_check {};
// expected-note@-1 {{because 'int' does not satisfy 'C1'}}
// expected-note@-2 {{because 'std_example::has_type' does not satisfy 'C1'}}
// expected-note@-2 {{because 'has_type' does not satisfy 'C1'}}
template<C2 T> struct C2_check {};
// expected-note@-1 {{because 'std_example::has_inner' does not satisfy 'C2'}}
// expected-note@-1 {{because 'has_inner' does not satisfy 'C2'}}
template<C3 T> struct C3_check {};
// expected-note@-1 {{because 'void' does not satisfy 'C3'}}
using c1 = C1_check<int>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}}
using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}}
using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}}
using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = has_type]}}
using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = has_inner]}}
using c4 = C3_check<void>; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}}
}
@ -199,10 +199,10 @@ template <typename T> concept C = requires { requires requires { T::a; }; };
// expected-note@-1 {{because 'T::a' would be invalid: no member named 'a' in 'PR48656::T1'}}
template <C...> struct A {};
// expected-note@-1 {{because 'PR48656::T1' does not satisfy 'C'}}
// expected-note@-1 {{because 'T1' does not satisfy 'C'}}
struct T1 {};
template struct A<T1>; // expected-error {{constraints not satisfied for class template 'A' [with $0 = <PR48656::T1>]}}
template struct A<T1>; // expected-error {{constraints not satisfied for class template 'A' [with $0 = <T1>]}}
struct T2 { static constexpr bool a = false; };
template struct A<T2>;

View File

@ -28,9 +28,8 @@ template<typename T> requires requires {
requires S<T>{};
// expected-error@-1{{atomic constraint must be of type 'bool' (found 'S<int>')}}
// expected-note@-2{{while checking the satisfaction}}
// expected-note@-3{{in instantiation of requirement}}
// expected-note@-4{{while checking the satisfaction}}
// expected-note@-6{{while substituting template arguments}}
// expected-note@-3{{while checking the satisfaction of nested requirement}}
// expected-note@-5{{while substituting template arguments}}
// expected-note@#F3INST{{while checking constraint satisfaction}}
// expected-note@#F3INST{{while substituting deduced template arguments into function template 'f3' [with T = int]}}
//

View File

@ -1,21 +1,31 @@
// RUN: %clang_cc1 -std=c++2a -x c++ -verify %s
// RUN: %clang_cc1 -std=c++2c -x c++ -verify %s
template<typename T> concept True = true;
template<typename T> concept Foo = True<T*>;
template<typename T> concept Bar = Foo<T&>;
template<typename T> requires Bar<T> struct S { };
template<typename T> requires Bar<T> && true struct S<T> { };
template<typename T> concept Foo = True<T*>; // #Foo
template<typename T> concept Bar = Foo<T&>; // #Bar
template<typename T> requires Bar<T> struct S { }; // #S
template<typename T> requires Bar<T> && true struct S<T> { }; // #SpecS
// expected-error@-1 {{class template partial specialization is not more specialized than the primary template}}
// expected-error@#Foo 2{{'type name' declared as a pointer to a reference of type 'T &'}}
// expected-note@#SpecS {{while substituting into concept arguments here}}
// expected-note@#S {{while substituting into concept arguments here}}
// expected-note@#Bar 2{{while substituting into concept arguments here}}
// expected-note@#S {{template is declared here}}
template<typename T> concept True2 = sizeof(T) >= 0;
template<typename T> concept Foo2 = True2<T*>;
// expected-error@-1{{'type name' declared as a pointer to a reference of type 'type-parameter-0-0 &'}}
template<typename T> concept Bar2 = Foo2<T&>;
// expected-note@-1{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}}
template<typename T> requires Bar2<T> struct S2 { };
template<typename T> concept Foo2 = True2<T*>; // #Foo2
template<typename T> concept Bar2 = Foo2<T&>; // #Bar2
// expected-note@-1 3{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}}
template<typename T> requires Bar2<T> struct S2 { }; // #SpecS2_1
// expected-note@-1{{template is declared here}}
template<typename T> requires Bar2<T> && true struct S2<T> { };
template<typename T> requires Bar2<T> && true struct S2<T> { }; // #SpecS2_2
// expected-error@-1{{class template partial specialization is not more specialized than the primary template}}
// expected-note@-2{{while calculating associated constraint of template 'S2<T>' here}}
// expected-error@#Foo2{{'type name' declared as a pointer to a reference of type 'T &'}}
namespace type_pack {
template<typename... Args>
@ -71,16 +81,31 @@ namespace non_type_pack {
namespace PR47174 {
// This checks that we don't crash with a failed substitution on the first constrained argument when
// performing normalization.
template <Bar2 T, True U>
template <Bar2 T, True U> // #S3_Header
requires true struct S3; // expected-note {{template is declared here}}
template <True T, True U>
requires true struct S3<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}}
requires true struct S3<T, U>;
// expected-error@-1 {{class template partial specialization is not more specialized than the primary template}}
// expected-error@#Foo2 2{{'type name' declared as a pointer to a reference of type 'T &'}}
// expected-note@#SpecS2_1 {{while substituting into concept arguments here}}
// expected-note@#SpecS2_2 {{while substituting into concept arguments here}}
// expected-note@#S3_Header {{while substituting into concept arguments here}}
// expected-note@#Bar2 {{while substituting into concept arguments here}}
// Same as above, for the second position (but this was already working).
template <True T, Bar2 U>
requires true struct S4; // expected-note {{template is declared here}}
template <True T, Bar2 U> // #S4_Header
requires true struct S4; // #S4
template <True T, True U>
requires true struct S4<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}}
requires true struct S4<T, U>; // #S4-spec
// expected-error@-1 {{class template partial specialization is not more specialized than the primary template}}
// expected-error@#Foo2 {{'type name' declared as a pointer to a reference of type 'U &'}}
// expected-note@#S4_Header {{while substituting into concept arguments here}}
// expected-note@#S4 {{template is declared here}}
// expected-note@#S4 {{similar constraint expressions not considered equivalent}}
// expected-note@#S4-spec {{similar constraint expression here}}
struct X {
template<int> struct Y {
@ -96,7 +121,7 @@ template<class T> requires C1<T> && C2<T> void t1() = delete; // expected-note {
template void t1<X>();
void t1() { t1<X>(); } // expected-error {{call to deleted function 't1'}}
template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}}
template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}}
template<class T> requires C2<T> void t2() {}; // expected-note 2 {{candidate function}}
template void t2<X>(); // expected-error {{partial ordering for explicit instantiation of 't2' is ambiguous}}
void t2() { t2<X>(); } // expected-error {{call to 't2' is ambiguous}}

View File

@ -86,16 +86,18 @@ using f1 = F<int>;
using f2 = F<long>; // expected-error {{constraints not satisfied for alias template 'F' [with T = long]}}
template<typename T, typename... Ts>
concept OneOf = (is_same_v<T, Ts> || ...);
// expected-note@-1 2{{because 'is_same_v<char, char[1]>' evaluated to false}}
// expected-note@-2 2{{and 'is_same_v<char, char[2]>' evaluated to false}}
// expected-note@-3 {{because 'is_same_v<short, int>' evaluated to false}}
// expected-note@-4 {{and 'is_same_v<short, long>' evaluated to false}}
// expected-note@-5 {{and 'is_same_v<short, char>' evaluated to false}}
// expected-note@-6 3{{because 'is_same_v<int, char[1]>' evaluated to false}}
// expected-note@-7 3{{and 'is_same_v<int, char[2]>' evaluated to false}}
// expected-note@-8 2{{because 'is_same_v<std::nullptr_t, char>' evaluated to false}}
// expected-note@-9 2{{and 'is_same_v<std::nullptr_t, int>' evaluated to false}}
concept OneOf = (is_same_v<T, Ts> || ...); // #OneOf
// expected-note@#OneOf 2{{because 'is_same_v<char, char[1]>' evaluated to false}}
// expected-note@#OneOf 2{{and 'is_same_v<char, char[2]>' evaluated to false}}
// expected-note@#OneOf {{because 'is_same_v<short, int>' evaluated to false}}
// expected-note@#OneOf {{and 'is_same_v<short, long>' evaluated to false}}
// expected-note@#OneOf {{and 'is_same_v<short, char>' evaluated to false}}
// expected-note@#OneOf 3{{because 'is_same_v<int, char[1]>' evaluated to false}}
// expected-note@#OneOf 3{{and 'is_same_v<int, char[2]>' evaluated to false}}
// expected-note@#OneOf {{because 'is_same_v<decltype(nullptr), char>' evaluated to false}}
// expected-note@#OneOf {{because 'is_same_v<std::nullptr_t, char>' evaluated to false}}
// expected-note@#OneOf {{and 'is_same_v<std::nullptr_t, int>' evaluated to false}}
// expected-note@#OneOf {{and 'is_same_v<decltype(nullptr), int>' evaluated to false}}
template<OneOf<char[1], char[2]> T, OneOf<int, long, char> U>
// expected-note@-1 2{{because 'OneOf<char, char[1], char[2]>' evaluated to false}}
@ -124,6 +126,7 @@ using I = int;
using i1 = I<1>;
using i2 = I<'a'>;
// FIXME: This crashes with -std=c++2c
using i3 = I<nullptr>;
// expected-error@-1 {{constraints not satisfied for alias template 'I' [with x = nullptr]}}

View File

@ -127,13 +127,12 @@ struct F {
template <typename T>
constexpr int f5() requires C<T> { return 1; } // expected-note {{while checking the satisfaction}}
// expected-note@-1 {{while substituting template arguments}}
// expected-note@-2 {{candidate template ignored}}
// expected-note@-1 {{candidate template ignored}}
template <typename T>
constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}}
// expected-note@-1 4 {{while substituting template arguments}}
// expected-note@-2 {{candidate template ignored}}
constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}} \
// expected-note 4 {{while substituting template arguments}} \
// expected-note {{candidate template ignored}}
static_assert(f5<int>() == 1);
static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint satisfaction}}

View File

@ -1257,13 +1257,13 @@ void f() {
(&A::e)(a, a);
// expected-error@-1 {{no matching function for call to 'e'}} \
// expected-note@#tpl-address-e{{candidate template ignored: constraints not satisfied [with T = A, U = A]}} \
// expected-note@#tpl-address-e{{because '__is_same(tpl_address::A, int)' evaluated to false}}
// expected-note@#tpl-address-e{{because '__is_same(A, int)' evaluated to false}}
(&A::e<A>)(a, 0);
(&A::e<A>)(a, a);
// expected-error@-1 {{no matching function for call to 'e'}} \
// expected-note@#tpl-address-e{{candidate template ignored: constraints not satisfied [with T = A, U = A]}} \
// expected-note@#tpl-address-e{{because '__is_same(tpl_address::A, int)' evaluated to false}}
// expected-note@#tpl-address-e{{because '__is_same(A, int)' evaluated to false}}
(&A::e<A, int>)(a, 0);
@ -1273,12 +1273,12 @@ void f() {
(&A::f<A>)(a);
// expected-error@-1 {{no matching function for call to 'f'}} \
// expected-note@#tpl-address-f{{candidate template ignored: constraints not satisfied [with T = A]}} \
// expected-note@#tpl-address-f{{because '__is_same(tpl_address::A, int)' evaluated to false}}
// expected-note@#tpl-address-f{{because '__is_same(A, int)' evaluated to false}}
(&A::f)(a);
// expected-error@-1 {{no matching function for call to 'f'}} \
// expected-note@#tpl-address-f{{candidate template ignored: constraints not satisfied [with T = A]}} \
// expected-note@#tpl-address-f{{because '__is_same(tpl_address::A, int)' evaluated to false}}
// expected-note@#tpl-address-f{{because '__is_same(A, int)' evaluated to false}}
(&A::g)(a);
(&A::g)(a, 0);

View File

@ -1,7 +1,7 @@
// RUN: %clang_cc1 -std=c++2c -verify %s
template <class T> concept A = true;
template <class T> concept C = A<T> && true;
template <class T> concept A = (T(), true);
template <class T> concept C = A<T> && true; // #C
template <class T> concept D = A<T> && __is_same(T, int);
@ -40,13 +40,23 @@ constexpr int i(T...) { return 1; }; // expected-note {{candidate}}
static_assert(i(0) == 1); // expected-error {{call to 'i' is ambiguous}}
template <class... T> requires (A<T> || ... || true)
constexpr int j(T...) { return 0; };
template <class... T> requires (C<T> && ... && true)
constexpr int j(T...) { return 1; };
template <class... T> requires (A<T> || ... || true) constexpr int j(T...) { return 0; }; // #j1
template <class... T> requires (C<T> && ... && true) constexpr int j(T...) { return 1; }; // #j2
static_assert(j(0) == 1);
// expected-error@-1 {{call to 'j' is ambiguous}}
// expected-note@#j1 {{candidate function [with T = <int>]}}
// expected-note@#j2 {{candidate function [with T = <int>]}}
// expected-note@#j2 {{imilar constraint expressions not considered equivalent}}
// expected-note@#j1 {{similar constraint expression here}}
static_assert(j() == 1);
// expected-error@-1 {{call to 'j' is ambiguous}}
// expected-note@#j1 {{candidate function [with T = <>]}}
// expected-note@#j2 {{candidate function [with T = <>]}}
// expected-note@#j2 {{imilar constraint expressions not considered equivalent}}
// expected-note@#j1 {{similar constraint expression here}}
@ -107,7 +117,7 @@ void test() {
}
namespace substitution {
struct S {
struct S {
using type = int;
};
@ -144,51 +154,69 @@ consteval int Or3() requires (C<typename T::type> || ... || C<typename U::type>)
static_assert(And1<>() == 1);
static_assert(And1<S>() == 1);
static_assert(And1<S, S>() == 1);
// FIXME: The diagnostics are not so great
static_assert(And1<int>() == 1); // expected-error {{no matching function for call to 'And1'}}
// expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and1 {{because substituted constraint expression is ill-formed}}
// expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int>]}}
// expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And1<S, int>() == 1); // expected-error {{no matching function for call to 'And1'}}
// expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and1 {{because substituted constraint expression is ill-formed}}
// expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <S, int>]}}
// expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And1<int, S>() == 1); // expected-error {{no matching function for call to 'And1'}}
// expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and1 {{because substituted constraint expression is ill-formed}}
// expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int, S>]}}
// expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And2<S>() == 2);
static_assert(And2<S, S>() == 2);
static_assert(And2<int>() == 2);
static_assert(And2<int>() == 2); // expected-error {{no matching function for call to 'And2'}}
// expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
// expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And2<int, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
// expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and2 {{because substituted constraint expression is ill-formed}}
// expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}} \
// expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And2<S, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
// expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and2 {{because substituted constraint expression is ill-formed}}
// expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}}
// expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And2<int, S>() == 2); // expected-error {{no matching function for call to 'And2'}}
// expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and2 {{because substituted constraint expression is ill-formed}}
// expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}}
// expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And3<S>() == 3);
static_assert(And3<S, S>() == 3);
static_assert(And3<int>() == 3); // expected-error {{no matching function for call to 'And3'}}
// expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and3 {{because substituted constraint expression is ill-formed}}
// expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
// expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And3<int, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
// expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and3 {{because substituted constraint expression is ill-formed}}
// expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}}
// expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And3<S, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
// expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and3 {{because substituted constraint expression is ill-formed}}
// expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}}
// expected-note@#and3 {{because 'typename U::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And3<int, S>() == 3); // expected-error {{no matching function for call to 'And3'}}
// expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#and3 {{because substituted constraint expression is ill-formed}}
// expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}}
// expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(Or1<>() == 1); // expected-error {{no matching function for call to 'Or1'}}
@ -198,25 +226,26 @@ static_assert(Or1<int, S>() == 1);
static_assert(Or1<S, int>() == 1);
static_assert(Or1<S, S>() == 1);
static_assert(Or1<int>() == 1); // expected-error {{no matching function for call to 'Or1'}}
// expected-note@#or1 {{candidate template ignored: constraints not satisfied}} \
// expected-note@#or1 {{because substituted constraint expression is ill-formed}}
// expected-note@#or1 {{candidate template ignored: constraints not satisfied}}
// expected-note@#or1 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(Or2<S>() == 2);
static_assert(Or2<int, S>() == 2);
static_assert(Or2<S, int>() == 2);
static_assert(Or2<S, S>() == 2);
static_assert(Or2<int>() == 2); // expected-error {{no matching function for call to 'Or2'}}
// expected-note@#or2 {{candidate template ignored: constraints not satisfied}} \
// expected-note@#or2 {{because substituted constraint expression is ill-formed}}
// expected-note@#or2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
// expected-note@#or2 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(Or3<S>() == 3);
static_assert(Or3<int, S>() == 3);
static_assert(Or3<S, int>() == 3);
static_assert(Or3<S, S>() == 3);
static_assert(Or3<int>() == 3); // expected-error {{no matching function for call to 'Or3'}}
// expected-note@#or3 {{candidate template ignored: constraints not satisfied}} \
// expected-note@#or3 {{because substituted constraint expression is ill-formed}}
// expected-note@#or3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#or3 {{because 'typename T::type' does not satisfy 'C'}}
// expected-note@#C {{because 'T' does not satisfy 'A'}}
}
namespace bool_conversion_break {
@ -226,7 +255,7 @@ struct Thingy {
static constexpr int compare(const Thingy&) {return 1;}
};
template <typename ...T, typename ...U>
void f(A<T ...> *, A<U ...> *) // expected-note {{candidate template ignored: failed template argument deduction}}
void f(A<T ...> *, A<U ...> *) // expected-note {{candidate template ignored: constraints not satisfied}}
requires (T::compare(U{}) && ...); // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
void g() {
@ -269,9 +298,7 @@ struct S {
static_assert(S<int>::f<int>() == 2);
static_assert(S<int>::g<int>() == 2); // expected-error {{call to 'g' is ambiguous}}
// expected-note@#nested-ambiguous-g1 {{candidate}}
// expected-note@#nested-ambiguous-g2 {{candidate}}
static_assert(S<int>::g<int>() == 2);
}
@ -384,3 +411,98 @@ struct LazyLitMatrix<index_by<Indices...>, init> {
}
}
namespace GH135190 {
template <typename T>
concept A = __is_same_as(T, int) || __is_same_as(T, double) ;
template <typename T>
concept B = A<T> && __is_same_as(T, double);
template <class... Ts>
requires(A<Ts> && ...)
constexpr int g() {
return 1;
}
template <class... Ts>
requires(B<Ts> && ...)
constexpr int g() {
return 2;
}
static_assert(g<double>() == 2);
template <class... Ts>
concept all_A = (A<Ts> && ...);
template <class... Ts>
concept all_B = (B<Ts> && ...);
template <class... Ts>
requires all_A<Ts...>
constexpr int h() {
return 1;
}
template <class... Ts>
requires all_B<Ts...>
constexpr int h() {
return 2;
}
static_assert(h<double>() == 2);
}
namespace parameter_mapping_regressions {
namespace case1 {
namespace std {
template <class _Tp, class... _Args>
constexpr bool is_constructible_v = __is_constructible(_Tp, _Args...);
template <class _Tp, class... _Args>
concept constructible_from = is_constructible_v<_Tp, _Args...>;
template <class _Tp>
concept default_initializable = true;
template <class> using iterator_t = int;
template <class _Tp>
concept view = constructible_from<_Tp, _Tp>;
template <class... _Views>
requires(view<_Views> && ...)
class zip_transform_view;
} // namespace std
struct IterDefaultCtrView {};
template <class... Views>
using Iter = std::iterator_t<std::zip_transform_view<Views...>>;
static_assert(
std::default_initializable<Iter<IterDefaultCtrView, IterDefaultCtrView>>);
}
namespace case2 {
template <class _Bp>
constexpr bool False = false;
template <class... _Views>
concept __zip_all_random_access = (False<_Views> && ...);
// expected-note@-1 {{evaluated to false}}
template <typename... _Views>
struct zip_view {
void f() requires __zip_all_random_access<_Views...>{};
// expected-note@-1 {{because 'int' does not satisfy}}
};
zip_view<int> test_v;
static_assert(!__zip_all_random_access<int>);
void test() {
test_v.f(); // expected-error {{invalid reference to function 'f'}}
}
}
}

View File

@ -106,7 +106,7 @@ concept BinaryDefaultedFalse = false;
template <template <typename...> concept C, typename T>
struct S {
template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
template <C TT> // expected-note 2{{because 'int' does not satisfy 'UnaryFalse'}}
void f(TT); // expected-note {{ignored}}
void g(C auto); // expected-note {{ignored}} \
// expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
@ -171,7 +171,7 @@ concept BinaryDefaultedFalse = false;
template <template <typename...> concept C, typename T>
struct S {
template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
template <C TT> // expected-note 2{{because 'int' does not satisfy 'UnaryFalse'}}
void f(TT); // expected-note {{ignored}}
void g(C auto); // expected-note {{ignored}} \
// expected-note {{because 'int' does not satisfy 'UnaryFalse'}}

View File

@ -1,6 +1,6 @@
// RUN: %clang -fsyntax-only -std=c++2a -Xclang -verify -ftemplate-depth=5 -ftemplate-backtrace-limit=4 %s
// RequiresExpr contains invalid requirement. (Eg. Highly recurisive template).
// RequiresExpr contains invalid requirement. (Eg. Highly recursive template).
template<int x>
struct A { static constexpr bool far(); };
class B {
@ -19,7 +19,7 @@ constexpr bool A<x>::far() {
// expected-error@#Invalid {{recursive template instantiation exceeded maximum depth}}
// expected-note@#Invalid 3 {{while}}
// expected-note@#Invalid {{contexts in backtrace}}
// expected-note@#Invalid {{increase recursive template instantiation depth}}
// expected-note@#Invalid {{use -ftemplate-depth=N to increase}}
};
}
static_assert(A<1>::far());

View File

@ -102,7 +102,7 @@ static_assert(__is_constructible(Movable, int));
// expected-error@-1 {{no matching constructor for initialization of 'Movable'}} \
// expected-note@-1 2{{}}
// expected-error@#err-self-constraint-1{{satisfaction of constraint '__is_constructible(Movable, T)' depends on itself}}
// expected-note@#err-self-constraint-1 4{{}}
// expected-note@#err-self-constraint-1 3{{}}
// expected-note@#Movable {{'Movable' defined here}}
template <typename T>
@ -200,7 +200,6 @@ void h(short n) { f(n); }
// expected-note@-1{{while checking constraint satisfaction for template}}
// expected-note@#GH62096-note1{{in instantiation}}
// expected-note@#GH62096-note1{{while substituting template arguments into constraint expression here}}
// expected-note@#GH62096-note2{{while substituting template arguments into constraint expression here}}
// expected-note@#GH62096-note2{{while checking the satisfaction of concept}}
// expected-note@#GH62096-err {{expression evaluates}}
}

View File

@ -5129,12 +5129,12 @@ namespace GH121278 {
#if __cplusplus >= 202002L
template <typename B, typename D>
concept C = __is_base_of(B, D);
// expected-error@-1 {{incomplete type 'GH121278::S' used in type trait expression}}
// expected-error@-1 {{incomplete type 'S' used in type trait expression}}
// expected-note@-2 {{while substituting template arguments into constraint expression here}}
struct T;
struct S;
bool b = C<T, S>;
// expected-note@-1 {{while checking the satisfaction of concept 'C<GH121278::T, GH121278::S>' requested here}}
// expected-note@-1 {{while checking the satisfaction of concept 'C<T, S>' requested here}}
#endif
}

View File

@ -19,7 +19,7 @@ Buffer<double2> r4;
// expected-error@+4 {{constraints not satisfied for class template 'Buffer'}}
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class Buffer}}
// expected-note@*:* {{because 'hlsl::Buffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because 'Buffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(hlsl::Buffer<int>)' evaluated to false}}
Buffer<Buffer<int> > r5;
@ -65,7 +65,7 @@ Buffer<half[4]> r10;
typedef vector<int, 8> int8;
// expected-error@+3 {{constraints not satisfied for class template 'Buffer'}}
// expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because 'int8' (aka 'vector<int, 8>') does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<int, 8>)' evaluated to false}}
Buffer<int8> r11;
@ -90,7 +90,7 @@ enum numbers { one, two, three };
Buffer<numbers> r15;
// expected-error@+3 {{constraints not satisfied for class template 'Buffer'}}
// expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because 'double3' (aka 'vector<double, 3>') does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<double, 3>)' evaluated to false}}
Buffer<double3> r16;

View File

@ -19,7 +19,7 @@ RWBuffer<double2> r4;
// expected-error@+4 {{constraints not satisfied for class template 'RWBuffer'}}
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
// expected-note@*:* {{because 'hlsl::RWBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because 'RWBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(hlsl::RWBuffer<int>)' evaluated to false}}
RWBuffer<RWBuffer<int> > r5;
@ -65,7 +65,7 @@ RWBuffer<half[4]> r10;
typedef vector<int, 8> int8;
// expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}}
// expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because 'int8' (aka 'vector<int, 8>') does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<int, 8>)' evaluated to false}}
RWBuffer<int8> r11;
@ -90,7 +90,7 @@ enum numbers { one, two, three };
RWBuffer<numbers> r15;
// expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}}
// expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because 'double3' (aka 'vector<double, 3>') does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<double, 3>)' evaluated to false}}
RWBuffer<double3> r16;

View File

@ -4,7 +4,7 @@
constexpr bool CausesRecoveryExpr = "test" + 1.0f;
template<typename T>
concept ReferencesCRE = CausesRecoveryExpr;
concept ReferencesCRE = CausesRecoveryExpr; // #subst1
template<typename T> requires CausesRecoveryExpr // #NVC1REQ
void NoViableCands1(){} // #NVC1
@ -19,16 +19,18 @@ void NVCUse() {
NoViableCands1<int>();
// expected-error@-1 {{no matching function for call to 'NoViableCands1'}}
// expected-note@#NVC1{{candidate template ignored: constraints not satisfied}}
// expected-note@#NVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#NVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
NoViableCands2<int>();
// expected-error@-1 {{no matching function for call to 'NoViableCands2'}}
// expected-note@#NVC2{{candidate template ignored: constraints not satisfied}}
// expected-note@#NVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
NoViableCands3<int>();
// expected-error@-1 {{no matching function for call to 'NoViableCands3'}}
// expected-note@#NVC3{{candidate template ignored: constraints not satisfied}}
// expected-note@#NVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#NVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
template<typename T> requires CausesRecoveryExpr // #OVC1REQ
@ -58,12 +60,14 @@ void OVCUse() {
// expected-error@-1 {{no matching function for call to 'OtherViableCands2'}}
// expected-note@#OVC2_ALT {{candidate function}}
// expected-note@#OVC2 {{candidate template ignored: constraints not satisfied}}
// expected-note@#OVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#OVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
OtherViableCands3<int>();
// expected-error@-1 {{no matching function for call to 'OtherViableCands3'}}
// expected-note@#OVC3_ALT {{candidate function}}
// expected-note@#OVC3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#OVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#OVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
template<typename T> requires CausesRecoveryExpr // #OBNVC1REQ
@ -95,13 +99,15 @@ void OBNVCUse() {
// expected-note@#OBNVC2_ALT {{candidate template ignored: constraints not satisfied}}
// expected-note@#OBNVC2REQ_ALT {{because 'false' evaluated to false}}
// expected-note@#OBNVC2 {{candidate template ignored: constraints not satisfied}}
// expected-note@#OBNVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#OBNVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
OtherBadNoViableCands3<int>();
// expected-error@-1 {{no matching function for call to 'OtherBadNoViableCands3'}}
// expected-note@#OBNVC3_ALT {{candidate template ignored: constraints not satisfied}}
// expected-note@#OBNVC3REQ_ALT {{because 'false' evaluated to false}}
// expected-note@#OBNVC3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#OBNVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#OBNVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
@ -136,12 +142,14 @@ void MemOVCUse() {
// expected-error@-1 {{no matching member function for call to 'OtherViableCands2'}}
// expected-note@#MEMOVC2_ALT {{candidate function}}
// expected-note@#MEMOVC2 {{candidate template ignored: constraints not satisfied}}
// expected-note@#MEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#MEMOVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
S.OtherViableCands3<int>();
// expected-error@-1 {{no matching member function for call to 'OtherViableCands3'}}
// expected-note@#MEMOVC3_ALT {{candidate function}}
// expected-note@#MEMOVC3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#MEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#MEMOVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
struct StaticOVC {
@ -173,12 +181,14 @@ void StaticMemOVCUse() {
// expected-error@-1 {{no matching function for call to 'OtherViableCands2'}}
// expected-note@#SMEMOVC2_ALT {{candidate function}}
// expected-note@#SMEMOVC2 {{candidate template ignored: constraints not satisfied}}
// expected-note@#SMEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#SMEMOVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
StaticOVC::OtherViableCands3<int>();
// expected-error@-1 {{no matching function for call to 'OtherViableCands3'}}
// expected-note@#SMEMOVC3_ALT {{candidate function}}
// expected-note@#SMEMOVC3 {{candidate template ignored: constraints not satisfied}}
// expected-note@#SMEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#SMEMOVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
namespace GH58548 {

View File

@ -12,7 +12,7 @@ void g() {
// expected-note@#FDEF{{because 'int' does not satisfy 'c'}}
// expected-note@#CDEF{{because 'f(t)' would be invalid: no matching function for call to 'f'}}
}
} // namespace GH53213
} // namespace GH53213
namespace GH45736 {
struct constrained;
@ -67,15 +67,14 @@ struct my_range{
void baz() {
auto it = begin(rng); // #BEGIN_CALL
// expected-error@#INF_BEGIN {{satisfaction of constraint 'Inf<Inf auto>' depends on itself}}
// expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}}
// expected-error-re@#INF_REQ {{satisfaction of constraint {{.*}} depends on itself}}
// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<DirectRecursiveCheck::my_range>' requested here}}
// expected-note@#INF_BEGIN_EXPR {{while checking constraint satisfaction for template 'begin<DirectRecursiveCheck::my_range>' required here}}
// expected-note@#INF_BEGIN_EXPR {{while substituting deduced template arguments into function template 'begin'}}
// expected-note@#INF_BEGIN_EXPR {{in instantiation of requirement here}}
// expected-note@#INF_REQ {{while substituting template arguments into constraint expression here}}
// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<DirectRecursiveCheck::my_range>' requested here}}
// expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}}
// expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin<DirectRecursiveCheck::my_range>' required here}}
// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<struct my_range>' requested here}}
// expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin<struct my_range>' required here}}
// expected-note@#BEGIN_CALL {{while substituting deduced template arguments into function template}}
// Fallout of the failure is failed lookup, which is necessary to stop odd
@ -83,6 +82,7 @@ auto it = begin(rng); // #BEGIN_CALL
// expected-error@#BEGIN_CALL {{no matching function for call to 'begin'}}
// expected-note@#NOTINF_BEGIN {{candidate function}}
// expected-note@#INF_BEGIN{{candidate template ignored: constraints not satisfied}}
// expected-note@#INF_BEGIN{{because 'Inf auto' does not satisfy 'Inf}}
}
} // namespace DirectRecursiveCheck
@ -100,16 +100,17 @@ namespace GH50891 {
static_assert(Numeric<Deferred>); // #STATIC_ASSERT
// expected-error@#NUMERIC{{satisfaction of constraint 'requires (T a) { foo(a); }' depends on itself}}
// expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}}
// expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric<GH50891::Deferred>' requested here}}
// expected-note@#OP_TO {{while substituting template arguments into constraint expression here}}
// expected-note@#FOO_CALL {{while checking constraint satisfaction for template}}
// expected-note@#FOO_CALL {{while substituting deduced template arguments into function template}}
// expected-note@#FOO_CALL {{in instantiation of requirement here}}
// expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric<Deferred>' requested here}}
// expected-note@#OP_TO {{skipping 1 context}}
// expected-note@#FOO_CALL 2{{while checking constraint satisfaction for template}}
// expected-note@#FOO_CALL 2{{while substituting deduced template arguments into function template}}
// expected-note@#FOO_CALL 2{{in instantiation of requirement here}}
// expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}}
// expected-error@#STATIC_ASSERT {{static assertion failed}}
// expected-note@#STATIC_ASSERT{{while checking the satisfaction of concept 'Numeric<GH50891::Deferred>' requested here}}
// expected-note@#STATIC_ASSERT{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
// expected-note@#STATIC_ASSERT{{while checking the satisfaction of concept 'Numeric<Deferred>' requested here}}
// expected-note@#STATIC_ASSERT{{because 'Deferred' does not satisfy 'Numeric'}}
// expected-note@#FOO_CALL{{because 'foo(a)' would be invalid}}
} // namespace GH50891

View File

@ -1002,7 +1002,7 @@ template<class>
concept Irrelevant = false;
template <typename T>
concept ErrorRequires = requires(ErrorRequires auto x) { x; };
concept ErrorRequires = requires(ErrorRequires auto x) { x; }; //#GH54678-ill-formed-concept
// expected-error@-1 {{a concept definition cannot refer to itself}} \
// expected-error@-1 {{'auto' not allowed in requires expression parameter}} \
// expected-note@-1 {{declared here}}
@ -1023,8 +1023,7 @@ template<class T> void eee(T t) // expected-note {{candidate template ignored: c
requires (Irrelevant<T> || Irrelevant<T> || True<T>) && False<T> {} // expected-note {{'long' does not satisfy 'False'}}
template<class T> void fff(T t) // expected-note {{candidate template ignored: constraints not satisfied}}
requires((ErrorRequires<T> || False<T> || True<T>) && False<T>) {} // expected-note {{'unsigned long' does not satisfy 'False'}}
requires((ErrorRequires<T> || False<T> || True<T>) && False<T>) {} // expected-note {{because 'unsigned long' does not satisfy 'False'}}
void test() {
aaa(42); // expected-error {{no matching function}}
bbb(42L); // expected-error{{no matching function}}
@ -1264,12 +1263,7 @@ C auto x = 0;
// expected-error@#T_Type {{type 'int' cannot be used prior to '::'}} \
// expected-note@-1 {{in instantiation of default argument}}
// This will be fixed when we merge https://github.com/llvm/llvm-project/pull/141776
// Which makes us behave like GCC.
static_assert(f(0));
// expected-error@-1 {{no matching function for call}} \
// expected-note@#GH61824_f {{constraints not satisfied}} \
// expected-note@#T_Type {{type 'int' cannot be used prior to '::'}}
}
@ -1278,4 +1272,65 @@ template <typename T> concept PerfectSquare = [](){} // expected-note 2{{here}}
([](auto) { return true; }) < PerfectSquare <class T>;
// expected-error@-1 {{declaration of 'T' shadows template parameter}} \
// expected-error@-1 {{a concept definition cannot refer to itself}}
}
namespace GH61811{
template <class T> struct A { static const int x = 42; };
template <class Ta> concept A42 = A<Ta>::x == 42;
template <class Tv> concept Void = __is_same_as(Tv, void);
template <class Tb, class Ub> concept A42b = Void<Tb> || A42<Ub>;
template <class Tc> concept R42c = A42b<Tc, Tc&>;
static_assert (R42c<void>);
}
namespace parameter_mapping_regressions {
namespace case1 {
template <template <class> class> using __meval = struct __q;
template <template <class> class _Tp>
concept __mvalid = requires { typename __meval<_Tp>; };
template <class _Fn>
concept __minvocable = __mvalid<_Fn::template __f>;
template <class...> struct __mdefer_;
template <class _Fn, class... _Args>
requires __minvocable<_Fn>
struct __mdefer_<_Fn, _Args...> {};
template <class = __q> struct __mtransform {
template <class> using __f = int;
};
struct __completion_domain_or_none_ : __mdefer_<__mtransform<>> {};
}
namespace case2 {
template<auto& Q, class P> concept C = Q.template operator()<P>();
template<class P> concept E = C<[]<class Ty>{ return false; }, P>;
static_assert(!E<int>);
}
namespace case3 {
template <class> constexpr bool is_move_constructible_v = false;
template <class _Tp>
concept __cpp17_move_constructible = is_move_constructible_v<_Tp>; // #is_move_constructible_v
template <class _Tp>
concept __cpp17_copy_constructible = __cpp17_move_constructible<_Tp>; // #__cpp17_move_constructible
template <class _Iter>
concept __cpp17_iterator = __cpp17_copy_constructible<_Iter>; // #__cpp17_copy_constructible
struct not_move_constructible {};
static_assert(__cpp17_iterator<not_move_constructible>); \
// expected-error {{static assertion failed}} \
// expected-note {{because 'not_move_constructible' does not satisfy '__cpp17_iterator'}} \
// expected-note@#__cpp17_copy_constructible {{because 'not_move_constructible' does not satisfy '__cpp17_copy_constructible'}} \
// expected-note@#__cpp17_move_constructible {{because 'parameter_mapping_regressions::case3::not_move_constructible' does not satisfy '__cpp17_move_constructible'}} \
// expected-note@#is_move_constructible_v {{because 'is_move_constructible_v<parameter_mapping_regressions::case3::not_move_constructible>' evaluated to false}}
}
}

View File

@ -574,8 +574,9 @@ static_assert(x.size == 4);
// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'U (&)[3]'
// CHECK-NEXT: | `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True'
// CHECK-NEXT: | |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-0-0'
// CHECK-NEXT: | | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
// CHECK-NEXT: | | `-TemplateArgument type 'T'
// CHECK-NEXT: | | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
// CHECK-NEXT: | | `-TemplateTypeParm 0x{{.+}} 'T'
// CHECK-NEXT: | `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
// CHECK-NEXT: | `-TemplateTypeParm 0x{{.+}} 'T'
@ -588,8 +589,9 @@ static_assert(x.size == 4);
// CHECK-NEXT: |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'double (&)[3]'
// CHECK-NEXT: `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True'
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
// CHECK-NEXT: | `-TemplateArgument type 'type-parameter-0-0'
// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
// CHECK-NEXT: | `-TemplateArgument type 'T'
// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
// CHECK-NEXT: | `-TemplateTypeParm 0x{{.+}} 'T'
// CHECK-NEXT: `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
// CHECK-NEXT: `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
// CHECK-NEXT: `-TemplateTypeParm 0x{{.+}} 'T'
@ -660,8 +662,9 @@ Test test(42);
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} Concept {{.*}} 'Constraint' depth 0 index 1 auto:1
// CHECK-NEXT: | `-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'Constraint'
// CHECK-NEXT: | |-ImplicitConceptSpecializationDecl {{.*}}
// CHECK-NEXT: | | |-TemplateArgument type 'type-parameter-0-1'
// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent depth 0 index 1
// CHECK-NEXT: | | |-TemplateArgument type 'auto:1'
// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'auto:1' dependent depth 0 index 1
// CHECK-NEXT: | | | `-TemplateTypeParm {{.*}} 'auto:1'
// CHECK-NEXT: | | `-TemplateArgument type 'int'
// CHECK-NEXT: | | `-BuiltinType {{.*}} 'int'
// CHECK-NEXT: | |-TemplateArgument {{.*}} type 'auto:1':'type-parameter-0-1'

View File

@ -1,5 +1,6 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
template<typename...>
concept C = false; // expected-note 9{{because}}

View File

@ -7,8 +7,7 @@ template<typename T>
constexpr bool is_same_v<T, T> = true;
template<typename T, typename U>
concept same_as = is_same_v<T, U>;
// expected-note@-1{{because 'is_same_v<int, bool>' evaluated to false}}
concept same_as = is_same_v<T, U>; //#is_same_v
template<typename T, typename... Us>
concept either = (is_same_v<T, Us> || ...);
@ -17,6 +16,7 @@ template<typename... Ts>
struct T {
template<same_as<Ts>... Us>
// expected-note@-1{{because 'same_as<int, bool>' evaluated to false}}
// expected-note@#is_same_v{{because 'is_same_v<int, bool>' evaluated to false}}
static void foo(Us... u, int x) { };
// expected-note@-1{{candidate template ignored: deduced too few arguments}}
// expected-note@-2{{candidate template ignored: constraints not satisfied}}

View File

@ -72,12 +72,12 @@ namespace type_requirement {
template<typename T> requires
false_v<requires { typename T::template temp<T>; }>
// expected-note@-1 {{because 'false_v<requires { typename type_requirement::contains_template<int>::template temp<type_requirement::contains_template<int>>; }>' evaluated to false}}
// expected-note@-2 {{because 'false_v<requires { typename type_requirement::contains_template<short>::template temp<type_requirement::contains_template<short>>; }>' evaluated to false}}
// expected-note@-1 {{because 'false_v<requires { typename contains_template<int>::template temp<contains_template<int>>; }>' evaluated to false}}
// expected-note@-2 {{because 'false_v<requires { typename contains_template<short>::template temp<contains_template<short>>; }>' evaluated to false}}
struct r2 {};
using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<int>]}}
using r2i2 = r2<contains_template<short>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<short>]}}
using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = contains_template<int>]}}
using r2i2 = r2<contains_template<short>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = contains_template<short>]}}
// substitution error occurs, then requires expr is instantiated again
@ -108,7 +108,7 @@ namespace type_requirement {
// expected-note@-1 {{because 'false_v<requires { <<error-type>>; } && requires { <<error-type>>; }>' evaluated to false}}
struct r7 {};
using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, type_requirement::A>]}}
using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, A>]}}
}
namespace expr_requirement {
@ -268,3 +268,13 @@ struct Foo {
};
} // namespace GH110785
namespace sugared_instantiation {
template <class C1> concept C = requires { C1{}; };
template <class D1> concept D = requires { new D1; };
// Test that 'deduced auto' doesn't get confused with 'undeduced auto'.
auto f() { return 0; }
static_assert(requires { { f() } -> C; });
static_assert(requires { { f() } -> D; });
} // namespace sugared_instantiation

View File

@ -1,4 +1,6 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify=expected,cxx20
// RUN: %clang_cc1 -std=c++2c -x c++ %s -verify
template<auto T, decltype(T) U>
concept C1 = sizeof(U) >= 4;
@ -9,20 +11,101 @@ concept C2 = C1<Y{}, V>;
// sizeof(U) >= 4 [U = V (decltype(Y{}))]
template<char W>
constexpr int foo() requires C2<int, W> { return 1; }
constexpr int foo() requires C2<int, W> { return 1; } // #cand1
// sizeof(U) >= 4 [U = W (decltype(int{}))]
template<char X>
// expected-note@+1{{candidate function}}
constexpr int foo() requires C1<1, X> && true { return 2; }
constexpr int foo() requires C1<1, X> && true { return 2; } // #cand2
// sizeof(U) >= 4 [U = X (decltype(1))]
static_assert(foo<'a'>() == 2);
template<char Z>
// expected-note@+1{{candidate function}}
constexpr int foo() requires C2<long long, Z> && true { return 3; }
constexpr int foo() requires C2<long long, Z> && true { return 3; } // #cand3
// sizeof(U) >= 4 [U = Z (decltype(long long{}))]
static_assert(foo<'a'>() == 3);
// expected-error@-1{{call to 'foo' is ambiguous}}
// expected-error@-1{{call to 'foo' is ambiguous}}
// expected-note@#cand2 {{candidate function}}
// expected-note@#cand3 {{candidate function}}
namespace case1 {
template<auto T, decltype(T) U>
concept C1 = sizeof(T) >= 4; // #case1_C1
template<typename Y, char V>
concept C2 = C1<Y{}, V>; // #case1_C2
template<class T, char W>
constexpr int foo() requires C2<T, W> { return 1; } // #case1_foo1
template<class T, char X>
constexpr int foo() requires C1<T{}, X> && true { return 2; } // #case1_foo2
static_assert(foo<char, 'a'>() == 2);
// expected-error@-1{{no matching function for call to 'foo'}}
// expected-note@#case1_foo1{{candidate template ignored: constraints not satisfied [with T = char, W = 'a']}}
// expected-note@#case1_foo1{{because 'C2<char, 'a'>' evaluated to false}}
// expected-note@#case1_C2{{because 'C1<char{}, 'a'>' evaluated to false}}
// expected-note@#case1_C1{{because 'sizeof ('\x00') >= 4' (1 >= 4) evaluated to false}}
// expected-note@#case1_foo2{{candidate template ignored: constraints not satisfied [with T = char, X = 'a']}}
// expected-note@#case1_foo2{{because 'C1<char{}, 'a'>' evaluated to false}}
// expected-note@#case1_C1{{because 'sizeof ('\x00') >= 4' (1 >= 4) evaluated to false}}
static_assert(foo<int, 'a'>() == 2);
}
namespace packs {
template<auto T, decltype(T) U>
concept C1 = sizeof(U) >= 4;
template<typename Y, char V>
concept C2 = C1<Y{}, V>;
template<char... W>
constexpr int foo() requires (C2<int, W> && ...) { return 1; } // #packs-cand1
template<char... X>
constexpr int foo() requires (C1<1, X> && ...) && true { return 2; } // #packs-cand2
static_assert(foo<'a'>() == 2);
// cxx20-error@-1{{call to 'foo' is ambiguous}}
// cxx20-note@#packs-cand1 {{candidate function}}
// cxx20-note@#packs-cand2 {{candidate function}}
}
namespace case2 {
template<auto T> concept C1 = sizeof(decltype(T)) >= 0;
template<typename Y> concept C2 = C1<Y{}>;
template<char W>
constexpr int foo() requires C2<int> { return 1; }
template<char X>
constexpr int foo() requires C1<0> && true { return 2; }
static_assert(foo<0>() == 2);
}
namespace case3 {
template<auto T> concept C1 = sizeof(decltype(T)) >= 0;
template<typename Y> concept C2 = C1<Y{}>;
template<char W>
constexpr int foo() requires C2<int> { return 1; } // #case3_foo1
template<char X>
constexpr int foo() requires C1<1> && true { return 2; } // #case3_foo2
static_assert(foo<0>() == 2);
// expected-error@-1{{call to 'foo' is ambiguous}}
// expected-note@#case3_foo1 {{candidate function}}
// expected-note@#case3_foo2 {{candidate function}}
}

View File

@ -53,7 +53,7 @@ static_assert(!DotFollowingPointer::f(Bad{}), "");
#if __cplusplus >= 202002L
template <class T>
concept C = requires(T t) { t.begin(); };
// cxx20-note@-1 {{because 't.begin()' would be invalid: member reference type 'Holder<Incomplete> *' is a pointer}}
// cxx20-note@-1 {{because 't.begin()' would be invalid: member reference type 'Bad' (aka 'Holder<Incomplete> *') is a pointer}}
static_assert(C<Good>);
static_assert(!C<Bad>);

View File

@ -143,7 +143,7 @@ void check_forward_iterator_requirements() {
// expected-note@*:* {{because 'not_default_constructible' does not satisfy '__cpp17_default_constructible'}}
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(postincrement_not_ref, ""); // expected-error {{static assertion failed}}
#ifndef _AIX
// expected-note-re@*:* {{because type constraint 'convertible_to<{{(valid_iterator<postincrement_not_ref>::)?}}Proxy, const postincrement_not_ref &>' was not satisfied}}
// expected-note-re@*:* {{'convertible_to<{{(valid_iterator<postincrement_not_ref>::)?}}Proxy, const postincrement_not_ref &>'}}
#endif
}
@ -173,7 +173,7 @@ void check_bidirectional_iterator_requirements() {
_LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(missing_postdecrement, ""); // expected-error {{static assertion failed}}
// expected-note@*:* {{cannot decrement value of type 'missing_postdecrement'}}
_LIBCPP_REQUIRE_CPP17_BIDIRECTIONAL_ITERATOR(not_returning_iter_reference, ""); // expected-error {{static assertion failed}}
// expected-note-re@*:* {{because type constraint 'same_as<int, __iter_reference<not_returning_iter_reference>{{ ?}}>' was not satisfied}}
// expected-note-re@*:* {{'same_as<int, __iter_reference<not_returning_iter_reference>{{ ?}}>'}}
// clang-format on
}