[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:
parent
fa57ce980a
commit
e9972debc9
@ -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
|
||||
|
||||
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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{}; }
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
@ -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(),
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}}
|
||||
|
||||
|
||||
@ -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) {}
|
||||
|
||||
@ -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}}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {};
|
||||
|
||||
@ -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>;
|
||||
|
||||
@ -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]}}
|
||||
//
|
||||
|
||||
@ -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}}
|
||||
|
||||
@ -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]}}
|
||||
|
||||
|
||||
@ -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}}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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'}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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'}}
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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}}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
|
||||
|
||||
|
||||
template<typename...>
|
||||
concept C = false; // expected-note 9{{because}}
|
||||
|
||||
|
||||
@ -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}}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}}
|
||||
}
|
||||
|
||||
@ -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>);
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user