forked from OSchip/llvm-project
[Concepts] Function trailing requires clauses
Function trailing requires clauses now parsed, supported in overload resolution and when calling, referencing and taking the address of functions or function templates. Differential Revision: https://reviews.llvm.org/D43357
This commit is contained in:
parent
9c91d79dad
commit
b65b1f322b
|
@ -64,6 +64,17 @@ inline bool isGenericLambdaCallOperatorSpecialization(DeclContext *DC) {
|
|||
dyn_cast<CXXMethodDecl>(DC));
|
||||
}
|
||||
|
||||
inline bool isGenericLambdaCallOperatorOrStaticInvokerSpecialization(
|
||||
DeclContext *DC) {
|
||||
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC);
|
||||
if (!MD) return false;
|
||||
const CXXRecordDecl *LambdaClass = MD->getParent();
|
||||
if (LambdaClass && LambdaClass->isGenericLambda())
|
||||
return (isLambdaCallOperator(MD) || MD->isLambdaStaticInvoker()) &&
|
||||
MD->isFunctionTemplateSpecialization();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// This returns the parent DeclContext ensuring that the correct
|
||||
// parent DeclContext is returned for Lambdas
|
||||
|
|
|
@ -384,6 +384,9 @@ public:
|
|||
for (const auto *Parameter : D->parameters())
|
||||
Visit(Parameter);
|
||||
|
||||
if (const Expr *TRC = D->getTrailingRequiresClause())
|
||||
Visit(TRC);
|
||||
|
||||
if (const auto *C = dyn_cast<CXXConstructorDecl>(D))
|
||||
for (const auto *I : C->inits())
|
||||
Visit(I);
|
||||
|
|
|
@ -669,10 +669,12 @@ struct QualifierInfo {
|
|||
/// Represents a ValueDecl that came out of a declarator.
|
||||
/// Contains type source information through TypeSourceInfo.
|
||||
class DeclaratorDecl : public ValueDecl {
|
||||
// A struct representing both a TInfo and a syntactic qualifier,
|
||||
// to be used for the (uncommon) case of out-of-line declarations.
|
||||
// A struct representing a TInfo, a trailing requires-clause and a syntactic
|
||||
// qualifier, to be used for the (uncommon) case of out-of-line declarations
|
||||
// and constrained function decls.
|
||||
struct ExtInfo : public QualifierInfo {
|
||||
TypeSourceInfo *TInfo;
|
||||
Expr *TrailingRequiresClause = nullptr;
|
||||
};
|
||||
|
||||
llvm::PointerUnion<TypeSourceInfo *, ExtInfo *> DeclInfo;
|
||||
|
@ -739,6 +741,21 @@ public:
|
|||
|
||||
void setQualifierInfo(NestedNameSpecifierLoc QualifierLoc);
|
||||
|
||||
/// \brief Get the constraint-expression introduced by the trailing
|
||||
/// requires-clause in the function/member declaration, or null if no
|
||||
/// requires-clause was provided.
|
||||
Expr *getTrailingRequiresClause() {
|
||||
return hasExtInfo() ? getExtInfo()->TrailingRequiresClause
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
const Expr *getTrailingRequiresClause() const {
|
||||
return hasExtInfo() ? getExtInfo()->TrailingRequiresClause
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void setTrailingRequiresClause(Expr *TrailingRequiresClause);
|
||||
|
||||
unsigned getNumTemplateParameterLists() const {
|
||||
return hasExtInfo() ? getExtInfo()->NumTemplParamLists : 0;
|
||||
}
|
||||
|
@ -1903,7 +1920,8 @@ protected:
|
|||
FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
|
||||
const DeclarationNameInfo &NameInfo, QualType T,
|
||||
TypeSourceInfo *TInfo, StorageClass S, bool isInlineSpecified,
|
||||
ConstexprSpecKind ConstexprKind);
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause = nullptr);
|
||||
|
||||
using redeclarable_base = Redeclarable<FunctionDecl>;
|
||||
|
||||
|
@ -1938,11 +1956,12 @@ public:
|
|||
SourceLocation NLoc, DeclarationName N, QualType T,
|
||||
TypeSourceInfo *TInfo, StorageClass SC, bool isInlineSpecified = false,
|
||||
bool hasWrittenPrototype = true,
|
||||
ConstexprSpecKind ConstexprKind = CSK_unspecified) {
|
||||
ConstexprSpecKind ConstexprKind = CSK_unspecified,
|
||||
Expr *TrailingRequiresClause = nullptr) {
|
||||
DeclarationNameInfo NameInfo(N, NLoc);
|
||||
return FunctionDecl::Create(C, DC, StartLoc, NameInfo, T, TInfo, SC,
|
||||
isInlineSpecified, hasWrittenPrototype,
|
||||
ConstexprKind);
|
||||
ConstexprKind, TrailingRequiresClause);
|
||||
}
|
||||
|
||||
static FunctionDecl *Create(ASTContext &C, DeclContext *DC,
|
||||
|
@ -1950,7 +1969,8 @@ public:
|
|||
const DeclarationNameInfo &NameInfo, QualType T,
|
||||
TypeSourceInfo *TInfo, StorageClass SC,
|
||||
bool isInlineSpecified, bool hasWrittenPrototype,
|
||||
ConstexprSpecKind ConstexprKind);
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause);
|
||||
|
||||
static FunctionDecl *CreateDeserialized(ASTContext &C, unsigned ID);
|
||||
|
||||
|
@ -2352,6 +2372,17 @@ public:
|
|||
/// the target functionality.
|
||||
bool isTargetMultiVersion() const;
|
||||
|
||||
/// \brief Get the associated-constraints of this function declaration.
|
||||
/// Currently, this will either be a vector of size 1 containing the
|
||||
/// trailing-requires-clause or an empty vector.
|
||||
///
|
||||
/// Use this instead of getTrailingRequiresClause for concepts APIs that
|
||||
/// accept an ArrayRef of constraint expressions.
|
||||
void getAssociatedConstraints(SmallVectorImpl<const Expr *> &AC) const {
|
||||
if (auto *TRC = getTrailingRequiresClause())
|
||||
AC.push_back(TRC);
|
||||
}
|
||||
|
||||
void setPreviousDeclaration(FunctionDecl * PrevDecl);
|
||||
|
||||
FunctionDecl *getCanonicalDecl() override;
|
||||
|
|
|
@ -1905,9 +1905,10 @@ protected:
|
|||
SourceLocation StartLoc, const DeclarationNameInfo &NameInfo,
|
||||
QualType T, TypeSourceInfo *TInfo, StorageClass SC,
|
||||
bool isInline, ConstexprSpecKind ConstexprKind,
|
||||
SourceLocation EndLocation)
|
||||
SourceLocation EndLocation,
|
||||
Expr *TrailingRequiresClause = nullptr)
|
||||
: FunctionDecl(DK, C, RD, StartLoc, NameInfo, T, TInfo, SC, isInline,
|
||||
ConstexprKind) {
|
||||
ConstexprKind, TrailingRequiresClause) {
|
||||
if (EndLocation.isValid())
|
||||
setRangeEnd(EndLocation);
|
||||
}
|
||||
|
@ -1918,7 +1919,8 @@ public:
|
|||
const DeclarationNameInfo &NameInfo, QualType T,
|
||||
TypeSourceInfo *TInfo, StorageClass SC,
|
||||
bool isInline, ConstexprSpecKind ConstexprKind,
|
||||
SourceLocation EndLocation);
|
||||
SourceLocation EndLocation,
|
||||
Expr *TrailingRequiresClause = nullptr);
|
||||
|
||||
static CXXMethodDecl *CreateDeserialized(ASTContext &C, unsigned ID);
|
||||
|
||||
|
@ -2363,7 +2365,8 @@ class CXXConstructorDecl final
|
|||
const DeclarationNameInfo &NameInfo, QualType T,
|
||||
TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline,
|
||||
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
|
||||
InheritedConstructor Inherited);
|
||||
InheritedConstructor Inherited,
|
||||
Expr *TrailingRequiresClause);
|
||||
|
||||
void anchor() override;
|
||||
|
||||
|
@ -2416,7 +2419,8 @@ public:
|
|||
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
|
||||
ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared,
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
InheritedConstructor Inherited = InheritedConstructor());
|
||||
InheritedConstructor Inherited = InheritedConstructor(),
|
||||
Expr *TrailingRequiresClause = nullptr);
|
||||
|
||||
ExplicitSpecifier getExplicitSpecifier() {
|
||||
return getCanonicalDecl()->getExplicitSpecifierInternal();
|
||||
|
@ -2623,9 +2627,11 @@ class CXXDestructorDecl : public CXXMethodDecl {
|
|||
CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
|
||||
const DeclarationNameInfo &NameInfo, QualType T,
|
||||
TypeSourceInfo *TInfo, bool isInline,
|
||||
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind)
|
||||
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause = nullptr)
|
||||
: CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo,
|
||||
SC_None, isInline, ConstexprKind, SourceLocation()) {
|
||||
SC_None, isInline, ConstexprKind, SourceLocation(),
|
||||
TrailingRequiresClause) {
|
||||
setImplicit(isImplicitlyDeclared);
|
||||
}
|
||||
|
||||
|
@ -2637,7 +2643,8 @@ public:
|
|||
const DeclarationNameInfo &NameInfo,
|
||||
QualType T, TypeSourceInfo *TInfo,
|
||||
bool isInline, bool isImplicitlyDeclared,
|
||||
ConstexprSpecKind ConstexprKind);
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause = nullptr);
|
||||
static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID);
|
||||
|
||||
void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);
|
||||
|
@ -2676,9 +2683,11 @@ class CXXConversionDecl : public CXXMethodDecl {
|
|||
CXXConversionDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
|
||||
const DeclarationNameInfo &NameInfo, QualType T,
|
||||
TypeSourceInfo *TInfo, bool isInline, ExplicitSpecifier ES,
|
||||
ConstexprSpecKind ConstexprKind, SourceLocation EndLocation)
|
||||
ConstexprSpecKind ConstexprKind, SourceLocation EndLocation,
|
||||
Expr *TrailingRequiresClause = nullptr)
|
||||
: CXXMethodDecl(CXXConversion, C, RD, StartLoc, NameInfo, T, TInfo,
|
||||
SC_None, isInline, ConstexprKind, EndLocation),
|
||||
SC_None, isInline, ConstexprKind, EndLocation,
|
||||
TrailingRequiresClause),
|
||||
ExplicitSpec(ES) {}
|
||||
void anchor() override;
|
||||
|
||||
|
@ -2694,7 +2703,7 @@ public:
|
|||
Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
|
||||
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
|
||||
bool isInline, ExplicitSpecifier ES, ConstexprSpecKind ConstexprKind,
|
||||
SourceLocation EndLocation);
|
||||
SourceLocation EndLocation, Expr *TrailingRequiresClause = nullptr);
|
||||
static CXXConversionDecl *CreateDeserialized(ASTContext &C, unsigned ID);
|
||||
|
||||
ExplicitSpecifier getExplicitSpecifier() {
|
||||
|
|
|
@ -2030,6 +2030,11 @@ bool RecursiveASTVisitor<Derived>::TraverseFunctionHelper(FunctionDecl *D) {
|
|||
}
|
||||
}
|
||||
|
||||
// Visit the trailing requires clause, if any.
|
||||
if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) {
|
||||
TRY_TO(TraverseStmt(TrailingRequiresClause));
|
||||
}
|
||||
|
||||
if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) {
|
||||
// Constructor initializers.
|
||||
for (auto *I : Ctor->inits()) {
|
||||
|
|
|
@ -181,6 +181,13 @@ def err_function_declared_typedef : Error<
|
|||
def err_at_defs_cxx : Error<"@defs is not supported in Objective-C++">;
|
||||
def err_at_in_class : Error<"unexpected '@' in member specification">;
|
||||
def err_unexpected_semi : Error<"unexpected ';' before %0">;
|
||||
def err_unparenthesized_non_primary_expr_in_requires_clause : Error<
|
||||
"parentheses are required around this expression in a requires clause">;
|
||||
def note_unparenthesized_non_primary_expr_in_requires_clause : Note<
|
||||
"parentheses are required around this expression in a requires clause">;
|
||||
def err_potential_function_call_in_constraint_logical_or : Error<
|
||||
"function call must be parenthesized to be considered part of the requires "
|
||||
"clause">;
|
||||
|
||||
def err_expected_fn_body : Error<
|
||||
"expected function body after function declarator">;
|
||||
|
@ -309,6 +316,12 @@ def err_init_list_bin_op : Error<"initializer list cannot be used on the "
|
|||
def warn_cxx98_compat_trailing_return_type : Warning<
|
||||
"trailing return types are incompatible with C++98">,
|
||||
InGroup<CXX98Compat>, DefaultIgnore;
|
||||
def err_requires_clause_must_appear_after_trailing_return : Error<
|
||||
"trailing return type must appear before trailing requires clause">;
|
||||
def err_requires_clause_on_declarator_not_declaring_a_function : Error<
|
||||
"trailing requires clause can only be used when declaring a function">;
|
||||
def err_requires_clause_inside_parens : Error<
|
||||
"trailing requires clause should be placed outside parentheses">;
|
||||
def ext_auto_storage_class : ExtWarn<
|
||||
"'auto' storage class specifier is not permitted in C++11, and will not "
|
||||
"be supported in future releases">, InGroup<DiagGroup<"auto-storage-class">>;
|
||||
|
@ -880,7 +893,7 @@ def warn_cxx98_compat_lambda : Warning<
|
|||
InGroup<CXX98Compat>, DefaultIgnore;
|
||||
def err_lambda_missing_parens : Error<
|
||||
"lambda requires '()' before %select{'mutable'|return type|"
|
||||
"attribute specifier|'constexpr'|'consteval'}0">;
|
||||
"attribute specifier|'constexpr'|'consteval'|'requires' clause}0">;
|
||||
def err_lambda_decl_specifier_repeated : Error<
|
||||
"%select{'mutable'|'constexpr'|'consteval'}0 cannot appear multiple times in a lambda declarator">;
|
||||
def err_lambda_capture_misplaced_ellipsis : Error<
|
||||
|
|
|
@ -2596,6 +2596,18 @@ def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
|
|||
"%select{and |because }0%1 does not satisfy %2">;
|
||||
def note_atomic_constraint_evaluated_to_false_elaborated : Note<
|
||||
"%select{and |because }0'%1' (%2 %3 %4) evaluated to false">;
|
||||
def err_constrained_virtual_method : Error<
|
||||
"virtual function cannot have a requires clause">;
|
||||
def err_trailing_requires_clause_on_deduction_guide : Error<
|
||||
"deduction guide cannot have a requires clause">;
|
||||
def err_reference_to_function_with_unsatisfied_constraints : Error<
|
||||
"invalid reference to function %0: constraints not satisfied">;
|
||||
def note_ambiguous_atomic_constraints : Note<
|
||||
"similar constraint expressions not considered equivalent; constraint "
|
||||
"expressions cannot be considered equivalent unless they originate from the "
|
||||
"same concept">;
|
||||
def note_ambiguous_atomic_constraints_similar_expression : Note<
|
||||
"similar constraint expression here">;
|
||||
|
||||
def err_template_different_requires_clause : Error<
|
||||
"requires clause differs in template redeclaration">;
|
||||
|
@ -3920,6 +3932,9 @@ def note_ovl_candidate_disabled_by_extension : Note<
|
|||
def err_addrof_function_disabled_by_enable_if_attr : Error<
|
||||
"cannot take address of function %0 because it has one or more "
|
||||
"non-tautological enable_if conditions">;
|
||||
def err_addrof_function_constraints_not_satisfied : Error<
|
||||
"cannot take address of function %0 because its constraints are not "
|
||||
"satisfied">;
|
||||
def note_addrof_ovl_candidate_disabled_by_enable_if_attr : Note<
|
||||
"candidate function made ineligible by enable_if">;
|
||||
def note_ovl_candidate_deduced_mismatch : Note<
|
||||
|
@ -4033,6 +4048,9 @@ def note_ovl_candidate_bad_target : Note<
|
|||
"call to "
|
||||
"%select{__device__|__global__|__host__|__host__ __device__|invalid}3 function from"
|
||||
" %select{__device__|__global__|__host__|__host__ __device__|invalid}4 function">;
|
||||
def note_ovl_candidate_constraints_not_satisfied : Note<
|
||||
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: constraints "
|
||||
"not satisfied">;
|
||||
def note_implicit_member_target_infer_collision : Note<
|
||||
"implicit %sub{select_special_member_kind}0 inferred target collision: call to both "
|
||||
"%select{__device__|__global__|__host__|__host__ __device__}1 and "
|
||||
|
|
|
@ -1679,6 +1679,9 @@ public:
|
|||
ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast);
|
||||
ExprResult ParseCaseExpression(SourceLocation CaseLoc);
|
||||
ExprResult ParseConstraintExpression();
|
||||
ExprResult
|
||||
ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause);
|
||||
ExprResult ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause);
|
||||
// Expr that doesn't include commas.
|
||||
ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
|
||||
|
||||
|
@ -1693,15 +1696,23 @@ private:
|
|||
|
||||
ExprResult ParseRHSOfBinaryExpression(ExprResult LHS,
|
||||
prec::Level MinPrec);
|
||||
ExprResult ParseCastExpression(bool isUnaryExpression,
|
||||
/// Control what ParseCastExpression will parse.
|
||||
enum CastParseKind {
|
||||
AnyCastExpr = 0,
|
||||
UnaryExprOnly,
|
||||
PrimaryExprOnly
|
||||
};
|
||||
ExprResult ParseCastExpression(CastParseKind ParseKind,
|
||||
bool isAddressOfOperand,
|
||||
bool &NotCastExpr,
|
||||
TypeCastState isTypeCast,
|
||||
bool isVectorLiteral = false);
|
||||
ExprResult ParseCastExpression(bool isUnaryExpression,
|
||||
bool isVectorLiteral = false,
|
||||
bool *NotPrimaryExpression = nullptr);
|
||||
ExprResult ParseCastExpression(CastParseKind ParseKind,
|
||||
bool isAddressOfOperand = false,
|
||||
TypeCastState isTypeCast = NotTypeCast,
|
||||
bool isVectorLiteral = false);
|
||||
bool isVectorLiteral = false,
|
||||
bool *NotPrimaryExpression = nullptr);
|
||||
|
||||
/// Returns true if the next token cannot start an expression.
|
||||
bool isNotExpressionStart();
|
||||
|
@ -1909,6 +1920,11 @@ private:
|
|||
|
||||
ExprResult ParseCoyieldExpression();
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C++ Concepts
|
||||
|
||||
void ParseTrailingRequiresClause(Declarator &D);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C99 6.7.8: Initialization.
|
||||
|
||||
|
@ -2730,6 +2746,9 @@ private:
|
|||
BalancedDelimiterTracker &Tracker,
|
||||
bool IsAmbiguous,
|
||||
bool RequiresArg = false);
|
||||
void InitCXXThisScopeForDeclaratorIfRelevant(
|
||||
const Declarator &D, const DeclSpec &DS,
|
||||
llvm::Optional<Sema::CXXThisScopeRAII> &ThisScope);
|
||||
bool ParseRefQualifier(bool &RefQualifierIsLValueRef,
|
||||
SourceLocation &RefQualifierLoc);
|
||||
bool isFunctionDeclaratorIdentifierList();
|
||||
|
@ -2837,10 +2856,11 @@ private:
|
|||
Decl *TagDecl);
|
||||
ExprResult ParseCXXMemberInitializer(Decl *D, bool IsFunction,
|
||||
SourceLocation &EqualLoc);
|
||||
bool ParseCXXMemberDeclaratorBeforeInitializer(Declarator &DeclaratorInfo,
|
||||
VirtSpecifiers &VS,
|
||||
ExprResult &BitfieldSize,
|
||||
LateParsedAttrList &LateAttrs);
|
||||
bool
|
||||
ParseCXXMemberDeclaratorBeforeInitializer(Declarator &DeclaratorInfo,
|
||||
VirtSpecifiers &VS,
|
||||
ExprResult &BitfieldSize,
|
||||
LateParsedAttrList &LateAttrs);
|
||||
void MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(Declarator &D,
|
||||
VirtSpecifiers &VS);
|
||||
DeclGroupPtrTy ParseCXXClassMemberDeclaration(
|
||||
|
|
|
@ -1826,6 +1826,10 @@ private:
|
|||
/// The asm label, if specified.
|
||||
Expr *AsmLabel;
|
||||
|
||||
/// \brief The constraint-expression specified by the trailing
|
||||
/// requires-clause, or null if no such clause was specified.
|
||||
Expr *TrailingRequiresClause;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
union {
|
||||
#endif
|
||||
|
@ -1855,7 +1859,8 @@ public:
|
|||
GroupingParens(false), FunctionDefinition(FDK_Declaration),
|
||||
Redeclaration(false), Extension(false), ObjCIvar(false),
|
||||
ObjCWeakProperty(false), InlineStorageUsed(false),
|
||||
Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr) {}
|
||||
Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr),
|
||||
TrailingRequiresClause(nullptr) {}
|
||||
|
||||
~Declarator() {
|
||||
clear();
|
||||
|
@ -2401,6 +2406,22 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
/// \brief Sets a trailing requires clause for this declarator.
|
||||
void setTrailingRequiresClause(Expr *TRC) {
|
||||
TrailingRequiresClause = TRC;
|
||||
}
|
||||
|
||||
/// \brief Sets a trailing requires clause for this declarator.
|
||||
Expr *getTrailingRequiresClause() {
|
||||
return TrailingRequiresClause;
|
||||
}
|
||||
|
||||
/// \brief Determine whether a trailing requires clause was written in this
|
||||
/// declarator.
|
||||
bool hasTrailingRequiresClause() const {
|
||||
return TrailingRequiresClause != nullptr;
|
||||
}
|
||||
|
||||
/// takeAttributes - Takes attributes from the given parsed-attributes
|
||||
/// set and add them to this declarator.
|
||||
///
|
||||
|
|
|
@ -754,7 +754,11 @@ class Sema;
|
|||
/// This constructor/conversion candidate fail due to an address space
|
||||
/// mismatch between the object being constructed and the overload
|
||||
/// candidate.
|
||||
ovl_fail_object_addrspace_mismatch
|
||||
ovl_fail_object_addrspace_mismatch,
|
||||
|
||||
/// This candidate was not viable because its associated constraints were
|
||||
/// not satisfied.
|
||||
ovl_fail_constraints_not_satisfied,
|
||||
};
|
||||
|
||||
/// A list of implicit conversion sequences for the arguments of an
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "clang/Sema/ObjCMethodList.h"
|
||||
#include "clang/Sema/Ownership.h"
|
||||
#include "clang/Sema/Scope.h"
|
||||
#include "clang/Sema/SemaConcept.h"
|
||||
#include "clang/Sema/TypoCorrection.h"
|
||||
#include "clang/Sema/Weak.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
@ -2300,6 +2301,8 @@ public:
|
|||
SkipBodyInfo *SkipBody = nullptr);
|
||||
Decl *ActOnStartOfFunctionDef(Scope *S, Decl *D,
|
||||
SkipBodyInfo *SkipBody = nullptr);
|
||||
void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D);
|
||||
ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr);
|
||||
void ActOnStartOfObjCMethodDef(Scope *S, Decl *D);
|
||||
bool isObjCMethodDecl(Decl *D) {
|
||||
return D && isa<ObjCMethodDecl>(D);
|
||||
|
@ -2884,7 +2887,8 @@ public:
|
|||
NamedDecl *&OldDecl,
|
||||
bool IsForUsingDecl);
|
||||
bool IsOverload(FunctionDecl *New, FunctionDecl *Old, bool IsForUsingDecl,
|
||||
bool ConsiderCudaAttrs = true);
|
||||
bool ConsiderCudaAttrs = true,
|
||||
bool ConsiderRequiresClauses = true);
|
||||
|
||||
ImplicitConversionSequence
|
||||
TryImplicitConversion(Expr *From, QualType ToType,
|
||||
|
@ -3249,10 +3253,9 @@ public:
|
|||
bool *pHadMultipleCandidates = nullptr);
|
||||
|
||||
FunctionDecl *
|
||||
resolveAddressOfOnlyViableOverloadCandidate(Expr *E,
|
||||
DeclAccessPair &FoundResult);
|
||||
resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &FoundResult);
|
||||
|
||||
bool resolveAndFixAddressOfOnlyViableOverloadCandidate(
|
||||
bool resolveAndFixAddressOfSingleOverloadCandidate(
|
||||
ExprResult &SrcExpr, bool DoFunctionPointerConversion = false);
|
||||
|
||||
FunctionDecl *
|
||||
|
@ -6035,7 +6038,8 @@ public:
|
|||
TypeSourceInfo *MethodType,
|
||||
SourceLocation EndLoc,
|
||||
ArrayRef<ParmVarDecl *> Params,
|
||||
ConstexprSpecKind ConstexprKind);
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause);
|
||||
|
||||
/// Number lambda for linkage purposes if necessary.
|
||||
void handleLambdaNumbering(
|
||||
|
@ -6169,16 +6173,35 @@ public:
|
|||
Expr *Src);
|
||||
|
||||
/// Check whether the given expression is a valid constraint expression.
|
||||
/// A diagnostic is emitted if it is not, and false is returned.
|
||||
bool CheckConstraintExpression(Expr *CE);
|
||||
/// 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
|
||||
/// non-primary expression being used as an atomic constraint.
|
||||
bool CheckConstraintExpression(Expr *CE, Token NextToken = Token(),
|
||||
bool *PossibleNonPrimary = nullptr,
|
||||
bool IsTrailingRequiresClause = false);
|
||||
|
||||
/// Check whether the given type-dependent expression will be the name of a
|
||||
/// function or another callable function-like entity (e.g. a function
|
||||
// template or overload set) for any substitution.
|
||||
bool IsDependentFunctionNameExpr(Expr *E);
|
||||
|
||||
private:
|
||||
/// \brief Caches pairs of template-like decls whose associated constraints
|
||||
/// were checked for subsumption and whether or not the first's constraints
|
||||
/// did in fact subsume the second's.
|
||||
/// Caches pairs of template-like decls whose associated constraints were
|
||||
/// checked for subsumption and whether or not the first's constraints did in
|
||||
/// fact subsume the second's.
|
||||
llvm::DenseMap<std::pair<NamedDecl *, NamedDecl *>, bool> SubsumptionCache;
|
||||
/// Caches the normalized associated constraints of declarations (concepts or
|
||||
/// constrained declarations). If an error occurred while normalizing the
|
||||
/// associated constraints of the template or concept, nullptr will be cached
|
||||
/// here.
|
||||
llvm::DenseMap<NamedDecl *, NormalizedConstraint *>
|
||||
NormalizationCache;
|
||||
|
||||
public:
|
||||
const NormalizedConstraint *
|
||||
getNormalizedAssociatedConstraints(
|
||||
NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints);
|
||||
|
||||
/// \brief Check whether the given declaration's associated constraints are
|
||||
/// at least as constrained than another declaration's according to the
|
||||
/// partial ordering of constraints.
|
||||
|
@ -6191,6 +6214,13 @@ public:
|
|||
NamedDecl *D2, ArrayRef<const Expr *> AC2,
|
||||
bool &Result);
|
||||
|
||||
/// If D1 was not at least as constrained as D2, but would've been if a pair
|
||||
/// of atomic constraints involved had been declared in a concept and not
|
||||
/// repeated in two separate places in code.
|
||||
/// \returns true if such a diagnostic was emitted, false otherwise.
|
||||
bool MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1,
|
||||
ArrayRef<const Expr *> AC1, NamedDecl *D2, ArrayRef<const Expr *> AC2);
|
||||
|
||||
/// \brief Check whether the given list of constraint expressions are
|
||||
/// satisfied (as if in a 'conjunction') given template arguments.
|
||||
/// \param ConstraintExprs a list of constraint expressions, treated as if
|
||||
|
@ -8644,6 +8674,10 @@ public:
|
|||
|
||||
void InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
|
||||
FunctionDecl *Function);
|
||||
bool CheckInstantiatedFunctionTemplateConstraints(
|
||||
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
|
||||
ArrayRef<TemplateArgument> TemplateArgs,
|
||||
ConstraintSatisfaction &Satisfaction);
|
||||
FunctionDecl *InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD,
|
||||
const TemplateArgumentList *Args,
|
||||
SourceLocation Loc);
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
//===-- SemaConcept.h - Semantic Analysis for Constraints and Concepts ----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
// This file provides semantic analysis for C++ constraints and concepts.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H
|
||||
#define LLVM_CLANG_SEMA_SEMACONCEPT_H
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
namespace clang {
|
||||
class Sema;
|
||||
|
||||
struct AtomicConstraint {
|
||||
const Expr *ConstraintExpr;
|
||||
Optional<MutableArrayRef<TemplateArgumentLoc>> ParameterMapping;
|
||||
|
||||
AtomicConstraint(Sema &S, const Expr *ConstraintExpr) :
|
||||
ConstraintExpr(ConstraintExpr) { };
|
||||
|
||||
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;
|
||||
|
||||
for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I)
|
||||
if (!C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
|
||||
.structurallyEquals(C.getCanonicalTemplateArgument(
|
||||
(*Other.ParameterMapping)[I].getArgument())))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subsumes(ASTContext &C, const AtomicConstraint &Other) const {
|
||||
// C++ [temp.constr.order] p2
|
||||
// - an atomic constraint A subsumes another atomic constraint B
|
||||
// if and only if the A and B are identical [...]
|
||||
//
|
||||
// C++ [temp.constr.atomic] p2
|
||||
// Two atomic constraints are identical if they are formed from the
|
||||
// same expression and the targets of the parameter mappings are
|
||||
// equivalent according to the rules for expressions [...]
|
||||
|
||||
// We do not actually substitute the parameter mappings into the
|
||||
// constraint expressions, therefore the constraint expressions are
|
||||
// the originals, and comparing them will suffice.
|
||||
if (ConstraintExpr != Other.ConstraintExpr)
|
||||
return false;
|
||||
|
||||
// Check that the parameter lists are identical
|
||||
return hasMatchingParameterMapping(C, Other);
|
||||
}
|
||||
};
|
||||
|
||||
/// \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<
|
||||
std::pair<NormalizedConstraint, NormalizedConstraint> *, 1,
|
||||
CompoundConstraintKind>;
|
||||
|
||||
llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
|
||||
|
||||
NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
|
||||
NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
|
||||
NormalizedConstraint RHS, CompoundConstraintKind Kind)
|
||||
: Constraint{CompoundConstraint{
|
||||
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
|
||||
std::move(LHS), std::move(RHS)}, Kind}} { };
|
||||
|
||||
NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other) {
|
||||
if (Other.isAtomic()) {
|
||||
Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
|
||||
} else {
|
||||
Constraint = CompoundConstraint(
|
||||
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
|
||||
NormalizedConstraint(C, Other.getLHS()),
|
||||
NormalizedConstraint(C, Other.getRHS())},
|
||||
Other.getCompoundKind());
|
||||
}
|
||||
}
|
||||
NormalizedConstraint(NormalizedConstraint &&Other):
|
||||
Constraint(Other.Constraint) {
|
||||
Other.Constraint = nullptr;
|
||||
}
|
||||
NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete;
|
||||
NormalizedConstraint &operator=(NormalizedConstraint &&Other) {
|
||||
if (&Other != this) {
|
||||
NormalizedConstraint Temp(std::move(Other));
|
||||
std::swap(Constraint, Temp.Constraint);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CompoundConstraintKind getCompoundKind() const {
|
||||
assert(!isAtomic() && "getCompoundKind called on atomic constraint.");
|
||||
return Constraint.get<CompoundConstraint>().getInt();
|
||||
}
|
||||
|
||||
bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
|
||||
|
||||
NormalizedConstraint &getLHS() const {
|
||||
assert(!isAtomic() && "getLHS called on atomic constraint.");
|
||||
return Constraint.get<CompoundConstraint>().getPointer()->first;
|
||||
}
|
||||
|
||||
NormalizedConstraint &getRHS() const {
|
||||
assert(!isAtomic() && "getRHS called on atomic constraint.");
|
||||
return Constraint.get<CompoundConstraint>().getPointer()->second;
|
||||
}
|
||||
|
||||
AtomicConstraint *getAtomicConstraint() const {
|
||||
assert(isAtomic() &&
|
||||
"getAtomicConstraint called on non-atomic constraint.");
|
||||
return Constraint.get<AtomicConstraint *>();
|
||||
}
|
||||
|
||||
private:
|
||||
static Optional<NormalizedConstraint>
|
||||
fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
|
||||
static Optional<NormalizedConstraint>
|
||||
fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E);
|
||||
};
|
||||
|
||||
} // clang
|
||||
|
||||
#endif //LLVM_CLANG_SEMA_SEMACONCEPT_H
|
|
@ -3279,10 +3279,12 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
|||
TypeSourceInfo *TInfo;
|
||||
SourceLocation ToInnerLocStart, ToEndLoc;
|
||||
NestedNameSpecifierLoc ToQualifierLoc;
|
||||
Expr *TrailingRequiresClause;
|
||||
if (auto Imp = importSeq(
|
||||
FromTy, D->getTypeSourceInfo(), D->getInnerLocStart(),
|
||||
D->getQualifierLoc(), D->getEndLoc()))
|
||||
std::tie(T, TInfo, ToInnerLocStart, ToQualifierLoc, ToEndLoc) = *Imp;
|
||||
D->getQualifierLoc(), D->getEndLoc(), D->getTrailingRequiresClause()))
|
||||
std::tie(T, TInfo, ToInnerLocStart, ToQualifierLoc, ToEndLoc,
|
||||
TrailingRequiresClause) = *Imp;
|
||||
else
|
||||
return Imp.takeError();
|
||||
|
||||
|
@ -3311,7 +3313,10 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
|||
ExplicitSpecifier(
|
||||
ExplicitExpr,
|
||||
FromConstructor->getExplicitSpecifier().getKind()),
|
||||
D->isInlineSpecified(), D->isImplicit(), D->getConstexprKind()))
|
||||
D->isInlineSpecified(), D->isImplicit(), D->getConstexprKind(),
|
||||
InheritedConstructor(), // FIXME: Properly import inherited
|
||||
// constructor info
|
||||
TrailingRequiresClause))
|
||||
return ToFunction;
|
||||
} else if (CXXDestructorDecl *FromDtor = dyn_cast<CXXDestructorDecl>(D)) {
|
||||
|
||||
|
@ -3329,7 +3334,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
|||
if (GetImportedOrCreateDecl<CXXDestructorDecl>(
|
||||
ToFunction, D, Importer.getToContext(), cast<CXXRecordDecl>(DC),
|
||||
ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(),
|
||||
D->isImplicit(), D->getConstexprKind()))
|
||||
D->isImplicit(), D->getConstexprKind(), TrailingRequiresClause))
|
||||
return ToFunction;
|
||||
|
||||
CXXDestructorDecl *ToDtor = cast<CXXDestructorDecl>(ToFunction);
|
||||
|
@ -3349,20 +3354,21 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
|
|||
ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(),
|
||||
ExplicitSpecifier(ExplicitExpr,
|
||||
FromConversion->getExplicitSpecifier().getKind()),
|
||||
D->getConstexprKind(), SourceLocation()))
|
||||
D->getConstexprKind(), SourceLocation(), TrailingRequiresClause))
|
||||
return ToFunction;
|
||||
} else if (auto *Method = dyn_cast<CXXMethodDecl>(D)) {
|
||||
if (GetImportedOrCreateDecl<CXXMethodDecl>(
|
||||
ToFunction, D, Importer.getToContext(), cast<CXXRecordDecl>(DC),
|
||||
ToInnerLocStart, NameInfo, T, TInfo, Method->getStorageClass(),
|
||||
Method->isInlineSpecified(), D->getConstexprKind(),
|
||||
SourceLocation()))
|
||||
SourceLocation(), TrailingRequiresClause))
|
||||
return ToFunction;
|
||||
} else {
|
||||
if (GetImportedOrCreateDecl(
|
||||
ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart,
|
||||
NameInfo, T, TInfo, D->getStorageClass(), D->isInlineSpecified(),
|
||||
D->hasWrittenPrototype(), D->getConstexprKind()))
|
||||
D->hasWrittenPrototype(), D->getConstexprKind(),
|
||||
TrailingRequiresClause))
|
||||
return ToFunction;
|
||||
}
|
||||
|
||||
|
|
|
@ -1839,23 +1839,27 @@ void DeclaratorDecl::setQualifierInfo(NestedNameSpecifierLoc QualifierLoc) {
|
|||
}
|
||||
// Set qualifier info.
|
||||
getExtInfo()->QualifierLoc = QualifierLoc;
|
||||
} else {
|
||||
} else if (hasExtInfo()) {
|
||||
// Here Qualifier == 0, i.e., we are removing the qualifier (if any).
|
||||
if (hasExtInfo()) {
|
||||
if (getExtInfo()->NumTemplParamLists == 0) {
|
||||
// Save type source info pointer.
|
||||
TypeSourceInfo *savedTInfo = getExtInfo()->TInfo;
|
||||
// Deallocate the extended decl info.
|
||||
getASTContext().Deallocate(getExtInfo());
|
||||
// Restore savedTInfo into (non-extended) decl info.
|
||||
DeclInfo = savedTInfo;
|
||||
}
|
||||
else
|
||||
getExtInfo()->QualifierLoc = QualifierLoc;
|
||||
}
|
||||
getExtInfo()->QualifierLoc = QualifierLoc;
|
||||
}
|
||||
}
|
||||
|
||||
void DeclaratorDecl::setTrailingRequiresClause(Expr *TrailingRequiresClause) {
|
||||
assert(TrailingRequiresClause);
|
||||
// Make sure the extended decl info is allocated.
|
||||
if (!hasExtInfo()) {
|
||||
// Save (non-extended) type source info pointer.
|
||||
auto *savedTInfo = DeclInfo.get<TypeSourceInfo*>();
|
||||
// Allocate external info struct.
|
||||
DeclInfo = new (getASTContext()) ExtInfo;
|
||||
// Restore savedTInfo into (extended) decl info.
|
||||
getExtInfo()->TInfo = savedTInfo;
|
||||
}
|
||||
// Set requires clause info.
|
||||
getExtInfo()->TrailingRequiresClause = TrailingRequiresClause;
|
||||
}
|
||||
|
||||
void DeclaratorDecl::setTemplateParameterListsInfo(
|
||||
ASTContext &Context, ArrayRef<TemplateParameterList *> TPLists) {
|
||||
assert(!TPLists.empty());
|
||||
|
@ -2777,7 +2781,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
|
|||
const DeclarationNameInfo &NameInfo, QualType T,
|
||||
TypeSourceInfo *TInfo, StorageClass S,
|
||||
bool isInlineSpecified,
|
||||
ConstexprSpecKind ConstexprKind)
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause)
|
||||
: DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo,
|
||||
StartLoc),
|
||||
DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0),
|
||||
|
@ -2807,6 +2812,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
|
|||
FunctionDeclBits.IsMultiVersion = false;
|
||||
FunctionDeclBits.IsCopyDeductionCandidate = false;
|
||||
FunctionDeclBits.HasODRHash = false;
|
||||
if (TrailingRequiresClause)
|
||||
setTrailingRequiresClause(TrailingRequiresClause);
|
||||
}
|
||||
|
||||
void FunctionDecl::getNameForDiagnostic(
|
||||
|
@ -4683,10 +4690,12 @@ FunctionDecl *FunctionDecl::Create(ASTContext &C, DeclContext *DC,
|
|||
QualType T, TypeSourceInfo *TInfo,
|
||||
StorageClass SC, bool isInlineSpecified,
|
||||
bool hasWrittenPrototype,
|
||||
ConstexprSpecKind ConstexprKind) {
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause) {
|
||||
FunctionDecl *New =
|
||||
new (C, DC) FunctionDecl(Function, C, DC, StartLoc, NameInfo, T, TInfo,
|
||||
SC, isInlineSpecified, ConstexprKind);
|
||||
SC, isInlineSpecified, ConstexprKind,
|
||||
TrailingRequiresClause);
|
||||
New->setHasWrittenPrototype(hasWrittenPrototype);
|
||||
return New;
|
||||
}
|
||||
|
@ -4694,7 +4703,7 @@ FunctionDecl *FunctionDecl::Create(ASTContext &C, DeclContext *DC,
|
|||
FunctionDecl *FunctionDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
|
||||
return new (C, ID) FunctionDecl(Function, C, nullptr, SourceLocation(),
|
||||
DeclarationNameInfo(), QualType(), nullptr,
|
||||
SC_None, false, CSK_unspecified);
|
||||
SC_None, false, CSK_unspecified, nullptr);
|
||||
}
|
||||
|
||||
BlockDecl *BlockDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) {
|
||||
|
|
|
@ -2041,16 +2041,19 @@ CXXMethodDecl *CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD,
|
|||
QualType T, TypeSourceInfo *TInfo,
|
||||
StorageClass SC, bool isInline,
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
SourceLocation EndLocation) {
|
||||
SourceLocation EndLocation,
|
||||
Expr *TrailingRequiresClause) {
|
||||
return new (C, RD)
|
||||
CXXMethodDecl(CXXMethod, C, RD, StartLoc, NameInfo, T, TInfo, SC,
|
||||
isInline, ConstexprKind, EndLocation);
|
||||
isInline, ConstexprKind, EndLocation,
|
||||
TrailingRequiresClause);
|
||||
}
|
||||
|
||||
CXXMethodDecl *CXXMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
|
||||
return new (C, ID) CXXMethodDecl(
|
||||
CXXMethod, C, nullptr, SourceLocation(), DeclarationNameInfo(),
|
||||
QualType(), nullptr, SC_None, false, CSK_unspecified, SourceLocation());
|
||||
QualType(), nullptr, SC_None, false, CSK_unspecified, SourceLocation(),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
|
||||
|
@ -2431,9 +2434,11 @@ CXXConstructorDecl::CXXConstructorDecl(
|
|||
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
|
||||
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
|
||||
ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared,
|
||||
ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited)
|
||||
ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited,
|
||||
Expr *TrailingRequiresClause)
|
||||
: CXXMethodDecl(CXXConstructor, C, RD, StartLoc, NameInfo, T, TInfo,
|
||||
SC_None, isInline, ConstexprKind, SourceLocation()) {
|
||||
SC_None, isInline, ConstexprKind, SourceLocation(),
|
||||
TrailingRequiresClause) {
|
||||
setNumCtorInitializers(0);
|
||||
setInheritingConstructor(static_cast<bool>(Inherited));
|
||||
setImplicit(isImplicitlyDeclared);
|
||||
|
@ -2457,7 +2462,7 @@ CXXConstructorDecl *CXXConstructorDecl::CreateDeserialized(ASTContext &C,
|
|||
auto *Result = new (C, ID, Extra)
|
||||
CXXConstructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(),
|
||||
QualType(), nullptr, ExplicitSpecifier(), false, false,
|
||||
CSK_unspecified, InheritedConstructor());
|
||||
CSK_unspecified, InheritedConstructor(), nullptr);
|
||||
Result->setInheritingConstructor(isInheritingConstructor);
|
||||
Result->CXXConstructorDeclBits.HasTrailingExplicitSpecifier =
|
||||
hasTraillingExplicit;
|
||||
|
@ -2469,7 +2474,8 @@ CXXConstructorDecl *CXXConstructorDecl::Create(
|
|||
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
|
||||
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
|
||||
ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared,
|
||||
ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited) {
|
||||
ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited,
|
||||
Expr *TrailingRequiresClause) {
|
||||
assert(NameInfo.getName().getNameKind()
|
||||
== DeclarationName::CXXConstructorName &&
|
||||
"Name must refer to a constructor");
|
||||
|
@ -2478,7 +2484,8 @@ CXXConstructorDecl *CXXConstructorDecl::Create(
|
|||
Inherited ? 1 : 0, ES.getExpr() ? 1 : 0);
|
||||
return new (C, RD, Extra)
|
||||
CXXConstructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, ES, isInline,
|
||||
isImplicitlyDeclared, ConstexprKind, Inherited);
|
||||
isImplicitlyDeclared, ConstexprKind, Inherited,
|
||||
TrailingRequiresClause);
|
||||
}
|
||||
|
||||
CXXConstructorDecl::init_const_iterator CXXConstructorDecl::init_begin() const {
|
||||
|
@ -2599,19 +2606,22 @@ CXXDestructorDecl *
|
|||
CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
|
||||
return new (C, ID)
|
||||
CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(),
|
||||
QualType(), nullptr, false, false, CSK_unspecified);
|
||||
QualType(), nullptr, false, false, CSK_unspecified,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
CXXDestructorDecl *CXXDestructorDecl::Create(
|
||||
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
|
||||
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
|
||||
bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) {
|
||||
bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause) {
|
||||
assert(NameInfo.getName().getNameKind()
|
||||
== DeclarationName::CXXDestructorName &&
|
||||
"Name must refer to a destructor");
|
||||
return new (C, RD)
|
||||
CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline,
|
||||
isImplicitlyDeclared, ConstexprKind);
|
||||
isImplicitlyDeclared, ConstexprKind,
|
||||
TrailingRequiresClause);
|
||||
}
|
||||
|
||||
void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
|
||||
|
@ -2630,20 +2640,20 @@ CXXConversionDecl *
|
|||
CXXConversionDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
|
||||
return new (C, ID) CXXConversionDecl(
|
||||
C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr,
|
||||
false, ExplicitSpecifier(), CSK_unspecified, SourceLocation());
|
||||
false, ExplicitSpecifier(), CSK_unspecified, SourceLocation(), nullptr);
|
||||
}
|
||||
|
||||
CXXConversionDecl *CXXConversionDecl::Create(
|
||||
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
|
||||
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
|
||||
bool isInline, ExplicitSpecifier ES, ConstexprSpecKind ConstexprKind,
|
||||
SourceLocation EndLocation) {
|
||||
SourceLocation EndLocation, Expr *TrailingRequiresClause) {
|
||||
assert(NameInfo.getName().getNameKind()
|
||||
== DeclarationName::CXXConversionFunctionName &&
|
||||
"Name must refer to a conversion function");
|
||||
return new (C, RD)
|
||||
CXXConversionDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline, ES,
|
||||
ConstexprKind, EndLocation);
|
||||
ConstexprKind, EndLocation, TrailingRequiresClause);
|
||||
}
|
||||
|
||||
bool CXXConversionDecl::isLambdaToBlockPointerConversion() const {
|
||||
|
|
|
@ -740,6 +740,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
|
|||
Proto.clear();
|
||||
}
|
||||
Out << Proto;
|
||||
|
||||
if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) {
|
||||
Out << " requires ";
|
||||
TrailingRequiresClause->printPretty(Out, nullptr, SubPolicy, Indentation);
|
||||
}
|
||||
} else {
|
||||
Ty.print(Out, Policy, Proto);
|
||||
}
|
||||
|
|
|
@ -171,13 +171,18 @@ void TemplateDecl::anchor() {}
|
|||
|
||||
void TemplateDecl::
|
||||
getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
|
||||
// TODO: Concepts: Append function trailing requires clause.
|
||||
TemplateParams->getAssociatedConstraints(AC);
|
||||
if (auto *FD = dyn_cast_or_null<FunctionDecl>(getTemplatedDecl()))
|
||||
if (const Expr *TRC = FD->getTrailingRequiresClause())
|
||||
AC.push_back(TRC);
|
||||
}
|
||||
|
||||
bool TemplateDecl::hasAssociatedConstraints() const {
|
||||
// TODO: Concepts: Regard function trailing requires clause.
|
||||
return TemplateParams->hasAssociatedConstraints();
|
||||
if (TemplateParams->hasAssociatedConstraints())
|
||||
return true;
|
||||
if (auto *FD = dyn_cast_or_null<FunctionDecl>(getTemplatedDecl()))
|
||||
return FD->getTrailingRequiresClause();
|
||||
return false;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -2014,6 +2014,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (Tok.is(tok::kw_requires))
|
||||
ParseTrailingRequiresClause(D);
|
||||
|
||||
// Save late-parsed attributes for now; they need to be parsed in the
|
||||
// appropriate function scope after the function Decl has been constructed.
|
||||
// These will be parsed in ParseFunctionDefinition or ParseLexedAttrList.
|
||||
|
@ -2165,6 +2168,12 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
|
|||
|
||||
ParseDeclarator(D);
|
||||
if (!D.isInvalidType()) {
|
||||
// C++2a [dcl.decl]p1
|
||||
// init-declarator:
|
||||
// declarator initializer[opt]
|
||||
// declarator requires-clause
|
||||
if (Tok.is(tok::kw_requires))
|
||||
ParseTrailingRequiresClause(D);
|
||||
Decl *ThisDecl = ParseDeclarationAfterDeclarator(D);
|
||||
D.complete(ThisDecl);
|
||||
if (ThisDecl)
|
||||
|
@ -6032,6 +6041,22 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
|
|||
PrototypeScope.Exit();
|
||||
} else if (Tok.is(tok::l_square)) {
|
||||
ParseBracketDeclarator(D);
|
||||
} else if (Tok.is(tok::kw_requires) && D.hasGroupingParens()) {
|
||||
// This declarator is declaring a function, but the requires clause is
|
||||
// in the wrong place:
|
||||
// void (f() requires true);
|
||||
// instead of
|
||||
// void f() requires true;
|
||||
// or
|
||||
// void (f()) requires true;
|
||||
Diag(Tok, diag::err_requires_clause_inside_parens);
|
||||
ConsumeToken();
|
||||
ExprResult TrailingRequiresClause = Actions.CorrectDelayedTyposInExpr(
|
||||
ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true));
|
||||
if (TrailingRequiresClause.isUsable() && D.isFunctionDeclarator() &&
|
||||
!D.hasTrailingRequiresClause())
|
||||
// We're already ill-formed if we got here but we'll accept it anyway.
|
||||
D.setTrailingRequiresClause(TrailingRequiresClause.get());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -6212,6 +6237,47 @@ void Parser::ParseParenDeclarator(Declarator &D) {
|
|||
PrototypeScope.Exit();
|
||||
}
|
||||
|
||||
void Parser::InitCXXThisScopeForDeclaratorIfRelevant(
|
||||
const Declarator &D, const DeclSpec &DS,
|
||||
llvm::Optional<Sema::CXXThisScopeRAII> &ThisScope) {
|
||||
// C++11 [expr.prim.general]p3:
|
||||
// If a declaration declares a member function or member function
|
||||
// template of a class X, the expression this is a prvalue of type
|
||||
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
|
||||
// and the end of the function-definition, member-declarator, or
|
||||
// declarator.
|
||||
// FIXME: currently, "static" case isn't handled correctly.
|
||||
bool IsCXX11MemberFunction = getLangOpts().CPlusPlus11 &&
|
||||
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
|
||||
(D.getContext() == DeclaratorContext::MemberContext
|
||||
? !D.getDeclSpec().isFriendSpecified()
|
||||
: D.getContext() == DeclaratorContext::FileContext &&
|
||||
D.getCXXScopeSpec().isValid() &&
|
||||
Actions.CurContext->isRecord());
|
||||
if (!IsCXX11MemberFunction)
|
||||
return;
|
||||
|
||||
Qualifiers Q = Qualifiers::fromCVRUMask(DS.getTypeQualifiers());
|
||||
if (D.getDeclSpec().hasConstexprSpecifier() && !getLangOpts().CPlusPlus14)
|
||||
Q.addConst();
|
||||
// FIXME: Collect C++ address spaces.
|
||||
// If there are multiple different address spaces, the source is invalid.
|
||||
// Carry on using the first addr space for the qualifiers of 'this'.
|
||||
// The diagnostic will be given later while creating the function
|
||||
// prototype for the method.
|
||||
if (getLangOpts().OpenCLCPlusPlus) {
|
||||
for (ParsedAttr &attr : DS.getAttributes()) {
|
||||
LangAS ASIdx = attr.asOpenCLLangAS();
|
||||
if (ASIdx != LangAS::Default) {
|
||||
Q.addAddressSpace(ASIdx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ThisScope.emplace(Actions, dyn_cast<CXXRecordDecl>(Actions.CurContext), Q,
|
||||
IsCXX11MemberFunction);
|
||||
}
|
||||
|
||||
/// ParseFunctionDeclarator - We are after the identifier and have parsed the
|
||||
/// declarator D up to a paren, which indicates that we are parsing function
|
||||
/// arguments.
|
||||
|
@ -6225,7 +6291,8 @@ void Parser::ParseParenDeclarator(Declarator &D) {
|
|||
///
|
||||
/// For C++, after the parameter-list, it also parses the cv-qualifier-seq[opt],
|
||||
/// (C++11) ref-qualifier[opt], exception-specification[opt],
|
||||
/// (C++11) attribute-specifier-seq[opt], and (C++11) trailing-return-type[opt].
|
||||
/// (C++11) attribute-specifier-seq[opt], (C++11) trailing-return-type[opt] and
|
||||
/// (C++2a) the trailing requires-clause.
|
||||
///
|
||||
/// [C++11] exception-specification:
|
||||
/// dynamic-exception-specification
|
||||
|
@ -6320,43 +6387,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
if (ParseRefQualifier(RefQualifierIsLValueRef, RefQualifierLoc))
|
||||
EndLoc = RefQualifierLoc;
|
||||
|
||||
// C++11 [expr.prim.general]p3:
|
||||
// If a declaration declares a member function or member function
|
||||
// template of a class X, the expression this is a prvalue of type
|
||||
// "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq
|
||||
// and the end of the function-definition, member-declarator, or
|
||||
// declarator.
|
||||
// FIXME: currently, "static" case isn't handled correctly.
|
||||
bool IsCXX11MemberFunction =
|
||||
getLangOpts().CPlusPlus11 &&
|
||||
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
|
||||
(D.getContext() == DeclaratorContext::MemberContext
|
||||
? !D.getDeclSpec().isFriendSpecified()
|
||||
: D.getContext() == DeclaratorContext::FileContext &&
|
||||
D.getCXXScopeSpec().isValid() &&
|
||||
Actions.CurContext->isRecord());
|
||||
|
||||
Qualifiers Q = Qualifiers::fromCVRUMask(DS.getTypeQualifiers());
|
||||
if (D.getDeclSpec().hasConstexprSpecifier() && !getLangOpts().CPlusPlus14)
|
||||
Q.addConst();
|
||||
// FIXME: Collect C++ address spaces.
|
||||
// If there are multiple different address spaces, the source is invalid.
|
||||
// Carry on using the first addr space for the qualifiers of 'this'.
|
||||
// The diagnostic will be given later while creating the function
|
||||
// prototype for the method.
|
||||
if (getLangOpts().OpenCLCPlusPlus) {
|
||||
for (ParsedAttr &attr : DS.getAttributes()) {
|
||||
LangAS ASIdx = attr.asOpenCLLangAS();
|
||||
if (ASIdx != LangAS::Default) {
|
||||
Q.addAddressSpace(ASIdx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sema::CXXThisScopeRAII ThisScope(
|
||||
Actions, dyn_cast<CXXRecordDecl>(Actions.CurContext), Q,
|
||||
IsCXX11MemberFunction);
|
||||
llvm::Optional<Sema::CXXThisScopeRAII> ThisScope;
|
||||
InitCXXThisScopeForDeclaratorIfRelevant(D, DS, ThisScope);
|
||||
|
||||
// Parse exception-specification[opt].
|
||||
bool Delayed = D.isFirstDeclarationOfMember() &&
|
||||
|
@ -6624,6 +6656,17 @@ void Parser::ParseParameterDeclarationClause(
|
|||
// Parse GNU attributes, if present.
|
||||
MaybeParseGNUAttributes(ParmDeclarator);
|
||||
|
||||
if (Tok.is(tok::kw_requires)) {
|
||||
// User tried to define a requires clause in a parameter declaration,
|
||||
// which is surely not a function declaration.
|
||||
// void f(int (*g)(int, int) requires true);
|
||||
Diag(Tok,
|
||||
diag::err_requires_clause_on_declarator_not_declaring_a_function);
|
||||
ConsumeToken();
|
||||
Actions.CorrectDelayedTyposInExpr(
|
||||
ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true));
|
||||
}
|
||||
|
||||
// Remember this parsed parameter in ParamInfo.
|
||||
IdentifierInfo *ParmII = ParmDeclarator.getIdentifier();
|
||||
|
||||
|
|
|
@ -2301,6 +2301,7 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
|
|||
LateParsedAttrList &LateParsedAttrs) {
|
||||
// member-declarator:
|
||||
// declarator pure-specifier[opt]
|
||||
// declarator requires-clause
|
||||
// declarator brace-or-equal-initializer[opt]
|
||||
// identifier[opt] ':' constant-expression
|
||||
if (Tok.isNot(tok::colon))
|
||||
|
@ -2314,6 +2315,8 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
|
|||
BitfieldSize = ParseConstantExpression();
|
||||
if (BitfieldSize.isInvalid())
|
||||
SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
|
||||
} else if (Tok.is(tok::kw_requires)) {
|
||||
ParseTrailingRequiresClause(DeclaratorInfo);
|
||||
} else {
|
||||
ParseOptionalCXX11VirtSpecifierSeq(
|
||||
VS, getCurrentClass().IsInterface,
|
||||
|
@ -2436,6 +2439,7 @@ void Parser::MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(
|
|||
///
|
||||
/// member-declarator:
|
||||
/// declarator virt-specifier-seq[opt] pure-specifier[opt]
|
||||
/// [C++2a] declarator requires-clause
|
||||
/// declarator constant-initializer[opt]
|
||||
/// [C++11] declarator brace-or-equal-initializer[opt]
|
||||
/// identifier[opt] ':' constant-expression
|
||||
|
@ -2669,6 +2673,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
|
|||
|
||||
SmallVector<Decl *, 8> DeclsInGroup;
|
||||
ExprResult BitfieldSize;
|
||||
ExprResult TrailingRequiresClause;
|
||||
bool ExpectSemi = true;
|
||||
|
||||
// Parse the first declarator.
|
||||
|
@ -3793,6 +3798,62 @@ TypeResult Parser::ParseTrailingReturnType(SourceRange &Range,
|
|||
: DeclaratorContext::TrailingReturnContext);
|
||||
}
|
||||
|
||||
/// Parse a requires-clause as part of a function declaration.
|
||||
void Parser::ParseTrailingRequiresClause(Declarator &D) {
|
||||
assert(Tok.is(tok::kw_requires) && "expected requires");
|
||||
|
||||
SourceLocation RequiresKWLoc = ConsumeToken();
|
||||
|
||||
ExprResult TrailingRequiresClause;
|
||||
ParseScope ParamScope(this,
|
||||
Scope::DeclScope |
|
||||
Scope::FunctionDeclarationScope |
|
||||
Scope::FunctionPrototypeScope);
|
||||
|
||||
Actions.ActOnStartTrailingRequiresClause(getCurScope(), D);
|
||||
|
||||
llvm::Optional<Sema::CXXThisScopeRAII> ThisScope;
|
||||
InitCXXThisScopeForDeclaratorIfRelevant(D, D.getDeclSpec(), ThisScope);
|
||||
|
||||
TrailingRequiresClause =
|
||||
ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true);
|
||||
|
||||
TrailingRequiresClause =
|
||||
Actions.ActOnFinishTrailingRequiresClause(TrailingRequiresClause);
|
||||
|
||||
if (!D.isDeclarationOfFunction()) {
|
||||
Diag(RequiresKWLoc,
|
||||
diag::err_requires_clause_on_declarator_not_declaring_a_function);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TrailingRequiresClause.isInvalid())
|
||||
SkipUntil({tok::l_brace, tok::arrow, tok::kw_try, tok::comma, tok::colon},
|
||||
StopAtSemi | StopBeforeMatch);
|
||||
else
|
||||
D.setTrailingRequiresClause(TrailingRequiresClause.get());
|
||||
|
||||
// Did the user swap the trailing return type and requires clause?
|
||||
if (D.isFunctionDeclarator() && Tok.is(tok::arrow) &&
|
||||
D.getDeclSpec().getTypeSpecType() == TST_auto) {
|
||||
SourceLocation ArrowLoc = Tok.getLocation();
|
||||
SourceRange Range;
|
||||
TypeResult TrailingReturnType =
|
||||
ParseTrailingReturnType(Range, /*MayBeFollowedByDirectInit=*/false);
|
||||
|
||||
if (!TrailingReturnType.isInvalid()) {
|
||||
Diag(ArrowLoc,
|
||||
diag::err_requires_clause_must_appear_after_trailing_return)
|
||||
<< Range;
|
||||
auto &FunctionChunk = D.getFunctionTypeInfo();
|
||||
FunctionChunk.HasTrailingReturnType = TrailingReturnType.isUsable();
|
||||
FunctionChunk.TrailingReturnType = TrailingReturnType.get();
|
||||
} else
|
||||
SkipUntil({tok::equal, tok::l_brace, tok::arrow, tok::kw_try, tok::comma},
|
||||
StopAtSemi | StopBeforeMatch);
|
||||
}
|
||||
}
|
||||
|
||||
/// We have just started parsing the definition of a new class,
|
||||
/// so push that class onto our stack of classes that is currently
|
||||
/// being parsed.
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "clang/Parse/Parser.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/Basic/PrettyStackTrace.h"
|
||||
#include "clang/Parse/RAIIObjectsForParser.h"
|
||||
#include "clang/Sema/DeclSpec.h"
|
||||
|
@ -145,7 +146,7 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) {
|
|||
// Silence extension warnings in the sub-expression
|
||||
ExtensionRAIIObject O(Diags);
|
||||
|
||||
LHS = ParseCastExpression(false);
|
||||
LHS = ParseCastExpression(AnyCastExpr);
|
||||
}
|
||||
|
||||
if (!LHS.isInvalid())
|
||||
|
@ -169,7 +170,7 @@ ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) {
|
|||
if (Tok.is(tok::kw_co_yield))
|
||||
return ParseCoyieldExpression();
|
||||
|
||||
ExprResult LHS = ParseCastExpression(/*isUnaryExpression=*/false,
|
||||
ExprResult LHS = ParseCastExpression(AnyCastExpr,
|
||||
/*isAddressOfOperand=*/false,
|
||||
isTypeCast);
|
||||
return ParseRHSOfBinaryExpression(LHS, prec::Assignment);
|
||||
|
@ -202,7 +203,7 @@ Parser::ParseConstantExpressionInExprEvalContext(TypeCastState isTypeCast) {
|
|||
Sema::ExpressionEvaluationContext::ConstantEvaluated &&
|
||||
"Call this function only if your ExpressionEvaluationContext is "
|
||||
"already ConstantEvaluated");
|
||||
ExprResult LHS(ParseCastExpression(false, false, isTypeCast));
|
||||
ExprResult LHS(ParseCastExpression(AnyCastExpr, false, isTypeCast));
|
||||
ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional));
|
||||
return Actions.ActOnConstantExpression(Res);
|
||||
}
|
||||
|
@ -220,7 +221,7 @@ ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) {
|
|||
ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) {
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
||||
ExprResult LHS(ParseCastExpression(false, false, NotTypeCast));
|
||||
ExprResult LHS(ParseCastExpression(AnyCastExpr, false, NotTypeCast));
|
||||
ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::Conditional));
|
||||
return Actions.ActOnCaseExpr(CaseLoc, Res);
|
||||
}
|
||||
|
@ -234,13 +235,143 @@ ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) {
|
|||
ExprResult Parser::ParseConstraintExpression() {
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
||||
ExprResult LHS(ParseCastExpression(/*isUnaryExpression=*/false));
|
||||
ExprResult LHS(ParseCastExpression(AnyCastExpr));
|
||||
ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr));
|
||||
if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get()))
|
||||
if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) {
|
||||
Actions.CorrectDelayedTyposInExpr(Res);
|
||||
return ExprError();
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
/// \brief Parse a constraint-logical-and-expression.
|
||||
///
|
||||
/// \param RightMostExpr If provided, will receive the right-most atomic
|
||||
/// constraint that was parsed.
|
||||
/// \verbatim
|
||||
/// C++2a[temp.constr.decl]p1
|
||||
/// constraint-logical-and-expression:
|
||||
/// primary-expression
|
||||
/// constraint-logical-and-expression '&&' primary-expression
|
||||
///
|
||||
/// \endverbatim
|
||||
ExprResult
|
||||
Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) {
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
||||
bool NotPrimaryExpression = false;
|
||||
auto ParsePrimary = [&] () {
|
||||
ExprResult E = ParseCastExpression(PrimaryExprOnly,
|
||||
/*isAddressOfOperand=*/false,
|
||||
/*isTypeCast=*/NotTypeCast,
|
||||
/*isVectorLiteral=*/false,
|
||||
&NotPrimaryExpression);
|
||||
if (E.isInvalid())
|
||||
return ExprError();
|
||||
auto RecoverFromNonPrimary = [&] (ExprResult E, bool Note) {
|
||||
E = ParsePostfixExpressionSuffix(E);
|
||||
// Use InclusiveOr, the precedence just after '&&' to not parse the
|
||||
// next arguments to the logical and.
|
||||
E = ParseRHSOfBinaryExpression(E, prec::InclusiveOr);
|
||||
if (!E.isInvalid())
|
||||
Diag(E.get()->getExprLoc(),
|
||||
Note
|
||||
? diag::note_unparenthesized_non_primary_expr_in_requires_clause
|
||||
: diag::err_unparenthesized_non_primary_expr_in_requires_clause)
|
||||
<< FixItHint::CreateInsertion(E.get()->getBeginLoc(), "(")
|
||||
<< FixItHint::CreateInsertion(
|
||||
PP.getLocForEndOfToken(E.get()->getEndLoc()), ")")
|
||||
<< E.get()->getSourceRange();
|
||||
return E;
|
||||
};
|
||||
|
||||
if (NotPrimaryExpression ||
|
||||
// Check if the following tokens must be a part of a non-primary
|
||||
// expression
|
||||
getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator,
|
||||
/*CPlusPlus11=*/true) > prec::LogicalAnd ||
|
||||
// Postfix operators other than '(' (which will be checked for in
|
||||
// CheckConstraintExpression).
|
||||
Tok.isOneOf(tok::period, tok::plusplus, tok::minusminus) ||
|
||||
(Tok.is(tok::l_square) && !NextToken().is(tok::l_square))) {
|
||||
E = RecoverFromNonPrimary(E, /*Note=*/false);
|
||||
if (E.isInvalid())
|
||||
return ExprError();
|
||||
NotPrimaryExpression = false;
|
||||
}
|
||||
bool PossibleNonPrimary;
|
||||
bool IsConstraintExpr =
|
||||
Actions.CheckConstraintExpression(E.get(), Tok, &PossibleNonPrimary,
|
||||
IsTrailingRequiresClause);
|
||||
if (!IsConstraintExpr || PossibleNonPrimary) {
|
||||
// Atomic constraint might be an unparenthesized non-primary expression
|
||||
// (such as a binary operator), in which case we might get here (e.g. in
|
||||
// 'requires 0 + 1 && true' we would now be at '+', and parse and ignore
|
||||
// the rest of the addition expression). Try to parse the rest of it here.
|
||||
if (PossibleNonPrimary)
|
||||
E = RecoverFromNonPrimary(E, /*Note=*/!IsConstraintExpr);
|
||||
Actions.CorrectDelayedTyposInExpr(E);
|
||||
return ExprError();
|
||||
}
|
||||
return E;
|
||||
};
|
||||
ExprResult LHS = ParsePrimary();
|
||||
if (LHS.isInvalid())
|
||||
return ExprError();
|
||||
while (Tok.is(tok::ampamp)) {
|
||||
SourceLocation LogicalAndLoc = ConsumeToken();
|
||||
ExprResult RHS = ParsePrimary();
|
||||
if (RHS.isInvalid()) {
|
||||
Actions.CorrectDelayedTyposInExpr(LHS);
|
||||
return ExprError();
|
||||
}
|
||||
ExprResult Op = Actions.ActOnBinOp(getCurScope(), LogicalAndLoc,
|
||||
tok::ampamp, LHS.get(), RHS.get());
|
||||
if (!Op.isUsable()) {
|
||||
Actions.CorrectDelayedTyposInExpr(RHS);
|
||||
Actions.CorrectDelayedTyposInExpr(LHS);
|
||||
return ExprError();
|
||||
}
|
||||
LHS = Op;
|
||||
}
|
||||
return LHS;
|
||||
}
|
||||
|
||||
/// \brief Parse a constraint-logical-or-expression.
|
||||
///
|
||||
/// \verbatim
|
||||
/// C++2a[temp.constr.decl]p1
|
||||
/// constraint-logical-or-expression:
|
||||
/// constraint-logical-and-expression
|
||||
/// constraint-logical-or-expression '||'
|
||||
/// constraint-logical-and-expression
|
||||
///
|
||||
/// \endverbatim
|
||||
ExprResult
|
||||
Parser::ParseConstraintLogicalOrExpression(bool IsTrailingRequiresClause) {
|
||||
ExprResult LHS(ParseConstraintLogicalAndExpression(IsTrailingRequiresClause));
|
||||
if (!LHS.isUsable())
|
||||
return ExprError();
|
||||
while (Tok.is(tok::pipepipe)) {
|
||||
SourceLocation LogicalOrLoc = ConsumeToken();
|
||||
ExprResult RHS =
|
||||
ParseConstraintLogicalAndExpression(IsTrailingRequiresClause);
|
||||
if (!RHS.isUsable()) {
|
||||
Actions.CorrectDelayedTyposInExpr(LHS);
|
||||
return ExprError();
|
||||
}
|
||||
ExprResult Op = Actions.ActOnBinOp(getCurScope(), LogicalOrLoc,
|
||||
tok::pipepipe, LHS.get(), RHS.get());
|
||||
if (!Op.isUsable()) {
|
||||
Actions.CorrectDelayedTyposInExpr(RHS);
|
||||
Actions.CorrectDelayedTyposInExpr(LHS);
|
||||
return ExprError();
|
||||
}
|
||||
LHS = Op;
|
||||
}
|
||||
return LHS;
|
||||
}
|
||||
|
||||
bool Parser::isNotExpressionStart() {
|
||||
tok::TokenKind K = Tok.getKind();
|
||||
if (K == tok::l_brace || K == tok::r_brace ||
|
||||
|
@ -414,7 +545,7 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
|
|||
} else if (getLangOpts().CPlusPlus && NextTokPrec <= prec::Conditional)
|
||||
RHS = ParseAssignmentExpression();
|
||||
else
|
||||
RHS = ParseCastExpression(false);
|
||||
RHS = ParseCastExpression(AnyCastExpr);
|
||||
|
||||
if (RHS.isInvalid()) {
|
||||
// FIXME: Errors generated by the delayed typo correction should be
|
||||
|
@ -519,22 +650,24 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a cast-expression, or, if \p isUnaryExpression is true,
|
||||
/// parse a unary-expression.
|
||||
/// Parse a cast-expression, unary-expression or primary-expression, based
|
||||
/// on \p ExprType.
|
||||
///
|
||||
/// \p isAddressOfOperand exists because an id-expression that is the
|
||||
/// operand of address-of gets special treatment due to member pointers.
|
||||
///
|
||||
ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
||||
ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
|
||||
bool isAddressOfOperand,
|
||||
TypeCastState isTypeCast,
|
||||
bool isVectorLiteral) {
|
||||
bool isVectorLiteral,
|
||||
bool *NotPrimaryExpression) {
|
||||
bool NotCastExpr;
|
||||
ExprResult Res = ParseCastExpression(isUnaryExpression,
|
||||
ExprResult Res = ParseCastExpression(ParseKind,
|
||||
isAddressOfOperand,
|
||||
NotCastExpr,
|
||||
isTypeCast,
|
||||
isVectorLiteral);
|
||||
isVectorLiteral,
|
||||
NotPrimaryExpression);
|
||||
if (NotCastExpr)
|
||||
Diag(Tok, diag::err_expected_expression);
|
||||
return Res;
|
||||
|
@ -759,11 +892,12 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback {
|
|||
/// '__is_rvalue_expr'
|
||||
/// \endverbatim
|
||||
///
|
||||
ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
||||
ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
|
||||
bool isAddressOfOperand,
|
||||
bool &NotCastExpr,
|
||||
TypeCastState isTypeCast,
|
||||
bool isVectorLiteral) {
|
||||
bool isVectorLiteral,
|
||||
bool *NotPrimaryExpression) {
|
||||
ExprResult Res;
|
||||
tok::TokenKind SavedKind = Tok.getKind();
|
||||
auto SavedType = PreferredType;
|
||||
|
@ -782,11 +916,21 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
// ParsePostfixExpressionSuffix.
|
||||
switch (SavedKind) {
|
||||
case tok::l_paren: {
|
||||
// If this expression is limited to being a unary-expression, the parent can
|
||||
// If this expression is limited to being a unary-expression, the paren can
|
||||
// not start a cast expression.
|
||||
ParenParseOption ParenExprType =
|
||||
(isUnaryExpression && !getLangOpts().CPlusPlus) ? CompoundLiteral
|
||||
: CastExpr;
|
||||
ParenParseOption ParenExprType;
|
||||
switch (ParseKind) {
|
||||
case CastParseKind::UnaryExprOnly:
|
||||
if (!getLangOpts().CPlusPlus)
|
||||
ParenExprType = CompoundLiteral;
|
||||
LLVM_FALLTHROUGH;
|
||||
case CastParseKind::AnyCastExpr:
|
||||
ParenExprType = ParenParseOption::CastExpr;
|
||||
break;
|
||||
case CastParseKind::PrimaryExprOnly:
|
||||
ParenExprType = FoldExpr;
|
||||
break;
|
||||
}
|
||||
ParsedType CastTy;
|
||||
SourceLocation RParenLoc;
|
||||
Res = ParseParenExpression(ParenExprType, false/*stopIfCastExr*/,
|
||||
|
@ -861,8 +1005,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
if (TryAnnotateTypeOrScopeToken())
|
||||
return ExprError();
|
||||
assert(Tok.isNot(tok::kw_decltype) && Tok.isNot(tok::kw___super));
|
||||
return ParseCastExpression(isUnaryExpression, isAddressOfOperand);
|
||||
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast,
|
||||
isVectorLiteral, NotPrimaryExpression);
|
||||
|
||||
case tok::identifier: { // primary-expression: identifier
|
||||
// unqualified-id: identifier
|
||||
// constant: enumeration-constant
|
||||
|
@ -949,8 +1094,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
= RevertibleTypeTraits.find(II);
|
||||
if (Known != RevertibleTypeTraits.end()) {
|
||||
Tok.setKind(Known->second);
|
||||
return ParseCastExpression(isUnaryExpression, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast,
|
||||
isVectorLiteral, NotPrimaryExpression);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -961,7 +1107,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
if (TryAnnotateTypeOrScopeToken())
|
||||
return ExprError();
|
||||
if (!Tok.is(tok::identifier))
|
||||
return ParseCastExpression(isUnaryExpression, isAddressOfOperand);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast,
|
||||
isVectorLiteral,
|
||||
NotPrimaryExpression);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1076,8 +1225,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
Tok.is(tok::r_paren) ? nullptr : &Replacement);
|
||||
if (!Res.isInvalid() && Res.isUnset()) {
|
||||
UnconsumeToken(Replacement);
|
||||
return ParseCastExpression(isUnaryExpression, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast,
|
||||
/*isVectorLiteral=*/false,
|
||||
NotPrimaryExpression);
|
||||
}
|
||||
if (!Res.isInvalid() && Tok.is(tok::less))
|
||||
checkPotentialAngleBracket(Res);
|
||||
|
@ -1122,12 +1273,16 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
case tok::kw___builtin_FILE:
|
||||
case tok::kw___builtin_FUNCTION:
|
||||
case tok::kw___builtin_LINE:
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
return ParseBuiltinPrimaryExpression();
|
||||
case tok::kw___null:
|
||||
return Actions.ActOnGNUNullExpr(ConsumeToken());
|
||||
|
||||
case tok::plusplus: // unary-expression: '++' unary-expression [C99]
|
||||
case tok::minusminus: { // unary-expression: '--' unary-expression [C99]
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
// C++ [expr.unary] has:
|
||||
// unary-expression:
|
||||
// ++ cast-expression
|
||||
|
@ -1140,7 +1295,8 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
// One special case is implicitly handled here: if the preceding tokens are
|
||||
// an ambiguous cast expression, such as "(T())++", then we recurse to
|
||||
// determine whether the '++' is prefix or postfix.
|
||||
Res = ParseCastExpression(!getLangOpts().CPlusPlus,
|
||||
Res = ParseCastExpression(getLangOpts().CPlusPlus ?
|
||||
UnaryExprOnly : AnyCastExpr,
|
||||
/*isAddressOfOperand*/false, NotCastExpr,
|
||||
NotTypeCast);
|
||||
if (NotCastExpr) {
|
||||
|
@ -1156,10 +1312,12 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
return Res;
|
||||
}
|
||||
case tok::amp: { // unary-expression: '&' cast-expression
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
// Special treatment because of member pointers
|
||||
SourceLocation SavedLoc = ConsumeToken();
|
||||
PreferredType.enterUnary(Actions, Tok.getLocation(), tok::amp, SavedLoc);
|
||||
Res = ParseCastExpression(false, true);
|
||||
Res = ParseCastExpression(AnyCastExpr, true);
|
||||
if (!Res.isInvalid())
|
||||
Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
|
||||
return Res;
|
||||
|
@ -1172,17 +1330,21 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
case tok::exclaim: // unary-expression: '!' cast-expression
|
||||
case tok::kw___real: // unary-expression: '__real' cast-expression [GNU]
|
||||
case tok::kw___imag: { // unary-expression: '__imag' cast-expression [GNU]
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
SourceLocation SavedLoc = ConsumeToken();
|
||||
PreferredType.enterUnary(Actions, Tok.getLocation(), SavedKind, SavedLoc);
|
||||
Res = ParseCastExpression(false);
|
||||
Res = ParseCastExpression(AnyCastExpr);
|
||||
if (!Res.isInvalid())
|
||||
Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
|
||||
return Res;
|
||||
}
|
||||
|
||||
case tok::kw_co_await: { // unary-expression: 'co_await' cast-expression
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
SourceLocation CoawaitLoc = ConsumeToken();
|
||||
Res = ParseCastExpression(false);
|
||||
Res = ParseCastExpression(AnyCastExpr);
|
||||
if (!Res.isInvalid())
|
||||
Res = Actions.ActOnCoawaitExpr(getCurScope(), CoawaitLoc, Res.get());
|
||||
return Res;
|
||||
|
@ -1190,9 +1352,11 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
|
||||
case tok::kw___extension__:{//unary-expression:'__extension__' cast-expr [GNU]
|
||||
// __extension__ silences extension warnings in the subexpression.
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
ExtensionRAIIObject O(Diags); // Use RAII to do this.
|
||||
SourceLocation SavedLoc = ConsumeToken();
|
||||
Res = ParseCastExpression(false);
|
||||
Res = ParseCastExpression(AnyCastExpr);
|
||||
if (!Res.isInvalid())
|
||||
Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
|
||||
return Res;
|
||||
|
@ -1209,8 +1373,12 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression
|
||||
// unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')'
|
||||
case tok::kw___builtin_omp_required_simd_align:
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
return ParseUnaryExprOrTypeTraitExpression();
|
||||
case tok::ampamp: { // unary-expression: '&&' identifier
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
SourceLocation AmpAmpLoc = ConsumeToken();
|
||||
if (Tok.isNot(tok::identifier))
|
||||
return ExprError(Diag(Tok, diag::err_expected) << tok::identifier);
|
||||
|
@ -1229,15 +1397,23 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
case tok::kw_dynamic_cast:
|
||||
case tok::kw_reinterpret_cast:
|
||||
case tok::kw_static_cast:
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
Res = ParseCXXCasts();
|
||||
break;
|
||||
case tok::kw___builtin_bit_cast:
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
Res = ParseBuiltinBitCast();
|
||||
break;
|
||||
case tok::kw_typeid:
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
Res = ParseCXXTypeid();
|
||||
break;
|
||||
case tok::kw___uuidof:
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
Res = ParseCXXUuidof();
|
||||
break;
|
||||
case tok::kw_this:
|
||||
|
@ -1302,6 +1478,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
return ExprError();
|
||||
}
|
||||
|
||||
// Everything henceforth is a postfix-expression.
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
|
||||
if (SavedKind == tok::kw_typename) {
|
||||
// postfix-expression: typename-specifier '(' expression-list[opt] ')'
|
||||
// typename-specifier braced-init-list
|
||||
|
@ -1338,8 +1518,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
if (TryAnnotateTypeOrScopeToken())
|
||||
return ExprError();
|
||||
if (!Tok.is(tok::annot_cxxscope))
|
||||
return ParseCastExpression(isUnaryExpression, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr,
|
||||
isTypeCast, isVectorLiteral,
|
||||
NotPrimaryExpression);
|
||||
|
||||
Token Next = NextToken();
|
||||
if (Next.is(tok::annot_template_id)) {
|
||||
|
@ -1352,8 +1533,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
ParseOptionalCXXScopeSpecifier(SS, nullptr,
|
||||
/*EnteringContext=*/false);
|
||||
AnnotateTemplateIdTokenAsType();
|
||||
return ParseCastExpression(isUnaryExpression, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr,
|
||||
isTypeCast, isVectorLiteral,
|
||||
NotPrimaryExpression);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1369,8 +1551,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
// translate it into a type and continue parsing as a cast
|
||||
// expression.
|
||||
AnnotateTemplateIdTokenAsType();
|
||||
return ParseCastExpression(isUnaryExpression, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand,
|
||||
NotCastExpr, isTypeCast, isVectorLiteral,
|
||||
NotPrimaryExpression);
|
||||
}
|
||||
|
||||
// Fall through to treat the template-id as an id-expression.
|
||||
|
@ -1387,15 +1570,22 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
if (TryAnnotateTypeOrScopeToken())
|
||||
return ExprError();
|
||||
if (!Tok.is(tok::coloncolon))
|
||||
return ParseCastExpression(isUnaryExpression, isAddressOfOperand);
|
||||
return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast,
|
||||
isVectorLiteral, NotPrimaryExpression);
|
||||
|
||||
// ::new -> [C++] new-expression
|
||||
// ::delete -> [C++] delete-expression
|
||||
SourceLocation CCLoc = ConsumeToken();
|
||||
if (Tok.is(tok::kw_new))
|
||||
if (Tok.is(tok::kw_new)) {
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
return ParseCXXNewExpression(true, CCLoc);
|
||||
if (Tok.is(tok::kw_delete))
|
||||
}
|
||||
if (Tok.is(tok::kw_delete)) {
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
return ParseCXXDeleteExpression(true, CCLoc);
|
||||
}
|
||||
|
||||
// This is not a type name or scope specifier, it is an invalid expression.
|
||||
Diag(CCLoc, diag::err_expected_expression);
|
||||
|
@ -1403,12 +1593,18 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
}
|
||||
|
||||
case tok::kw_new: // [C++] new-expression
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
return ParseCXXNewExpression(false, Tok.getLocation());
|
||||
|
||||
case tok::kw_delete: // [C++] delete-expression
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
return ParseCXXDeleteExpression(false, Tok.getLocation());
|
||||
|
||||
case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')'
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
Diag(Tok, diag::warn_cxx98_compat_noexcept_expr);
|
||||
SourceLocation KeyLoc = ConsumeToken();
|
||||
BalancedDelimiterTracker T(*this, tok::l_paren);
|
||||
|
@ -1437,13 +1633,19 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
|
||||
case tok::kw___array_rank:
|
||||
case tok::kw___array_extent:
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
return ParseArrayTypeTrait();
|
||||
|
||||
case tok::kw___is_lvalue_expr:
|
||||
case tok::kw___is_rvalue_expr:
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
return ParseExpressionTrait();
|
||||
|
||||
case tok::at: {
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
SourceLocation AtLoc = ConsumeToken();
|
||||
return ParseObjCAtExpression(AtLoc);
|
||||
}
|
||||
|
@ -1465,8 +1667,13 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
// expression, or we have something that doesn't appear to be a lambda.
|
||||
// If we're in the last case, we fall back to ParseObjCMessageExpression.
|
||||
Res = TryParseLambdaExpression();
|
||||
if (!Res.isInvalid() && !Res.get())
|
||||
if (!Res.isInvalid() && !Res.get()) {
|
||||
// We assume Objective-C++ message expressions are not
|
||||
// primary-expressions.
|
||||
if (NotPrimaryExpression)
|
||||
*NotPrimaryExpression = true;
|
||||
Res = ParseObjCMessageExpression();
|
||||
}
|
||||
break;
|
||||
}
|
||||
Res = ParseLambdaExpression();
|
||||
|
@ -1486,6 +1693,11 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
|
|||
// are compiling for OpenCL, we need to return an error as this implies
|
||||
// that the address of the function is being taken, which is illegal in CL.
|
||||
|
||||
if (ParseKind == PrimaryExprOnly)
|
||||
// This is strictly a primary-expression - no postfix-expr pieces should be
|
||||
// parsed.
|
||||
return Res;
|
||||
|
||||
// These can be followed by postfix-expr pieces.
|
||||
PreferredType = SavedType;
|
||||
Res = ParsePostfixExpressionSuffix(Res);
|
||||
|
@ -1929,7 +2141,7 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
|
|||
return ExprError();
|
||||
}
|
||||
|
||||
Operand = ParseCastExpression(true/*isUnaryExpression*/);
|
||||
Operand = ParseCastExpression(UnaryExprOnly);
|
||||
} else {
|
||||
// If it starts with a '(', we know that it is either a parenthesized
|
||||
// type-name, or it is a unary-expression that starts with a compound
|
||||
|
@ -2474,8 +2686,8 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
|
|||
RParenLoc = T.getCloseLocation();
|
||||
|
||||
PreferredType.enterTypeCast(Tok.getLocation(), Ty.get().get());
|
||||
ExprResult SubExpr = ParseCastExpression(/*isUnaryExpression=*/false);
|
||||
|
||||
ExprResult SubExpr = ParseCastExpression(AnyCastExpr);
|
||||
|
||||
if (Ty.isInvalid() || SubExpr.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
|
@ -2555,7 +2767,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
|
|||
// Parse the cast-expression that follows it next.
|
||||
// isVectorLiteral = true will make sure we don't parse any
|
||||
// Postfix expression yet
|
||||
Result = ParseCastExpression(/*isUnaryExpression=*/false,
|
||||
Result = ParseCastExpression(/*isUnaryExpression=*/AnyCastExpr,
|
||||
/*isAddressOfOperand=*/false,
|
||||
/*isTypeCast=*/IsTypeCast,
|
||||
/*isVectorLiteral=*/true);
|
||||
|
@ -2607,7 +2819,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr,
|
|||
PreferredType.enterTypeCast(Tok.getLocation(), CastTy.get());
|
||||
// Parse the cast-expression that follows it next.
|
||||
// TODO: For cast expression with CastTy.
|
||||
Result = ParseCastExpression(/*isUnaryExpression=*/false,
|
||||
Result = ParseCastExpression(/*isUnaryExpression=*/AnyCastExpr,
|
||||
/*isAddressOfOperand=*/false,
|
||||
/*isTypeCast=*/IsTypeCast);
|
||||
if (!Result.isInvalid()) {
|
||||
|
|
|
@ -1371,10 +1371,6 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
DeclEndLoc = Range.getEnd();
|
||||
}
|
||||
|
||||
PrototypeScope.Exit();
|
||||
|
||||
WarnIfHasCUDATargetAttr();
|
||||
|
||||
SourceLocation NoLoc;
|
||||
D.AddTypeInfo(DeclaratorChunk::getFunction(
|
||||
/*HasProto=*/true,
|
||||
|
@ -1389,13 +1385,22 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
/*DeclsInPrototype=*/None, LParenLoc, FunLocalRangeEnd, D,
|
||||
TrailingReturnType, &DS),
|
||||
std::move(Attr), DeclEndLoc);
|
||||
|
||||
// Parse requires-clause[opt].
|
||||
if (Tok.is(tok::kw_requires))
|
||||
ParseTrailingRequiresClause(D);
|
||||
|
||||
PrototypeScope.Exit();
|
||||
|
||||
WarnIfHasCUDATargetAttr();
|
||||
} else if (Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute,
|
||||
tok::kw_constexpr, tok::kw_consteval,
|
||||
tok::kw___private, tok::kw___global, tok::kw___local,
|
||||
tok::kw___constant, tok::kw___generic) ||
|
||||
tok::kw___constant, tok::kw___generic,
|
||||
tok::kw_requires) ||
|
||||
(Tok.is(tok::l_square) && NextToken().is(tok::l_square))) {
|
||||
// It's common to forget that one needs '()' before 'mutable', an attribute
|
||||
// specifier, or the result type. Deal with this.
|
||||
// specifier, the result type, or the requires clause. Deal with this.
|
||||
unsigned TokKind = 0;
|
||||
switch (Tok.getKind()) {
|
||||
case tok::kw_mutable: TokKind = 0; break;
|
||||
|
@ -1409,6 +1414,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
case tok::l_square: TokKind = 2; break;
|
||||
case tok::kw_constexpr: TokKind = 3; break;
|
||||
case tok::kw_consteval: TokKind = 4; break;
|
||||
case tok::kw_requires: TokKind = 5; break;
|
||||
default: llvm_unreachable("Unknown token kind");
|
||||
}
|
||||
|
||||
|
@ -1440,8 +1446,6 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
DeclEndLoc = Range.getEnd();
|
||||
}
|
||||
|
||||
WarnIfHasCUDATargetAttr();
|
||||
|
||||
SourceLocation NoLoc;
|
||||
D.AddTypeInfo(DeclaratorChunk::getFunction(
|
||||
/*HasProto=*/true,
|
||||
|
@ -1462,6 +1466,12 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||
/*DeclsInPrototype=*/None, DeclLoc, DeclEndLoc, D,
|
||||
TrailingReturnType),
|
||||
std::move(Attr), DeclEndLoc);
|
||||
|
||||
// Parse the requires-clause, if present.
|
||||
if (Tok.is(tok::kw_requires))
|
||||
ParseTrailingRequiresClause(D);
|
||||
|
||||
WarnIfHasCUDATargetAttr();
|
||||
}
|
||||
|
||||
// FIXME: Rename BlockScope -> ClosureScope if we decide to continue using
|
||||
|
@ -3238,7 +3248,7 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) {
|
|||
return ExprError();
|
||||
}
|
||||
|
||||
ExprResult Operand(ParseCastExpression(false));
|
||||
ExprResult Operand(ParseCastExpression(AnyCastExpr));
|
||||
if (Operand.isInvalid())
|
||||
return Operand;
|
||||
|
||||
|
@ -3469,7 +3479,7 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType,
|
|||
// If it is not a cast-expression, NotCastExpr will be true and no token
|
||||
// will be consumed.
|
||||
ColonProt.restore();
|
||||
Result = ParseCastExpression(false/*isUnaryExpression*/,
|
||||
Result = ParseCastExpression(AnyCastExpr,
|
||||
false/*isAddressofOperand*/,
|
||||
NotCastExpr,
|
||||
// type-id has priority.
|
||||
|
|
|
@ -2264,8 +2264,8 @@ ExprResult Parser::ParseOpenMPParensExpr(StringRef ClauseName,
|
|||
return ExprError();
|
||||
|
||||
SourceLocation ELoc = Tok.getLocation();
|
||||
ExprResult LHS(ParseCastExpression(
|
||||
/*isUnaryExpression=*/false, IsAddressOfOperand, NotTypeCast));
|
||||
ExprResult LHS(ParseCastExpression(AnyCastExpr, IsAddressOfOperand,
|
||||
NotTypeCast));
|
||||
ExprResult Val(ParseRHSOfBinaryExpression(LHS, prec::Conditional));
|
||||
Val = Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false);
|
||||
|
||||
|
@ -2513,7 +2513,7 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPClauseKind Kind,
|
|||
Kind == OMPC_if;
|
||||
if (NeedAnExpression) {
|
||||
SourceLocation ELoc = Tok.getLocation();
|
||||
ExprResult LHS(ParseCastExpression(false, false, NotTypeCast));
|
||||
ExprResult LHS(ParseCastExpression(AnyCastExpr, false, NotTypeCast));
|
||||
Val = ParseRHSOfBinaryExpression(LHS, prec::Conditional);
|
||||
Val =
|
||||
Actions.ActOnFinishFullExpr(Val.get(), ELoc, /*DiscardedValue*/ false);
|
||||
|
|
|
@ -130,7 +130,9 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization(
|
|||
|
||||
if (TryConsumeToken(tok::kw_requires)) {
|
||||
OptionalRequiresClauseConstraintER =
|
||||
Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression());
|
||||
Actions.CorrectDelayedTyposInExpr(
|
||||
ParseConstraintLogicalOrExpression(
|
||||
/*IsTrailingRequiresClause=*/false));
|
||||
if (!OptionalRequiresClauseConstraintER.isUsable()) {
|
||||
// Skip until the semi-colon or a '}'.
|
||||
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
|
||||
|
@ -254,8 +256,12 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate(
|
|||
});
|
||||
|
||||
LateParsedAttrList LateParsedAttrs(true);
|
||||
if (DeclaratorInfo.isFunctionDeclarator())
|
||||
if (DeclaratorInfo.isFunctionDeclarator()) {
|
||||
if (Tok.is(tok::kw_requires))
|
||||
ParseTrailingRequiresClause(DeclaratorInfo);
|
||||
|
||||
MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs);
|
||||
}
|
||||
|
||||
if (DeclaratorInfo.isFunctionDeclarator() &&
|
||||
isStartOfFunctionDefinition(DeclaratorInfo)) {
|
||||
|
|
|
@ -1031,6 +1031,10 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
|
|||
// direct-declarator '[' constant-expression[opt] ']'
|
||||
// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
|
||||
TPR = TryParseBracketDeclarator();
|
||||
} else if (Tok.is(tok::kw_requires)) {
|
||||
// declarator requires-clause
|
||||
// A requires clause indicates a function declaration.
|
||||
TPR = TPResult::True;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -2014,7 +2018,6 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
|
|||
/// 'throw' '(' type-id-list[opt] ')'
|
||||
///
|
||||
Parser::TPResult Parser::TryParseFunctionDeclarator() {
|
||||
|
||||
// The '(' is already parsed.
|
||||
|
||||
TPResult TPR = TryParseParameterDeclarationClause();
|
||||
|
|
|
@ -2008,7 +2008,7 @@ static bool fixOverloadedReinterpretCastExpr(Sema &Self, QualType DestType,
|
|||
// No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization
|
||||
// preserves Result.
|
||||
Result = E;
|
||||
if (!Self.resolveAndFixAddressOfOnlyViableOverloadCandidate(
|
||||
if (!Self.resolveAndFixAddressOfSingleOverloadCandidate(
|
||||
Result, /*DoFunctionPointerConversion=*/true))
|
||||
return false;
|
||||
return Result.isUsable();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Sema/SemaConcept.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/Sema/SemaInternal.h"
|
||||
#include "clang/Sema/SemaDiagnostic.h"
|
||||
|
@ -18,12 +19,16 @@
|
|||
#include "clang/Sema/Template.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/OperatorPrecedence.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
using namespace clang;
|
||||
using namespace sema;
|
||||
|
||||
bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {
|
||||
bool
|
||||
Sema::CheckConstraintExpression(Expr *ConstraintExpression, Token NextToken,
|
||||
bool *PossibleNonPrimary,
|
||||
bool IsTrailingRequiresClause) {
|
||||
// C++2a [temp.constr.atomic]p1
|
||||
// ..E shall be a constant expression of type bool.
|
||||
|
||||
|
@ -31,22 +36,56 @@ bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {
|
|||
|
||||
if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression)) {
|
||||
if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr)
|
||||
return CheckConstraintExpression(BinOp->getLHS()) &&
|
||||
CheckConstraintExpression(BinOp->getRHS());
|
||||
return CheckConstraintExpression(BinOp->getLHS(), NextToken,
|
||||
PossibleNonPrimary) &&
|
||||
CheckConstraintExpression(BinOp->getRHS(), NextToken,
|
||||
PossibleNonPrimary);
|
||||
} else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpression))
|
||||
return CheckConstraintExpression(C->getSubExpr());
|
||||
|
||||
// An atomic constraint!
|
||||
if (ConstraintExpression->isTypeDependent())
|
||||
return true;
|
||||
return CheckConstraintExpression(C->getSubExpr(), NextToken,
|
||||
PossibleNonPrimary);
|
||||
|
||||
QualType Type = ConstraintExpression->getType();
|
||||
|
||||
auto CheckForNonPrimary = [&] {
|
||||
if (PossibleNonPrimary)
|
||||
*PossibleNonPrimary =
|
||||
// We have the following case:
|
||||
// template<typename> requires func(0) struct S { };
|
||||
// The user probably isn't aware of the parentheses required around
|
||||
// the function call, and we're only going to parse 'func' as the
|
||||
// primary-expression, and complain that it is of non-bool type.
|
||||
(NextToken.is(tok::l_paren) &&
|
||||
(IsTrailingRequiresClause ||
|
||||
(Type->isDependentType() &&
|
||||
IsDependentFunctionNameExpr(ConstraintExpression)) ||
|
||||
Type->isFunctionType() ||
|
||||
Type->isSpecificBuiltinType(BuiltinType::Overload))) ||
|
||||
// We have the following case:
|
||||
// template<typename T> requires size_<T> == 0 struct S { };
|
||||
// The user probably isn't aware of the parentheses required around
|
||||
// the binary operator, and we're only going to parse 'func' as the
|
||||
// first operand, and complain that it is of non-bool type.
|
||||
getBinOpPrecedence(NextToken.getKind(),
|
||||
/*GreaterThanIsOperator=*/true,
|
||||
getLangOpts().CPlusPlus11) > prec::LogicalAnd;
|
||||
};
|
||||
|
||||
// An atomic constraint!
|
||||
if (ConstraintExpression->isTypeDependent()) {
|
||||
CheckForNonPrimary();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {
|
||||
Diag(ConstraintExpression->getExprLoc(),
|
||||
diag::err_non_bool_atomic_constraint) << Type
|
||||
<< ConstraintExpression->getSourceRange();
|
||||
CheckForNonPrimary();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PossibleNonPrimary)
|
||||
*PossibleNonPrimary = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -417,123 +456,25 @@ void Sema::DiagnoseUnsatisfiedConstraint(
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct AtomicConstraint {
|
||||
const Expr *ConstraintExpr;
|
||||
llvm::Optional<llvm::SmallVector<TemplateArgumentLoc, 3>> ParameterMapping;
|
||||
|
||||
AtomicConstraint(Sema &S, const Expr *ConstraintExpr) :
|
||||
ConstraintExpr(ConstraintExpr) { };
|
||||
|
||||
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;
|
||||
|
||||
for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I)
|
||||
if (!C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
|
||||
.structurallyEquals(C.getCanonicalTemplateArgument(
|
||||
(*Other.ParameterMapping)[I].getArgument())))
|
||||
return false;
|
||||
return true;
|
||||
const NormalizedConstraint *
|
||||
Sema::getNormalizedAssociatedConstraints(
|
||||
NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints) {
|
||||
auto CacheEntry = NormalizationCache.find(ConstrainedDecl);
|
||||
if (CacheEntry == NormalizationCache.end()) {
|
||||
auto Normalized =
|
||||
NormalizedConstraint::fromConstraintExprs(*this, ConstrainedDecl,
|
||||
AssociatedConstraints);
|
||||
CacheEntry =
|
||||
NormalizationCache
|
||||
.try_emplace(ConstrainedDecl,
|
||||
Normalized
|
||||
? new (Context) NormalizedConstraint(
|
||||
std::move(*Normalized))
|
||||
: nullptr)
|
||||
.first;
|
||||
}
|
||||
|
||||
bool subsumes(ASTContext &C, const AtomicConstraint &Other) const {
|
||||
// C++ [temp.constr.order] p2
|
||||
// - an atomic constraint A subsumes another atomic constraint B
|
||||
// if and only if the A and B are identical [...]
|
||||
//
|
||||
// C++ [temp.constr.atomic] p2
|
||||
// Two atomic constraints are identical if they are formed from the
|
||||
// same expression and the targets of the parameter mappings are
|
||||
// equivalent according to the rules for expressions [...]
|
||||
|
||||
// We do not actually substitute the parameter mappings into the
|
||||
// constraint expressions, therefore the constraint expressions are
|
||||
// the originals, and comparing them will suffice.
|
||||
if (ConstraintExpr != Other.ConstraintExpr)
|
||||
return false;
|
||||
|
||||
// Check that the parameter lists are identical
|
||||
return hasMatchingParameterMapping(C, Other);
|
||||
}
|
||||
};
|
||||
|
||||
/// \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 {
|
||||
enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
|
||||
|
||||
using CompoundConstraint = llvm::PointerIntPair<
|
||||
std::pair<NormalizedConstraint, NormalizedConstraint> *, 1,
|
||||
CompoundConstraintKind>;
|
||||
|
||||
llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
|
||||
|
||||
NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
|
||||
NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
|
||||
NormalizedConstraint RHS, CompoundConstraintKind Kind)
|
||||
: Constraint{CompoundConstraint{
|
||||
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{LHS,
|
||||
RHS},
|
||||
Kind}} { };
|
||||
|
||||
CompoundConstraintKind getCompoundKind() const {
|
||||
assert(!isAtomic() && "getCompoundKind called on atomic constraint.");
|
||||
return Constraint.get<CompoundConstraint>().getInt();
|
||||
}
|
||||
|
||||
bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
|
||||
|
||||
NormalizedConstraint &getLHS() const {
|
||||
assert(!isAtomic() && "getLHS called on atomic constraint.");
|
||||
return Constraint.get<CompoundConstraint>().getPointer()->first;
|
||||
}
|
||||
|
||||
NormalizedConstraint &getRHS() const {
|
||||
assert(!isAtomic() && "getRHS called on atomic constraint.");
|
||||
return Constraint.get<CompoundConstraint>().getPointer()->second;
|
||||
}
|
||||
|
||||
AtomicConstraint *getAtomicConstraint() const {
|
||||
assert(isAtomic() &&
|
||||
"getAtomicConstraint called on non-atomic constraint.");
|
||||
return Constraint.get<AtomicConstraint *>();
|
||||
}
|
||||
|
||||
static llvm::Optional<NormalizedConstraint>
|
||||
fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E) {
|
||||
assert(E.size() != 0);
|
||||
auto First = fromConstraintExpr(S, D, E[0]);
|
||||
if (E.size() == 1)
|
||||
return First;
|
||||
auto Second = fromConstraintExpr(S, D, E[1]);
|
||||
if (!Second)
|
||||
return llvm::Optional<NormalizedConstraint>{};
|
||||
llvm::Optional<NormalizedConstraint> Conjunction;
|
||||
Conjunction.emplace(S.Context, std::move(*First), std::move(*Second),
|
||||
CCK_Conjunction);
|
||||
for (unsigned I = 2; I < E.size(); ++I) {
|
||||
auto Next = fromConstraintExpr(S, D, E[I]);
|
||||
if (!Next)
|
||||
return llvm::Optional<NormalizedConstraint>{};
|
||||
NormalizedConstraint NewConjunction(S.Context, std::move(*Conjunction),
|
||||
std::move(*Next), CCK_Conjunction);
|
||||
*Conjunction = std::move(NewConjunction);
|
||||
}
|
||||
return Conjunction;
|
||||
}
|
||||
|
||||
private:
|
||||
static llvm::Optional<NormalizedConstraint> fromConstraintExpr(Sema &S,
|
||||
NamedDecl *D,
|
||||
const Expr *E);
|
||||
};
|
||||
return CacheEntry->second;
|
||||
}
|
||||
|
||||
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
||||
ConceptDecl *Concept, ArrayRef<TemplateArgument> TemplateArgs,
|
||||
|
@ -555,11 +496,13 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
|||
llvm::SmallBitVector OccurringIndices(TemplateParams->size());
|
||||
S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
|
||||
/*Depth=*/0, OccurringIndices);
|
||||
Atomic.ParameterMapping.emplace();
|
||||
Atomic.ParameterMapping->reserve(OccurringIndices.size());
|
||||
for (unsigned I = 0, C = TemplateParams->size(); I != C; ++I)
|
||||
Atomic.ParameterMapping.emplace(
|
||||
MutableArrayRef<TemplateArgumentLoc>(
|
||||
new (S.Context) TemplateArgumentLoc[OccurringIndices.count()],
|
||||
OccurringIndices.count()));
|
||||
for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
|
||||
if (OccurringIndices[I])
|
||||
Atomic.ParameterMapping->push_back(
|
||||
new (&(*Atomic.ParameterMapping)[J++]) TemplateArgumentLoc(
|
||||
S.getIdentityTemplateArgumentLoc(TemplateParams->begin()[I],
|
||||
// Here we assume we do not support things like
|
||||
// template<typename A, typename B>
|
||||
|
@ -585,6 +528,30 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
|
|||
return false;
|
||||
}
|
||||
|
||||
Optional<NormalizedConstraint>
|
||||
NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D,
|
||||
ArrayRef<const Expr *> E) {
|
||||
assert(E.size() != 0);
|
||||
auto First = fromConstraintExpr(S, D, E[0]);
|
||||
if (E.size() == 1)
|
||||
return First;
|
||||
auto Second = fromConstraintExpr(S, D, E[1]);
|
||||
if (!Second)
|
||||
return None;
|
||||
llvm::Optional<NormalizedConstraint> Conjunction;
|
||||
Conjunction.emplace(S.Context, std::move(*First), std::move(*Second),
|
||||
CCK_Conjunction);
|
||||
for (unsigned I = 2; I < E.size(); ++I) {
|
||||
auto Next = fromConstraintExpr(S, D, E[I]);
|
||||
if (!Next)
|
||||
return llvm::Optional<NormalizedConstraint>{};
|
||||
NormalizedConstraint NewConjunction(S.Context, std::move(*Conjunction),
|
||||
std::move(*Next), CCK_Conjunction);
|
||||
*Conjunction = std::move(NewConjunction);
|
||||
}
|
||||
return Conjunction;
|
||||
}
|
||||
|
||||
llvm::Optional<NormalizedConstraint>
|
||||
NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
|
||||
assert(E != nullptr);
|
||||
|
@ -604,11 +571,11 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
|
|||
return None;
|
||||
|
||||
return NormalizedConstraint(
|
||||
S.Context, *LHS, *RHS,
|
||||
S.Context, std::move(*LHS), std::move(*RHS),
|
||||
BO->getOpcode() == BO_LAnd ? CCK_Conjunction : CCK_Disjunction);
|
||||
}
|
||||
} else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) {
|
||||
Optional<NormalizedConstraint> SubNF;
|
||||
const NormalizedConstraint *SubNF;
|
||||
{
|
||||
Sema::InstantiatingTemplate Inst(
|
||||
S, CSE->getExprLoc(),
|
||||
|
@ -623,24 +590,26 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
|
|||
// constraint. If any such substitution results in an invalid type or
|
||||
// expression, the program is ill-formed; no diagnostic is required.
|
||||
// [...]
|
||||
SubNF = fromConstraintExpr(S, CSE->getNamedConcept(),
|
||||
CSE->getNamedConcept()->getConstraintExpr());
|
||||
ConceptDecl *CD = CSE->getNamedConcept();
|
||||
SubNF = S.getNormalizedAssociatedConstraints(CD,
|
||||
{CD->getConstraintExpr()});
|
||||
if (!SubNF)
|
||||
return None;
|
||||
}
|
||||
|
||||
Optional<NormalizedConstraint> New;
|
||||
New.emplace(S.Context, *SubNF);
|
||||
|
||||
if (substituteParameterMappings(
|
||||
S, *SubNF, CSE->getNamedConcept(),
|
||||
S, *New, CSE->getNamedConcept(),
|
||||
CSE->getTemplateArguments(), CSE->getTemplateArgsAsWritten()))
|
||||
return None;
|
||||
|
||||
return SubNF;
|
||||
return New;
|
||||
}
|
||||
return NormalizedConstraint{new (S.Context) AtomicConstraint(S, E)};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using NormalForm =
|
||||
llvm::SmallVector<llvm::SmallVector<AtomicConstraint *, 2>, 4>;
|
||||
|
||||
|
@ -703,22 +672,9 @@ static NormalForm makeDNF(const NormalizedConstraint &Normalized) {
|
|||
return Res;
|
||||
}
|
||||
|
||||
static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P,
|
||||
NamedDecl *DQ, ArrayRef<const Expr *> Q, bool &Subsumes) {
|
||||
// C++ [temp.constr.order] p2
|
||||
// In order to determine if a constraint P subsumes a constraint Q, P is
|
||||
// transformed into disjunctive normal form, and Q is transformed into
|
||||
// conjunctive normal form. [...]
|
||||
auto PNormalized = NormalizedConstraint::fromConstraintExprs(S, DP, P);
|
||||
if (!PNormalized)
|
||||
return true;
|
||||
const NormalForm PDNF = makeDNF(*PNormalized);
|
||||
|
||||
auto QNormalized = NormalizedConstraint::fromConstraintExprs(S, DQ, Q);
|
||||
if (!QNormalized)
|
||||
return true;
|
||||
const NormalForm QCNF = makeCNF(*QNormalized);
|
||||
|
||||
template<typename AtomicSubsumptionEvaluator>
|
||||
static bool subsumes(NormalForm PDNF, NormalForm QCNF,
|
||||
AtomicSubsumptionEvaluator E) {
|
||||
// C++ [temp.constr.order] p2
|
||||
// Then, P subsumes Q if and only if, for every disjunctive clause Pi in the
|
||||
// disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in
|
||||
|
@ -733,7 +689,7 @@ static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P,
|
|||
bool Found = false;
|
||||
for (const AtomicConstraint *Pia : Pi) {
|
||||
for (const AtomicConstraint *Qjb : Qj) {
|
||||
if (Pia->subsumes(S.Context, *Qjb)) {
|
||||
if (E(*Pia, *Qjb)) {
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -741,13 +697,32 @@ static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P,
|
|||
if (Found)
|
||||
break;
|
||||
}
|
||||
if (!Found) {
|
||||
Subsumes = false;
|
||||
if (!Found)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Subsumes = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename AtomicSubsumptionEvaluator>
|
||||
static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P,
|
||||
NamedDecl *DQ, ArrayRef<const Expr *> Q, bool &Subsumes,
|
||||
AtomicSubsumptionEvaluator E) {
|
||||
// C++ [temp.constr.order] p2
|
||||
// In order to determine if a constraint P subsumes a constraint Q, P is
|
||||
// transformed into disjunctive normal form, and Q is transformed into
|
||||
// conjunctive normal form. [...]
|
||||
auto *PNormalized = S.getNormalizedAssociatedConstraints(DP, P);
|
||||
if (!PNormalized)
|
||||
return true;
|
||||
const NormalForm PDNF = makeDNF(*PNormalized);
|
||||
|
||||
auto *QNormalized = S.getNormalizedAssociatedConstraints(DQ, Q);
|
||||
if (!QNormalized)
|
||||
return true;
|
||||
const NormalForm QCNF = makeCNF(*QNormalized);
|
||||
|
||||
Subsumes = subsumes(PDNF, QCNF, E);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -770,8 +745,84 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1,
|
|||
Result = CacheEntry->second;
|
||||
return false;
|
||||
}
|
||||
if (subsumes(*this, D1, AC1, D2, AC2, Result))
|
||||
|
||||
if (subsumes(*this, D1, AC1, D2, AC2, Result,
|
||||
[this] (const AtomicConstraint &A, const AtomicConstraint &B) {
|
||||
return A.subsumes(Context, B);
|
||||
}))
|
||||
return true;
|
||||
SubsumptionCache.try_emplace(Key, Result);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1,
|
||||
ArrayRef<const Expr *> AC1, NamedDecl *D2, ArrayRef<const Expr *> AC2) {
|
||||
if (isSFINAEContext())
|
||||
// No need to work here because our notes would be discarded.
|
||||
return false;
|
||||
|
||||
if (AC1.empty() || AC2.empty())
|
||||
return false;
|
||||
|
||||
auto NormalExprEvaluator =
|
||||
[this] (const AtomicConstraint &A, const AtomicConstraint &B) {
|
||||
return A.subsumes(Context, B);
|
||||
};
|
||||
|
||||
const Expr *AmbiguousAtomic1 = nullptr, *AmbiguousAtomic2 = nullptr;
|
||||
auto IdenticalExprEvaluator =
|
||||
[&] (const AtomicConstraint &A, const AtomicConstraint &B) {
|
||||
if (!A.hasMatchingParameterMapping(Context, B))
|
||||
return false;
|
||||
const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr;
|
||||
if (EA == EB)
|
||||
return true;
|
||||
|
||||
// Not the same source level expression - are the expressions
|
||||
// identical?
|
||||
llvm::FoldingSetNodeID IDA, IDB;
|
||||
EA->Profile(IDA, Context, /*Cannonical=*/true);
|
||||
EB->Profile(IDB, Context, /*Cannonical=*/true);
|
||||
if (IDA != IDB)
|
||||
return false;
|
||||
|
||||
AmbiguousAtomic1 = EA;
|
||||
AmbiguousAtomic2 = EB;
|
||||
return true;
|
||||
};
|
||||
|
||||
{
|
||||
// The subsumption checks might cause diagnostics
|
||||
SFINAETrap Trap(*this);
|
||||
auto *Normalized1 = getNormalizedAssociatedConstraints(D1, AC1);
|
||||
if (!Normalized1)
|
||||
return false;
|
||||
const NormalForm DNF1 = makeDNF(*Normalized1);
|
||||
const NormalForm CNF1 = makeCNF(*Normalized1);
|
||||
|
||||
auto *Normalized2 = getNormalizedAssociatedConstraints(D2, AC2);
|
||||
if (!Normalized2)
|
||||
return false;
|
||||
const NormalForm DNF2 = makeDNF(*Normalized2);
|
||||
const NormalForm CNF2 = makeCNF(*Normalized2);
|
||||
|
||||
bool Is1AtLeastAs2Normally = subsumes(DNF1, CNF2, NormalExprEvaluator);
|
||||
bool Is2AtLeastAs1Normally = subsumes(DNF2, CNF1, NormalExprEvaluator);
|
||||
bool Is1AtLeastAs2 = subsumes(DNF1, CNF2, IdenticalExprEvaluator);
|
||||
bool Is2AtLeastAs1 = subsumes(DNF2, CNF1, IdenticalExprEvaluator);
|
||||
if (Is1AtLeastAs2 == Is1AtLeastAs2Normally &&
|
||||
Is2AtLeastAs1 == Is2AtLeastAs1Normally)
|
||||
// Same result - no ambiguity was caused by identical atomic expressions.
|
||||
return false;
|
||||
}
|
||||
|
||||
// A different result! Some ambiguous atomic constraint(s) caused a difference
|
||||
assert(AmbiguousAtomic1 && AmbiguousAtomic2);
|
||||
|
||||
Diag(AmbiguousAtomic1->getBeginLoc(), diag::note_ambiguous_atomic_constraints)
|
||||
<< AmbiguousAtomic1->getSourceRange();
|
||||
Diag(AmbiguousAtomic2->getBeginLoc(),
|
||||
diag::note_ambiguous_atomic_constraints_similar_expression)
|
||||
<< AmbiguousAtomic2->getSourceRange();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7895,7 +7895,13 @@ struct FindOverriddenMethod {
|
|||
Path.Decls = Path.Decls.slice(1)) {
|
||||
NamedDecl *D = Path.Decls.front();
|
||||
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
|
||||
if (MD->isVirtual() && !S->IsOverload(Method, MD, false))
|
||||
if (MD->isVirtual() &&
|
||||
!S->IsOverload(
|
||||
Method, MD, /*UseMemberUsingDeclRules=*/false,
|
||||
/*ConsiderCudaAttrs=*/true,
|
||||
// C++2a [class.virtual]p2 does not consider requires clauses
|
||||
// when overriding.
|
||||
/*ConsiderRequiresClauses=*/false))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -8240,7 +8246,8 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
|||
|
||||
NewFD = FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), NameInfo,
|
||||
R, TInfo, SC, isInline, HasPrototype,
|
||||
CSK_unspecified);
|
||||
CSK_unspecified,
|
||||
/*TrailingRequiresClause=*/nullptr);
|
||||
if (D.isInvalidType())
|
||||
NewFD->setInvalidDecl();
|
||||
|
||||
|
@ -8257,6 +8264,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
|||
ConstexprKind = CSK_unspecified;
|
||||
D.getMutableDeclSpec().ClearConstexprSpec();
|
||||
}
|
||||
Expr *TrailingRequiresClause = D.getTrailingRequiresClause();
|
||||
|
||||
// Check that the return type is not an abstract class type.
|
||||
// For record types, this is done by the AbstractClassUsageDiagnoser once
|
||||
|
@ -8276,7 +8284,8 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
|||
return CXXConstructorDecl::Create(
|
||||
SemaRef.Context, cast<CXXRecordDecl>(DC), D.getBeginLoc(), NameInfo, R,
|
||||
TInfo, ExplicitSpecifier, isInline,
|
||||
/*isImplicitlyDeclared=*/false, ConstexprKind);
|
||||
/*isImplicitlyDeclared=*/false, ConstexprKind, InheritedConstructor(),
|
||||
TrailingRequiresClause);
|
||||
|
||||
} else if (Name.getNameKind() == DeclarationName::CXXDestructorName) {
|
||||
// This is a C++ destructor declaration.
|
||||
|
@ -8285,8 +8294,8 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
|||
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
|
||||
CXXDestructorDecl *NewDD = CXXDestructorDecl::Create(
|
||||
SemaRef.Context, Record, D.getBeginLoc(), NameInfo, R, TInfo,
|
||||
isInline,
|
||||
/*isImplicitlyDeclared=*/false, ConstexprKind);
|
||||
isInline, /*isImplicitlyDeclared=*/false, ConstexprKind,
|
||||
TrailingRequiresClause);
|
||||
|
||||
// If the destructor needs an implicit exception specification, set it
|
||||
// now. FIXME: It'd be nice to be able to create the right type to start
|
||||
|
@ -8306,7 +8315,8 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
|||
return FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(),
|
||||
D.getIdentifierLoc(), Name, R, TInfo, SC,
|
||||
isInline,
|
||||
/*hasPrototype=*/true, ConstexprKind);
|
||||
/*hasPrototype=*/true, ConstexprKind,
|
||||
TrailingRequiresClause);
|
||||
}
|
||||
|
||||
} else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) {
|
||||
|
@ -8323,9 +8333,14 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
|||
IsVirtualOkay = true;
|
||||
return CXXConversionDecl::Create(
|
||||
SemaRef.Context, cast<CXXRecordDecl>(DC), D.getBeginLoc(), NameInfo, R,
|
||||
TInfo, isInline, ExplicitSpecifier, ConstexprKind, SourceLocation());
|
||||
TInfo, isInline, ExplicitSpecifier, ConstexprKind, SourceLocation(),
|
||||
TrailingRequiresClause);
|
||||
|
||||
} else if (Name.getNameKind() == DeclarationName::CXXDeductionGuideName) {
|
||||
if (TrailingRequiresClause)
|
||||
SemaRef.Diag(TrailingRequiresClause->getBeginLoc(),
|
||||
diag::err_trailing_requires_clause_on_deduction_guide)
|
||||
<< TrailingRequiresClause->getSourceRange();
|
||||
SemaRef.CheckDeductionGuideDeclarator(D, R, SC);
|
||||
|
||||
return CXXDeductionGuideDecl::Create(SemaRef.Context, DC, D.getBeginLoc(),
|
||||
|
@ -8347,7 +8362,8 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
|||
// This is a C++ method declaration.
|
||||
CXXMethodDecl *Ret = CXXMethodDecl::Create(
|
||||
SemaRef.Context, cast<CXXRecordDecl>(DC), D.getBeginLoc(), NameInfo, R,
|
||||
TInfo, SC, isInline, ConstexprKind, SourceLocation());
|
||||
TInfo, SC, isInline, ConstexprKind, SourceLocation(),
|
||||
TrailingRequiresClause);
|
||||
IsVirtualOkay = !Ret->isStatic();
|
||||
return Ret;
|
||||
} else {
|
||||
|
@ -8361,7 +8377,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
|||
// - we're in C++ (where every function has a prototype),
|
||||
return FunctionDecl::Create(SemaRef.Context, DC, D.getBeginLoc(), NameInfo,
|
||||
R, TInfo, SC, isInline, true /*HasPrototype*/,
|
||||
ConstexprKind);
|
||||
ConstexprKind, TrailingRequiresClause);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10572,6 +10588,11 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
|
|||
}
|
||||
}
|
||||
}
|
||||
if (Method->isVirtual() && NewFD->getTrailingRequiresClause())
|
||||
// C++2a [class.virtual]p6
|
||||
// A virtual method shall not have a requires-clause.
|
||||
Diag(NewFD->getTrailingRequiresClause()->getBeginLoc(),
|
||||
diag::err_constrained_virtual_method);
|
||||
|
||||
if (Method->isStatic())
|
||||
checkThisInStaticMemberFunctionType(Method);
|
||||
|
|
|
@ -7569,7 +7569,8 @@ NamedDecl * Sema::DeclClonePragmaWeak(NamedDecl *ND, IdentifierInfo *II,
|
|||
NewFD = FunctionDecl::Create(
|
||||
FD->getASTContext(), FD->getDeclContext(), Loc, Loc,
|
||||
DeclarationName(II), FD->getType(), FD->getTypeSourceInfo(), SC_None,
|
||||
false /*isInlineSpecified*/, FD->hasPrototype(), CSK_unspecified);
|
||||
false /*isInlineSpecified*/, FD->hasPrototype(), CSK_unspecified,
|
||||
FD->getTrailingRequiresClause());
|
||||
NewD = NewFD;
|
||||
|
||||
if (FD->getQualifier())
|
||||
|
|
|
@ -3868,6 +3868,26 @@ void Sema::ActOnStartCXXInClassMemberInitializer() {
|
|||
PushFunctionScope();
|
||||
}
|
||||
|
||||
void Sema::ActOnStartTrailingRequiresClause(Scope *S, Declarator &D) {
|
||||
if (!D.isFunctionDeclarator())
|
||||
return;
|
||||
auto &FTI = D.getFunctionTypeInfo();
|
||||
if (!FTI.Params)
|
||||
return;
|
||||
for (auto &Param : ArrayRef<DeclaratorChunk::ParamInfo>(FTI.Params,
|
||||
FTI.NumParams)) {
|
||||
auto *ParamDecl = cast<NamedDecl>(Param.Param);
|
||||
if (ParamDecl->getDeclName())
|
||||
PushOnScopeChains(ParamDecl, S, /*AddToContext=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
ExprResult Sema::ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr) {
|
||||
if (ConstraintExpr.isInvalid())
|
||||
return ExprError();
|
||||
return CorrectDelayedTyposInExpr(ConstraintExpr);
|
||||
}
|
||||
|
||||
/// This is invoked after parsing an in-class initializer for a
|
||||
/// non-static C++ class member, and after instantiating an in-class initializer
|
||||
/// in a class template. Such actions are deferred until the class is complete.
|
||||
|
@ -12702,7 +12722,8 @@ Sema::findInheritingConstructor(SourceLocation Loc,
|
|||
BaseCtor->getExplicitSpecifier(), /*isInline=*/true,
|
||||
/*isImplicitlyDeclared=*/true,
|
||||
Constexpr ? BaseCtor->getConstexprKind() : CSK_unspecified,
|
||||
InheritedConstructor(Shadow, BaseCtor));
|
||||
InheritedConstructor(Shadow, BaseCtor),
|
||||
BaseCtor->getTrailingRequiresClause());
|
||||
if (Shadow->isInvalidDecl())
|
||||
DerivedCtor->setInvalidDecl();
|
||||
|
||||
|
@ -17092,6 +17113,11 @@ bool Sema::checkThisInStaticMemberFunctionType(CXXMethodDecl *Method) {
|
|||
if (checkThisInStaticMemberFunctionExceptionSpec(Method))
|
||||
return true;
|
||||
|
||||
// Check the trailing requires clause
|
||||
if (Expr *E = Method->getTrailingRequiresClause())
|
||||
if (!Finder.TraverseStmt(E))
|
||||
return true;
|
||||
|
||||
return checkThisInStaticMemberFunctionAttributes(Method);
|
||||
}
|
||||
|
||||
|
|
|
@ -326,6 +326,30 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
|
|||
|
||||
diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc);
|
||||
|
||||
// [expr.prim.id]p4
|
||||
// A program that refers explicitly or implicitly to a function with a
|
||||
// trailing requires-clause whose constraint-expression is not satisfied,
|
||||
// other than to declare it, is ill-formed. [...]
|
||||
//
|
||||
// See if this is a function with constraints that need to be satisfied.
|
||||
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
||||
if (Expr *RC = FD->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
bool Failed = CheckConstraintSatisfaction(RC, Satisfaction);
|
||||
if (Failed)
|
||||
// A diagnostic will have already been generated (non-constant
|
||||
// constraint expression, for example)
|
||||
return true;
|
||||
if (!Satisfaction.IsSatisfied) {
|
||||
Diag(Loc,
|
||||
diag::err_reference_to_function_with_unsatisfied_constraints)
|
||||
<< D;
|
||||
DiagnoseUnsatisfiedConstraint(Satisfaction);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -17975,7 +17999,7 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
|
|||
// No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization
|
||||
// leaves Result unchanged on failure.
|
||||
Result = E;
|
||||
if (resolveAndFixAddressOfOnlyViableOverloadCandidate(Result))
|
||||
if (resolveAndFixAddressOfSingleOverloadCandidate(Result))
|
||||
return Result;
|
||||
|
||||
// If that failed, try to recover with a call.
|
||||
|
@ -18112,3 +18136,8 @@ ExprResult Sema::ActOnObjCAvailabilityCheckExpr(
|
|||
return new (Context)
|
||||
ObjCAvailabilityCheckExpr(Version, AtLoc, RParen, Context.BoolTy);
|
||||
}
|
||||
|
||||
bool Sema::IsDependentFunctionNameExpr(Expr *E) {
|
||||
assert(E->isTypeDependent());
|
||||
return isa<UnresolvedLookupExpr>(E);
|
||||
}
|
||||
|
|
|
@ -361,7 +361,8 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
|
|||
TypeSourceInfo *MethodTypeInfo,
|
||||
SourceLocation EndLoc,
|
||||
ArrayRef<ParmVarDecl *> Params,
|
||||
ConstexprSpecKind ConstexprKind) {
|
||||
ConstexprSpecKind ConstexprKind,
|
||||
Expr *TrailingRequiresClause) {
|
||||
QualType MethodType = MethodTypeInfo->getType();
|
||||
TemplateParameterList *TemplateParams =
|
||||
getGenericLambdaTemplateParameterList(getCurLambda(), *this);
|
||||
|
@ -395,7 +396,7 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
|
|||
DeclarationNameInfo(MethodName, IntroducerRange.getBegin(),
|
||||
MethodNameLoc),
|
||||
MethodType, MethodTypeInfo, SC_None,
|
||||
/*isInline=*/true, ConstexprKind, EndLoc);
|
||||
/*isInline=*/true, ConstexprKind, EndLoc, TrailingRequiresClause);
|
||||
Method->setAccess(AS_public);
|
||||
if (!TemplateParams)
|
||||
Class->addDecl(Method);
|
||||
|
@ -972,7 +973,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
KnownDependent, Intro.Default);
|
||||
CXXMethodDecl *Method =
|
||||
startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params,
|
||||
ParamInfo.getDeclSpec().getConstexprSpecifier());
|
||||
ParamInfo.getDeclSpec().getConstexprSpecifier(),
|
||||
ParamInfo.getTrailingRequiresClause());
|
||||
if (ExplicitParams)
|
||||
CheckCXXDefaultArguments(Method);
|
||||
|
||||
|
|
|
@ -1137,7 +1137,8 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old,
|
|||
}
|
||||
|
||||
bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
|
||||
bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs) {
|
||||
bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs,
|
||||
bool ConsiderRequiresClauses) {
|
||||
// C++ [basic.start.main]p2: This function shall not be overloaded.
|
||||
if (New->isMain())
|
||||
return false;
|
||||
|
@ -1273,22 +1274,37 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
|
|||
if (getLangOpts().CUDA && ConsiderCudaAttrs) {
|
||||
// Don't allow overloading of destructors. (In theory we could, but it
|
||||
// would be a giant change to clang.)
|
||||
if (isa<CXXDestructorDecl>(New))
|
||||
return false;
|
||||
if (!isa<CXXDestructorDecl>(New)) {
|
||||
CUDAFunctionTarget NewTarget = IdentifyCUDATarget(New),
|
||||
OldTarget = IdentifyCUDATarget(Old);
|
||||
if (NewTarget != CFT_InvalidTarget) {
|
||||
assert((OldTarget != CFT_InvalidTarget) &&
|
||||
"Unexpected invalid target.");
|
||||
|
||||
CUDAFunctionTarget NewTarget = IdentifyCUDATarget(New),
|
||||
OldTarget = IdentifyCUDATarget(Old);
|
||||
if (NewTarget == CFT_InvalidTarget)
|
||||
return false;
|
||||
|
||||
assert((OldTarget != CFT_InvalidTarget) && "Unexpected invalid target.");
|
||||
|
||||
// Allow overloading of functions with same signature and different CUDA
|
||||
// target attributes.
|
||||
return NewTarget != OldTarget;
|
||||
// Allow overloading of functions with same signature and different CUDA
|
||||
// target attributes.
|
||||
if (NewTarget != OldTarget)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Concepts: Check function trailing requires clauses here.
|
||||
if (ConsiderRequiresClauses) {
|
||||
Expr *NewRC = New->getTrailingRequiresClause(),
|
||||
*OldRC = Old->getTrailingRequiresClause();
|
||||
if ((NewRC != nullptr) != (OldRC != nullptr))
|
||||
// RC are most certainly different - these are overloads.
|
||||
return true;
|
||||
|
||||
if (NewRC) {
|
||||
llvm::FoldingSetNodeID NewID, OldID;
|
||||
NewRC->Profile(NewID, Context, /*Canonical=*/true);
|
||||
OldRC->Profile(OldID, Context, /*Canonical=*/true);
|
||||
if (NewID != OldID)
|
||||
// RCs are not equivalent - these are overloads.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The signatures match; this is not an overload.
|
||||
return false;
|
||||
|
@ -6258,6 +6274,16 @@ void Sema::AddOverloadCandidate(
|
|||
return;
|
||||
}
|
||||
|
||||
if (Expr *RequiresClause = Function->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
|
||||
!Satisfaction.IsSatisfied) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the implicit conversion sequences for each of the
|
||||
// arguments.
|
||||
for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {
|
||||
|
@ -6774,6 +6800,16 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
|
|||
return;
|
||||
}
|
||||
|
||||
if (Expr *RequiresClause = Method->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
|
||||
!Satisfaction.IsSatisfied) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the implicit conversion sequences for each of the
|
||||
// arguments.
|
||||
for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {
|
||||
|
@ -7130,6 +7166,17 @@ void Sema::AddConversionCandidate(
|
|||
return;
|
||||
}
|
||||
|
||||
Expr *RequiresClause = Conversion->getTrailingRequiresClause();
|
||||
if (RequiresClause) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (CheckConstraintSatisfaction(RequiresClause, Satisfaction) ||
|
||||
!Satisfaction.IsSatisfied) {
|
||||
Candidate.Viable = false;
|
||||
Candidate.FailureKind = ovl_fail_constraints_not_satisfied;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We won't go through a user-defined type conversion function to convert a
|
||||
// derived to base as such conversions are given Conversion Rank. They only
|
||||
// go through a copy constructor. 13.3.3.1.2-p4 [over.ics.user]
|
||||
|
@ -9461,6 +9508,35 @@ bool clang::isBetterOverloadCandidate(
|
|||
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
|
||||
}
|
||||
|
||||
// -— F1 and F2 are non-template functions with the same
|
||||
// parameter-type-lists, and F1 is more constrained than F2 [...],
|
||||
if (Cand1.Function && Cand2.Function && !Cand1IsSpecialization &&
|
||||
!Cand2IsSpecialization && Cand1.Function->hasPrototype() &&
|
||||
Cand2.Function->hasPrototype()) {
|
||||
auto *PT1 = cast<FunctionProtoType>(Cand1.Function->getFunctionType());
|
||||
auto *PT2 = cast<FunctionProtoType>(Cand2.Function->getFunctionType());
|
||||
if (PT1->getNumParams() == PT2->getNumParams() &&
|
||||
PT1->isVariadic() == PT2->isVariadic() &&
|
||||
S.FunctionParamTypesAreEqual(PT1, PT2)) {
|
||||
Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
|
||||
Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
|
||||
if (RC1 && RC2) {
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (S.IsAtLeastAsConstrained(Cand1.Function, {RC1}, Cand2.Function,
|
||||
{RC2}, AtLeastAsConstrained1))
|
||||
return false;
|
||||
if (!AtLeastAsConstrained1)
|
||||
return false;
|
||||
if (S.IsAtLeastAsConstrained(Cand2.Function, {RC2}, Cand1.Function,
|
||||
{RC1}, AtLeastAsConstrained2))
|
||||
return false;
|
||||
if (!AtLeastAsConstrained2)
|
||||
return true;
|
||||
} else if (RC1 || RC2)
|
||||
return RC1 != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// -- F1 is a constructor for a class D, F2 is a constructor for a base
|
||||
// class B of D, and for all arguments the corresponding parameters of
|
||||
// F1 and F2 have the same type.
|
||||
|
@ -9829,6 +9905,24 @@ static bool checkAddressOfFunctionIsAvailable(Sema &S, const FunctionDecl *FD,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (const Expr *RC = FD->getTrailingRequiresClause()) {
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (S.CheckConstraintSatisfaction(RC, Satisfaction))
|
||||
return false;
|
||||
if (!Satisfaction.IsSatisfied) {
|
||||
if (Complain) {
|
||||
if (InOverloadResolution)
|
||||
S.Diag(FD->getBeginLoc(),
|
||||
diag::note_ovl_candidate_unsatisfied_constraints);
|
||||
else
|
||||
S.Diag(Loc, diag::err_addrof_function_constraints_not_satisfied)
|
||||
<< FD;
|
||||
S.DiagnoseUnsatisfiedConstraint(Satisfaction);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto I = llvm::find_if(FD->parameters(), [](const ParmVarDecl *P) {
|
||||
return P->hasAttr<PassObjectSizeAttr>();
|
||||
});
|
||||
|
@ -9886,6 +9980,55 @@ void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
|
|||
MaybeEmitInheritedConstructorNote(*this, Found);
|
||||
}
|
||||
|
||||
static void
|
||||
MaybeDiagnoseAmbiguousConstraints(Sema &S, ArrayRef<OverloadCandidate> Cands) {
|
||||
// Perhaps the ambiguity was caused by two atomic constraints that are
|
||||
// 'identical' but not equivalent:
|
||||
//
|
||||
// void foo() requires (sizeof(T) > 4) { } // #1
|
||||
// void foo() requires (sizeof(T) > 4) && T::value { } // #2
|
||||
//
|
||||
// The 'sizeof(T) > 4' constraints are seemingly equivalent and should cause
|
||||
// #2 to subsume #1, but these constraint are not considered equivalent
|
||||
// according to the subsumption rules because they are not the same
|
||||
// source-level construct. This behavior is quite confusing and we should try
|
||||
// to help the user figure out what happened.
|
||||
|
||||
SmallVector<const Expr *, 3> FirstAC, SecondAC;
|
||||
FunctionDecl *FirstCand = nullptr, *SecondCand = nullptr;
|
||||
for (auto I = Cands.begin(), E = Cands.end(); I != E; ++I) {
|
||||
if (!I->Function)
|
||||
continue;
|
||||
SmallVector<const Expr *, 3> AC;
|
||||
if (auto *Template = I->Function->getPrimaryTemplate())
|
||||
Template->getAssociatedConstraints(AC);
|
||||
else
|
||||
I->Function->getAssociatedConstraints(AC);
|
||||
if (AC.empty())
|
||||
continue;
|
||||
if (FirstCand == nullptr) {
|
||||
FirstCand = I->Function;
|
||||
FirstAC = AC;
|
||||
} else if (SecondCand == nullptr) {
|
||||
SecondCand = I->Function;
|
||||
SecondAC = AC;
|
||||
} else {
|
||||
// We have more than one pair of constrained functions - this check is
|
||||
// expensive and we'd rather not try to diagnose it.
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!SecondCand)
|
||||
return;
|
||||
// The diagnostic can only happen if there are associated constraints on
|
||||
// both sides (there needs to be some identical atomic constraint).
|
||||
if (S.MaybeEmitAmbiguousAtomicConstraintsDiagnostic(FirstCand, FirstAC,
|
||||
SecondCand, SecondAC))
|
||||
// Just show the user one diagnostic, they'll probably figure it out
|
||||
// from here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Notes the location of all overload candidates designated through
|
||||
// OverloadedExpr
|
||||
void Sema::NoteAllOverloadCandidates(Expr *OverloadedExpr, QualType DestType,
|
||||
|
@ -10771,6 +10914,23 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
|
|||
case ovl_non_default_multiversion_function:
|
||||
// Do nothing, these should simply be ignored.
|
||||
break;
|
||||
|
||||
case ovl_fail_constraints_not_satisfied: {
|
||||
std::string FnDesc;
|
||||
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
|
||||
ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn,
|
||||
Cand->getRewriteKind(), FnDesc);
|
||||
|
||||
S.Diag(Fn->getLocation(),
|
||||
diag::note_ovl_candidate_constraints_not_satisfied)
|
||||
<< (unsigned)FnKindPair.first << (unsigned)ocs_non_template
|
||||
<< FnDesc /* Ignored */;
|
||||
ConstraintSatisfaction Satisfaction;
|
||||
if (S.CheckConstraintSatisfaction(Fn->getTrailingRequiresClause(),
|
||||
Satisfaction))
|
||||
break;
|
||||
S.DiagnoseUnsatisfiedConstraint(Satisfaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11156,6 +11316,9 @@ void OverloadCandidateSet::NoteCandidates(PartialDiagnosticAt PD,
|
|||
S.Diag(PD.first, PD.second);
|
||||
|
||||
NoteCandidates(S, Args, Cands, Opc, OpLoc);
|
||||
|
||||
if (OCD == OCD_AmbiguousCandidates)
|
||||
MaybeDiagnoseAmbiguousConstraints(S, {begin(), end()});
|
||||
}
|
||||
|
||||
void OverloadCandidateSet::NoteCandidates(Sema &S, ArrayRef<Expr *> Args,
|
||||
|
@ -11804,15 +11967,33 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *AddressOfExpr,
|
|||
/// resolve that function to a single function that can have its address taken.
|
||||
/// This will modify `Pair` iff it returns non-null.
|
||||
///
|
||||
/// This routine can only realistically succeed if all but one candidates in the
|
||||
/// overload set for SrcExpr cannot have their addresses taken.
|
||||
/// This routine can only succeed if from all of the candidates in the overload
|
||||
/// set for SrcExpr that can have their addresses taken, there is one candidate
|
||||
/// that is more constrained than the rest.
|
||||
FunctionDecl *
|
||||
Sema::resolveAddressOfOnlyViableOverloadCandidate(Expr *E,
|
||||
DeclAccessPair &Pair) {
|
||||
Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
|
||||
OverloadExpr::FindResult R = OverloadExpr::find(E);
|
||||
OverloadExpr *Ovl = R.Expression;
|
||||
bool IsResultAmbiguous = false;
|
||||
FunctionDecl *Result = nullptr;
|
||||
DeclAccessPair DAP;
|
||||
SmallVector<FunctionDecl *, 2> AmbiguousDecls;
|
||||
|
||||
auto CheckMoreConstrained =
|
||||
[&] (FunctionDecl *FD1, FunctionDecl *FD2) -> Optional<bool> {
|
||||
SmallVector<const Expr *, 1> AC1, AC2;
|
||||
FD1->getAssociatedConstraints(AC1);
|
||||
FD2->getAssociatedConstraints(AC2);
|
||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||
if (IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1))
|
||||
return None;
|
||||
if (IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2))
|
||||
return None;
|
||||
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
|
||||
return None;
|
||||
return AtLeastAsConstrained1;
|
||||
};
|
||||
|
||||
// Don't use the AddressOfResolver because we're specifically looking for
|
||||
// cases where we have one overload candidate that lacks
|
||||
// enable_if/pass_object_size/...
|
||||
|
@ -11824,32 +12005,54 @@ Sema::resolveAddressOfOnlyViableOverloadCandidate(Expr *E,
|
|||
if (!checkAddressOfFunctionIsAvailable(FD))
|
||||
continue;
|
||||
|
||||
// We have more than one result; quit.
|
||||
if (Result)
|
||||
return nullptr;
|
||||
// We have more than one result - see if it is more constrained than the
|
||||
// previous one.
|
||||
if (Result) {
|
||||
Optional<bool> MoreConstrainedThanPrevious = CheckMoreConstrained(FD,
|
||||
Result);
|
||||
if (!MoreConstrainedThanPrevious) {
|
||||
IsResultAmbiguous = true;
|
||||
AmbiguousDecls.push_back(FD);
|
||||
continue;
|
||||
}
|
||||
if (!*MoreConstrainedThanPrevious)
|
||||
continue;
|
||||
// FD is more constrained - replace Result with it.
|
||||
}
|
||||
IsResultAmbiguous = false;
|
||||
DAP = I.getPair();
|
||||
Result = FD;
|
||||
}
|
||||
|
||||
if (Result)
|
||||
if (IsResultAmbiguous)
|
||||
return nullptr;
|
||||
|
||||
if (Result) {
|
||||
SmallVector<const Expr *, 1> ResultAC;
|
||||
// We skipped over some ambiguous declarations which might be ambiguous with
|
||||
// the selected result.
|
||||
for (FunctionDecl *Skipped : AmbiguousDecls)
|
||||
if (!CheckMoreConstrained(Skipped, Result).hasValue())
|
||||
return nullptr;
|
||||
Pair = DAP;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// Given an overloaded function, tries to turn it into a non-overloaded
|
||||
/// function reference using resolveAddressOfOnlyViableOverloadCandidate. This
|
||||
/// function reference using resolveAddressOfSingleOverloadCandidate. This
|
||||
/// will perform access checks, diagnose the use of the resultant decl, and, if
|
||||
/// requested, potentially perform a function-to-pointer decay.
|
||||
///
|
||||
/// Returns false if resolveAddressOfOnlyViableOverloadCandidate fails.
|
||||
/// Returns false if resolveAddressOfSingleOverloadCandidate fails.
|
||||
/// Otherwise, returns true. This may emit diagnostics and return true.
|
||||
bool Sema::resolveAndFixAddressOfOnlyViableOverloadCandidate(
|
||||
bool Sema::resolveAndFixAddressOfSingleOverloadCandidate(
|
||||
ExprResult &SrcExpr, bool DoFunctionPointerConverion) {
|
||||
Expr *E = SrcExpr.get();
|
||||
assert(E->getType() == Context.OverloadTy && "SrcExpr must be an overload");
|
||||
|
||||
DeclAccessPair DAP;
|
||||
FunctionDecl *Found = resolveAddressOfOnlyViableOverloadCandidate(E, DAP);
|
||||
FunctionDecl *Found = resolveAddressOfSingleOverloadCandidate(E, DAP);
|
||||
if (!Found || Found->isCPUDispatchMultiVersion() ||
|
||||
Found->isCPUSpecificMultiVersion())
|
||||
return false;
|
||||
|
|
|
@ -3750,6 +3750,11 @@ static void checkMoreSpecializedThanPrimary(Sema &S, PartialSpecDecl *Partial) {
|
|||
}
|
||||
|
||||
S.Diag(Template->getLocation(), diag::note_template_decl_here);
|
||||
SmallVector<const Expr *, 3> PartialAC, TemplateAC;
|
||||
Template->getAssociatedConstraints(TemplateAC);
|
||||
Partial->getAssociatedConstraints(PartialAC);
|
||||
S.MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Partial, PartialAC, Template,
|
||||
TemplateAC);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -3389,11 +3389,6 @@ Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
|
|||
PartialOverloading))
|
||||
return Result;
|
||||
|
||||
if (TemplateDeductionResult Result
|
||||
= CheckDeducedArgumentConstraints(*this, FunctionTemplate, Builder,
|
||||
Info))
|
||||
return Result;
|
||||
|
||||
// C++ [temp.deduct.call]p10: [DR1391]
|
||||
// If deduction succeeds for all parameters that contain
|
||||
// template-parameters that participate in template argument deduction,
|
||||
|
@ -3439,6 +3434,23 @@ Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
|
|||
return TDK_SubstitutionFailure;
|
||||
}
|
||||
|
||||
// C++2a [temp.deduct]p5
|
||||
// [...] When all template arguments have been deduced [...] all uses of
|
||||
// template parameters [...] are replaced with the corresponding deduced
|
||||
// or default argument values.
|
||||
// [...] If the function template has associated constraints
|
||||
// ([temp.constr.decl]), those constraints are checked for satisfaction
|
||||
// ([temp.constr.constr]). If the constraints are not satisfied, type
|
||||
// deduction fails.
|
||||
if (CheckInstantiatedFunctionTemplateConstraints(Info.getLocation(),
|
||||
Specialization, Builder, Info.AssociatedConstraintsSatisfaction))
|
||||
return TDK_MiscellaneousDeductionFailure;
|
||||
|
||||
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
|
||||
Info.reset(TemplateArgumentList::CreateCopy(Context, Builder));
|
||||
return TDK_ConstraintsNotSatisfied;
|
||||
}
|
||||
|
||||
if (OriginalCallArgs) {
|
||||
// C++ [temp.deduct.call]p4:
|
||||
// In general, the deduction process attempts to find template argument
|
||||
|
@ -3559,7 +3571,7 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
|
|||
|
||||
DeclAccessPair DAP;
|
||||
if (FunctionDecl *Viable =
|
||||
S.resolveAddressOfOnlyViableOverloadCandidate(Arg, DAP))
|
||||
S.resolveAddressOfSingleOverloadCandidate(Arg, DAP))
|
||||
return GetTypeOfFunction(S, R, Viable);
|
||||
|
||||
return {};
|
||||
|
|
|
@ -150,7 +150,7 @@ Sema::getTemplateInstantiationArgs(NamedDecl *D,
|
|||
break;
|
||||
|
||||
// If this function is a generic lambda specialization, we are done.
|
||||
if (isGenericLambdaCallOperatorSpecialization(Function))
|
||||
if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
|
||||
break;
|
||||
|
||||
} else if (FunctionTemplateDecl *FunTmpl
|
||||
|
|
|
@ -1845,6 +1845,18 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// FIXME: Concepts: Do not substitute into constraint expressions
|
||||
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
|
||||
if (TrailingRequiresClause) {
|
||||
ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
|
||||
TemplateArgs);
|
||||
if (SubstRC.isInvalid())
|
||||
return nullptr;
|
||||
TrailingRequiresClause = SubstRC.get();
|
||||
if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If we're instantiating a local function declaration, put the result
|
||||
// in the enclosing namespace; otherwise we need to find the instantiated
|
||||
// context.
|
||||
|
@ -1881,7 +1893,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
|
|||
Function = FunctionDecl::Create(
|
||||
SemaRef.Context, DC, D->getInnerLocStart(), NameInfo, T, TInfo,
|
||||
D->getCanonicalDecl()->getStorageClass(), D->isInlineSpecified(),
|
||||
D->hasWrittenPrototype(), D->getConstexprKind());
|
||||
D->hasWrittenPrototype(), D->getConstexprKind(),
|
||||
TrailingRequiresClause);
|
||||
Function->setRangeEnd(D->getSourceRange().getEnd());
|
||||
}
|
||||
|
||||
|
@ -1908,6 +1921,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
|
|||
Params[P]->setOwningFunction(Function);
|
||||
Function->setParams(Params);
|
||||
|
||||
if (TrailingRequiresClause)
|
||||
Function->setTrailingRequiresClause(TrailingRequiresClause);
|
||||
|
||||
if (TemplateParams) {
|
||||
// Our resulting instantiation is actually a function template, since we
|
||||
// are substituting only the outer template parameters. For example, given
|
||||
|
@ -2167,6 +2183,18 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// FIXME: Concepts: Do not substitute into constraint expressions
|
||||
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
|
||||
if (TrailingRequiresClause) {
|
||||
ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
|
||||
TemplateArgs);
|
||||
if (SubstRC.isInvalid())
|
||||
return nullptr;
|
||||
TrailingRequiresClause = SubstRC.get();
|
||||
if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DeclContext *DC = Owner;
|
||||
if (isFriend) {
|
||||
if (QualifierLoc) {
|
||||
|
@ -2199,23 +2227,27 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
|
|||
Method = CXXConstructorDecl::Create(
|
||||
SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
|
||||
InstantiatedExplicitSpecifier, Constructor->isInlineSpecified(), false,
|
||||
Constructor->getConstexprKind());
|
||||
Constructor->getConstexprKind(), InheritedConstructor(),
|
||||
TrailingRequiresClause);
|
||||
Method->setRangeEnd(Constructor->getEndLoc());
|
||||
} else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(D)) {
|
||||
Method = CXXDestructorDecl::Create(
|
||||
SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
|
||||
Destructor->isInlineSpecified(), false, Destructor->getConstexprKind());
|
||||
Destructor->isInlineSpecified(), false, Destructor->getConstexprKind(),
|
||||
TrailingRequiresClause);
|
||||
Method->setRangeEnd(Destructor->getEndLoc());
|
||||
} else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(D)) {
|
||||
Method = CXXConversionDecl::Create(
|
||||
SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
|
||||
Conversion->isInlineSpecified(), InstantiatedExplicitSpecifier,
|
||||
Conversion->getConstexprKind(), Conversion->getEndLoc());
|
||||
Conversion->getConstexprKind(), Conversion->getEndLoc(),
|
||||
TrailingRequiresClause);
|
||||
} else {
|
||||
StorageClass SC = D->isStatic() ? SC_Static : SC_None;
|
||||
Method = CXXMethodDecl::Create(SemaRef.Context, Record, StartLoc, NameInfo,
|
||||
T, TInfo, SC, D->isInlineSpecified(),
|
||||
D->getConstexprKind(), D->getEndLoc());
|
||||
D->getConstexprKind(), D->getEndLoc(),
|
||||
TrailingRequiresClause);
|
||||
}
|
||||
|
||||
if (D->isInlined())
|
||||
|
@ -4117,6 +4149,48 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
|
|||
TemplateArgs);
|
||||
}
|
||||
|
||||
bool Sema::CheckInstantiatedFunctionTemplateConstraints(
|
||||
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
|
||||
ArrayRef<TemplateArgument> TemplateArgs,
|
||||
ConstraintSatisfaction &Satisfaction) {
|
||||
// In most cases we're not going to have constraints, so check for that first.
|
||||
FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
|
||||
// Note - code synthesis context for the constraints check is created
|
||||
// inside CheckConstraintsSatisfaction.
|
||||
SmallVector<const Expr *, 3> TemplateAC;
|
||||
Template->getAssociatedConstraints(TemplateAC);
|
||||
if (TemplateAC.empty()) {
|
||||
Satisfaction.IsSatisfied = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enter the scope of this instantiation. We don't use
|
||||
// PushDeclContext because we don't have a scope.
|
||||
Sema::ContextRAII savedContext(*this, Decl);
|
||||
LocalInstantiationScope Scope(*this);
|
||||
|
||||
MultiLevelTemplateArgumentList MLTAL =
|
||||
getTemplateInstantiationArgs(Decl, nullptr, /*RelativeToPrimary*/true);
|
||||
|
||||
// If this is not an explicit specialization - we need to get the instantiated
|
||||
// version of the template arguments and add them to scope for the
|
||||
// substitution.
|
||||
if (Decl->isTemplateInstantiation()) {
|
||||
InstantiatingTemplate Inst(*this, Decl->getPointOfInstantiation(),
|
||||
InstantiatingTemplate::ConstraintsCheck{}, Decl->getPrimaryTemplate(),
|
||||
MLTAL.getInnermost(), SourceRange());
|
||||
if (Inst.isInvalid())
|
||||
return true;
|
||||
if (addInstantiatedParametersToScope(*this, Decl,
|
||||
Decl->getTemplateInstantiationPattern(),
|
||||
Scope, MLTAL))
|
||||
return true;
|
||||
}
|
||||
|
||||
return CheckConstraintSatisfaction(Template, TemplateAC, TemplateArgs,
|
||||
PointOfInstantiation, Satisfaction);
|
||||
}
|
||||
|
||||
/// Initializes the common fields of an instantiation function
|
||||
/// declaration (New) from the corresponding fields of its template (Tmpl).
|
||||
///
|
||||
|
|
|
@ -937,6 +937,10 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
|
|||
}
|
||||
}
|
||||
|
||||
if (Expr *TRC = D.getTrailingRequiresClause())
|
||||
if (TRC->containsUnexpandedParameterPack())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -11552,6 +11552,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
|||
NewCallOpType);
|
||||
}
|
||||
|
||||
// Transform the trailing requires clause
|
||||
ExprResult NewTrailingRequiresClause;
|
||||
if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause())
|
||||
// FIXME: Concepts: Substitution into requires clause should only happen
|
||||
// when checking satisfaction.
|
||||
NewTrailingRequiresClause = getDerived().TransformExpr(TRC);
|
||||
|
||||
// Create the local class that will describe the lambda.
|
||||
CXXRecordDecl *OldClass = E->getLambdaClass();
|
||||
CXXRecordDecl *Class
|
||||
|
@ -11572,7 +11579,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
|
|||
Class, E->getIntroducerRange(), NewCallOpTSI,
|
||||
E->getCallOperator()->getEndLoc(),
|
||||
NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(),
|
||||
E->getCallOperator()->getConstexprKind());
|
||||
E->getCallOperator()->getConstexprKind(),
|
||||
NewTrailingRequiresClause.get());
|
||||
|
||||
LSI->CallOperator = NewCallOperator;
|
||||
|
||||
|
|
|
@ -822,6 +822,7 @@ void ASTDeclReader::VisitDeclaratorDecl(DeclaratorDecl *DD) {
|
|||
if (Record.readInt()) { // hasExtInfo
|
||||
auto *Info = new (Reader.getContext()) DeclaratorDecl::ExtInfo();
|
||||
Record.readQualifierInfo(*Info);
|
||||
Info->TrailingRequiresClause = Record.readExpr();
|
||||
DD->DeclInfo = Info;
|
||||
}
|
||||
QualType TSIType = Record.readType();
|
||||
|
|
|
@ -523,8 +523,11 @@ void ASTDeclWriter::VisitDeclaratorDecl(DeclaratorDecl *D) {
|
|||
VisitValueDecl(D);
|
||||
Record.AddSourceLocation(D->getInnerLocStart());
|
||||
Record.push_back(D->hasExtInfo());
|
||||
if (D->hasExtInfo())
|
||||
Record.AddQualifierInfo(*D->getExtInfo());
|
||||
if (D->hasExtInfo()) {
|
||||
DeclaratorDecl::ExtInfo *Info = D->getExtInfo();
|
||||
Record.AddQualifierInfo(*Info);
|
||||
Record.AddStmt(Info->TrailingRequiresClause);
|
||||
}
|
||||
// The location information is deferred until the end of the record.
|
||||
Record.AddTypeRef(D->getTypeSourceInfo() ? D->getTypeSourceInfo()->getType()
|
||||
: QualType());
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
template<typename T>
|
||||
class A {
|
||||
virtual void f1() requires (sizeof(T) == 0);
|
||||
// expected-error@-1{{virtual function cannot have a requires clause}}
|
||||
virtual void f2() requires (sizeof(T) == 1);
|
||||
// expected-error@-1{{virtual function cannot have a requires clause}}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class B : A<T> {
|
||||
virtual void f1() requires (sizeof(T) == 0) override {}
|
||||
// expected-error@-1{{virtual function cannot have a requires clause}}
|
||||
};
|
||||
|
||||
template<typename T> struct C : T {void f() requires true; };
|
||||
// expected-error@-1{{virtual function cannot have a requires clause}}
|
||||
struct D { virtual void f(); };
|
||||
template struct C<D>;
|
||||
// expected-note@-1{{in instantiation of template class 'C<D>' requested here}}
|
|
@ -0,0 +1,35 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool is_same_v = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_same_v<T, T> = true;
|
||||
|
||||
void f1(int a) requires true; // OK
|
||||
auto f2(int a) -> bool requires true; // OK
|
||||
auto f3(int a) -> bool (*)(int b) requires true; // OK
|
||||
auto f4(int a) requires true -> bool; // expected-error{{trailing return type must appear before trailing requires clause}}
|
||||
int f5(int a) requires; // expected-error{{expected expression}}
|
||||
int f6(int a) requires {} // expected-error{{expected expression}}
|
||||
void (f7()) requires true;
|
||||
void (f8() requires true); // expected-error{{trailing requires clause should be placed outside parentheses}}
|
||||
void (*(f9 requires (true)))(); // expected-error{{trailing requires clause should be placed outside parentheses}}
|
||||
static_assert(is_same_v<decltype(f9), void (*)()>);
|
||||
void (*pf)() requires true; // expected-error{{trailing requires clause can only be used when declaring a function}}
|
||||
void g1(int (*dsdads)() requires false); // expected-error{{trailing requires clause can only be used when declaring a function}}
|
||||
void g2(int (*(*dsdads)())() requires true); // expected-error{{trailing requires clause can only be used when declaring a function}}
|
||||
void g3(int (*(*dsdads)(int) requires true)() ); // expected-error{{trailing requires clause should be placed outside parentheses}}
|
||||
using T = void ();
|
||||
T x requires true;
|
||||
struct S {
|
||||
T m1 requires true, m2 requires true;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct R {
|
||||
R(T t);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
R(T) -> R<T> requires true; // expected-error{{deduction guide cannot have a requires clause}}
|
|
@ -0,0 +1,18 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
template<typename T> requires (sizeof(T) >= 4 && sizeof(T) <= 10)
|
||||
// expected-note@-1{{because 'sizeof(char [20]) <= 10' (20 <= 10) evaluated to false}}
|
||||
// expected-note@-2{{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
|
||||
void foo() requires (sizeof(T) <= 8) {}
|
||||
// expected-note@-1{{candidate template ignored: constraints not satisfied [with T = char]}}
|
||||
// expected-note@-2{{candidate template ignored: constraints not satisfied [with T = char [9]]}}
|
||||
// expected-note@-3{{candidate template ignored: constraints not satisfied [with T = char [20]]}}
|
||||
// expected-note@-4{{because 'sizeof(char [9]) <= 8' (9 <= 8) evaluated to false}}
|
||||
|
||||
void bar() {
|
||||
foo<char>(); // expected-error{{no matching function for call to 'foo'}}
|
||||
foo<int>();
|
||||
foo<unsigned long long int>();
|
||||
foo<char[9]>(); // expected-error{{no matching function for call to 'foo'}}
|
||||
foo<char[20]>(); // expected-error{{no matching function for call to 'foo'}}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
namespace functions
|
||||
{
|
||||
void foo(int) requires false {}
|
||||
// expected-note@-1 3{{because 'false' evaluated to false}}
|
||||
// expected-note@-2 {{candidate function not viable: constraints not satisfied}}
|
||||
void bar(int) requires true {}
|
||||
|
||||
void a(int);
|
||||
void a(double);
|
||||
|
||||
void baz() {
|
||||
foo(1); // expected-error{{no matching function for call to 'foo'}}
|
||||
bar(1);
|
||||
void (*p1)(int) = foo; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
void (*p3)(int) = bar;
|
||||
decltype(foo)* a1 = nullptr; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
decltype(bar)* a2 = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace methods
|
||||
{
|
||||
template<typename T>
|
||||
struct A {
|
||||
static void foo(int) requires (sizeof(T) == 1) {} // expected-note 3{{because 'sizeof(char [2]) == 1' (2 == 1) evaluated to false}}
|
||||
static void bar(int) requires (sizeof(T) == 2) {} // expected-note 3{{because 'sizeof(char) == 2' (1 == 2) evaluated to false}}
|
||||
};
|
||||
|
||||
void baz() {
|
||||
A<char>::foo(1);
|
||||
A<char>::bar(1); // expected-error{{invalid reference to function 'bar': constraints not satisfied}}
|
||||
A<char[2]>::foo(1); // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
A<char[2]>::bar(1);
|
||||
void (*p1)(int) = A<char>::foo;
|
||||
void (*p2)(int) = A<char>::bar; // expected-error{{invalid reference to function 'bar': constraints not satisfied}}
|
||||
void (*p3)(int) = A<char[2]>::foo; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
void (*p4)(int) = A<char[2]>::bar;
|
||||
decltype(A<char>::foo)* a1 = nullptr;
|
||||
decltype(A<char>::bar)* a2 = nullptr; // expected-error{{invalid reference to function 'bar': constraints not satisfied}}
|
||||
decltype(A<char[2]>::foo)* a3 = nullptr; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
decltype(A<char[2]>::bar)* a4 = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace operators
|
||||
{
|
||||
template<typename T>
|
||||
struct A {
|
||||
A<T> operator-(A<T> b) requires (sizeof(T) == 1) { return b; } // expected-note{{because 'sizeof(int) == 1' (4 == 1) evaluated to false}}
|
||||
};
|
||||
|
||||
void baz() {
|
||||
auto* x = &A<int>::operator-; // expected-error{{invalid reference to function 'operator-': constraints not satisfied}}
|
||||
auto y = &A<char>::operator-;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
auto l1 = [] (auto x) requires (sizeof(decltype(x)) == 1) { return x; };
|
||||
// expected-note@-1{{candidate template ignored: constraints not satisfied [with $0 = int]}}
|
||||
// expected-note@-2{{because 'sizeof(decltype(x)) == 1' (4 == 1) evaluated to false}}
|
||||
|
||||
auto l1t1 = l1('a');
|
||||
auto l1t2 = l1(1);
|
||||
// expected-error@-1{{no matching function for call to object of type '(lambda at}}
|
||||
|
||||
auto l2 = [] (auto... x) requires ((sizeof(decltype(x)) >= 2) && ...) { return (x + ...); };
|
||||
// expected-note@-1{{candidate template ignored: constraints not satisfied [with $0 = <char>]}}
|
||||
// expected-note@-2{{candidate template ignored: constraints not satisfied [with $0 = <int, char>]}}
|
||||
// expected-note@-3 2{{because 'sizeof(decltype(x)) >= 2' (1 >= 2) evaluated to false}}
|
||||
|
||||
auto l2t1 = l2('a');
|
||||
// expected-error@-1{{no matching function for call to object of type '(lambda at}}
|
||||
auto l2t2 = l2(1, 'a');
|
||||
// expected-error@-1{{no matching function for call to object of type '(lambda at}}
|
||||
auto l2t3 = l2((short)1, (short)1);
|
|
@ -0,0 +1,114 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr static bool is_same_v = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr static bool is_same_v<T, T> = true;
|
||||
|
||||
namespace templates
|
||||
{
|
||||
template<typename T>
|
||||
concept AtLeast1 = sizeof(T) >= 1;
|
||||
|
||||
template<typename T>
|
||||
int foo(T t) requires (sizeof(T) == 4) { // expected-note {{candidate function}}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
char foo(T t) requires AtLeast1<T> { // expected-note {{candidate function}}
|
||||
return 'a';
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
double foo(T t) requires (AtLeast1<T> && sizeof(T) <= 2) {
|
||||
return 'a';
|
||||
}
|
||||
|
||||
static_assert(is_same_v<decltype(foo(10)), int>); // expected-error {{call to 'foo' is ambiguous}}
|
||||
static_assert(is_same_v<decltype(foo(short(10))), double>);
|
||||
|
||||
template<typename T>
|
||||
void bar() requires (sizeof(T) == 1) { }
|
||||
// expected-note@-1{{similar constraint expressions not considered equivalent}}
|
||||
// expected-note@-2{{candidate function [with T = char]}}
|
||||
|
||||
template<typename T>
|
||||
void bar() requires (sizeof(T) == 1 && sizeof(T) >= 0) { }
|
||||
// expected-note@-1{{candidate function [with T = char]}}
|
||||
// expected-note@-2{{similar constraint expression here}}
|
||||
|
||||
static_assert(is_same_v<decltype(bar<char>()), void>);
|
||||
// expected-error@-1{{call to 'bar' is ambiguous}}
|
||||
|
||||
template<typename T>
|
||||
constexpr int baz() requires AtLeast1<T> { // expected-note {{candidate function}}
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<typename T> requires AtLeast1<T>
|
||||
constexpr int baz() { // expected-note {{candidate function [with T = int]}}
|
||||
return 2;
|
||||
}
|
||||
|
||||
static_assert(baz<int>() == 1); // expected-error {{call to 'baz' is ambiguous}}
|
||||
}
|
||||
|
||||
namespace non_template
|
||||
{
|
||||
template<typename T>
|
||||
concept AtLeast2 = sizeof(T) >= 2;
|
||||
|
||||
template<typename T>
|
||||
concept AtMost8 = sizeof(T) <= 8;
|
||||
|
||||
int foo() requires AtLeast2<long> && AtMost8<long> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double foo() requires AtLeast2<long> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double baz() requires AtLeast2<long> && AtMost8<long> { // expected-note {{candidate function}}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int baz() requires AtMost8<long> && AtLeast2<long> { // expected-note {{candidate function}}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void bar() requires (sizeof(long) >= 8) { }
|
||||
// expected-note@-1 {{candidate function}}
|
||||
// expected-note@-2 {{similar constraint expressions not considered equivalent}}
|
||||
|
||||
void bar() requires (sizeof(long) >= 8 && sizeof(int) <= 30) { }
|
||||
// expected-note@-1 {{candidate function}}
|
||||
// expected-note@-2 {{similar constraint expression here}}
|
||||
|
||||
static_assert(is_same_v<decltype(foo()), int>);
|
||||
static_assert(is_same_v<decltype(baz()), int>); // expected-error {{call to 'baz' is ambiguous}}
|
||||
static_assert(is_same_v<decltype(bar()), void>); // expected-error {{call to 'bar' is ambiguous}}
|
||||
|
||||
constexpr int goo(int a) requires AtLeast2<int> && true {
|
||||
return 1;
|
||||
}
|
||||
|
||||
constexpr int goo(const int b) requires AtLeast2<int> {
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Only trailing requires clauses of redeclarations are compared for overload resolution.
|
||||
constexpr int doo(int a, ...) requires AtLeast2<int> && true { // expected-note {{candidate function}}
|
||||
return 1;
|
||||
}
|
||||
|
||||
constexpr int doo(int b) requires AtLeast2<int> { // expected-note {{candidate function}}
|
||||
return 2;
|
||||
}
|
||||
|
||||
static_assert(goo(1) == 1);
|
||||
static_assert(doo(2) == 1); // expected-error {{call to 'doo' is ambiguous}}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
struct S2 {};
|
||||
// expected-note@-1 {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'S1' to 'const S2' for 1st argument}}
|
||||
// expected-note@-2 {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'S1' to 'S2' for 1st argument}}
|
||||
// expected-note@-3 {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}}
|
||||
|
||||
struct S1 {
|
||||
void foo() const requires true {}
|
||||
void foo() const requires false {}
|
||||
void bar() const requires false {}
|
||||
// expected-note@-1 {{because 'false' evaluated to false}}
|
||||
operator bool() const requires true { return true; }
|
||||
explicit operator bool() const requires false;
|
||||
explicit operator S2() const requires false;
|
||||
// expected-note@-1 {{candidate function not viable: constraints not satisfied}}
|
||||
// expected-note@-2 {{because 'false' evaluated to false}}
|
||||
};
|
||||
|
||||
void foo() {
|
||||
S1().foo();
|
||||
S1().bar();
|
||||
// expected-error@-1 {{invalid reference to function 'bar': constraints not satisfied}}
|
||||
(void) static_cast<bool>(S1());
|
||||
(void) static_cast<S2>(S1());
|
||||
// expected-error@-1 {{no matching conversion for static_cast from 'S1' to 'S2'}}
|
||||
}
|
||||
|
||||
// Test that constraints are checked before implicit conversions are formed.
|
||||
|
||||
template<typename T>
|
||||
struct invalid_template { using X = typename T::non_existant; };
|
||||
struct A {
|
||||
template<typename T, bool=invalid_template<T>::aadasas>
|
||||
operator T() {}
|
||||
};
|
||||
|
||||
void foo(int) requires false;
|
||||
void foo(A) requires true;
|
||||
|
||||
struct S {
|
||||
void foo(int) requires false;
|
||||
void foo(A) requires true;
|
||||
S(A) requires false;
|
||||
S(double) requires true;
|
||||
~S() requires false;
|
||||
// expected-note@-1 2{{because 'false' evaluated to false}}
|
||||
~S() requires true;
|
||||
operator int() requires true;
|
||||
operator int() requires false;
|
||||
};
|
||||
|
||||
void bar() {
|
||||
foo(A{});
|
||||
S{1.}.foo(A{});
|
||||
// expected-error@-1{{invalid reference to function '~S': constraints not satisfied}}
|
||||
// Note - this behavior w.r.t. constrained dtors is a consequence of current
|
||||
// wording, which does not invoke overload resolution when a dtor is called.
|
||||
// P0848 is set to address this issue.
|
||||
S s = 1;
|
||||
// expected-error@-1{{invalid reference to function '~S': constraints not satisfied}}
|
||||
int a = s;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr static bool is_same_v = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr static bool is_same_v<T, T> = true;
|
||||
|
||||
template<typename T>
|
||||
concept AtLeast2 = sizeof(T) >= 2;
|
||||
|
||||
template<typename T>
|
||||
concept AtMost8 = sizeof(T) <= 8;
|
||||
|
||||
int foo() requires AtLeast2<long> && AtMost8<long> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double foo() requires AtLeast2<char> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
char bar() requires AtLeast2<char> { // expected-note {{possible target for call}}
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
short bar() requires AtLeast2<long> && AtMost8<long> {
|
||||
// expected-note@-1{{possible target for call}}
|
||||
// expected-note@-2{{candidate function}}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int bar() requires AtMost8<long> && AtLeast2<long> {
|
||||
// expected-note@-1{{possible target for call}}
|
||||
// expected-note@-2{{candidate function}}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
char baz() requires AtLeast2<char> {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
short baz() requires AtLeast2<long> && AtMost8<long> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int baz() requires AtMost8<long> && AtLeast2<long> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
long baz() requires AtMost8<long> && AtLeast2<long> && AtLeast2<short> {
|
||||
return 3.0;
|
||||
}
|
||||
|
||||
void a() {
|
||||
static_assert(is_same_v<decltype(&foo), int(*)()>);
|
||||
static_assert(is_same_v<decltype(&bar), long(*)()>);
|
||||
// expected-error@-1{{reference to overloaded function could not be resolved; did you mean to call it with no arguments?}}
|
||||
// expected-error@-2{{call to 'bar' is ambiguous}}
|
||||
static_assert(is_same_v<decltype(&baz), long(*)()>);
|
||||
}
|
|
@ -23,14 +23,13 @@ static_assert(is_same_v<decltype(dereference<int*>(nullptr)), int>);
|
|||
static_assert(is_same_v<decltype(dereference(2)), int>); // expected-error {{no matching function for call to 'dereference'}}
|
||||
static_assert(is_same_v<decltype(dereference<char>('a')), char>); // expected-error {{no matching function for call to 'dereference'}}
|
||||
|
||||
|
||||
template<typename T> requires T{} + T{} // expected-note {{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}}
|
||||
template<typename T> requires (T{} + T{}) // expected-note {{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}}
|
||||
auto foo(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = A]}}
|
||||
return t + t;
|
||||
}
|
||||
|
||||
|
||||
template<typename T> requires !((T{} - T{}) && (T{} + T{})) || false
|
||||
template<typename T> requires (!((T{} - T{}) && (T{} + T{})) || false)
|
||||
// expected-note@-1{{because substituted constraint expression is ill-formed: invalid operands to binary expression ('A' and 'A')}}
|
||||
// expected-note@-2{{and 'false' evaluated to false}}
|
||||
auto bar(T t) { // expected-note {{candidate template ignored: constraints not satisfied [with T = A]}}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template<typename T> requires sizeof(T) >= 2 // expected-note{{because 'sizeof(char) >= 2' (1 >= 2) evaluated to false}}
|
||||
template<typename T> requires (sizeof(T) >= 2) // expected-note{{because 'sizeof(char) >= 2' (1 >= 2) evaluated to false}}
|
||||
struct A {
|
||||
static constexpr int value = sizeof(T);
|
||||
};
|
||||
|
@ -9,8 +9,8 @@ static_assert(A<int>::value == 4);
|
|||
static_assert(A<char>::value == 1); // expected-error{{constraints not satisfied for class template 'A' [with T = char]}}
|
||||
|
||||
template<typename T, typename U>
|
||||
requires sizeof(T) != sizeof(U) // expected-note{{because 'sizeof(int) != sizeof(char [4])' (4 != 4) evaluated to false}}
|
||||
&& sizeof(T) >= 4 // expected-note{{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
|
||||
requires (sizeof(T) != sizeof(U) // expected-note{{because 'sizeof(int) != sizeof(char [4])' (4 != 4) evaluated to false}}
|
||||
&& sizeof(T) >= 4) // expected-note{{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
|
||||
constexpr int SizeDiff = sizeof(T) > sizeof(U) ? sizeof(T) - sizeof(U) : sizeof(U) - sizeof(T);
|
||||
|
||||
static_assert(SizeDiff<int, char> == 3);
|
||||
|
@ -44,16 +44,16 @@ static_assert(S<S2>::value);
|
|||
template<typename T>
|
||||
struct AA
|
||||
{
|
||||
template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
|
||||
template<typename U> requires (sizeof(U) == sizeof(T)) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
|
||||
struct B
|
||||
{
|
||||
static constexpr int a = 0;
|
||||
};
|
||||
|
||||
template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
|
||||
template<typename U> requires (sizeof(U) == sizeof(T)) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
|
||||
static constexpr int b = 1;
|
||||
|
||||
template<typename U> requires sizeof(U) == sizeof(T) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
|
||||
template<typename U> requires (sizeof(U) == sizeof(T)) // expected-note{{because 'sizeof(int [2]) == sizeof(int)' (8 == 4) evaluated to false}}
|
||||
static constexpr int getB() { // expected-note{{candidate template ignored: constraints not satisfied [with U = int [2]]}}
|
||||
return 2;
|
||||
}
|
||||
|
@ -85,8 +85,8 @@ template<typename T> requires B<T>::type // expected-note{{in instantiation of t
|
|||
// expected-note@-1{{while substituting template arguments into constraint expression here}}
|
||||
struct C { };
|
||||
|
||||
template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
template<typename T> requires (T{}) // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
struct D { };
|
||||
|
||||
static_assert(C<int>{}); // expected-note{{while checking constraint satisfaction for template 'C<int>' required here}}
|
||||
static_assert(D<int>{}); // expected-note{{while checking constraint satisfaction for template 'D<int>' required here}}
|
||||
static_assert(D<int>{}); // expected-note{{while checking constraint satisfaction for template 'D<int>' required here}}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace class_templates
|
||||
{
|
||||
template<typename T, typename U> requires sizeof(T) >= 4 // expected-note {{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
|
||||
template<typename T, typename U> requires (sizeof(T) >= 4) // expected-note {{because 'sizeof(char) >= 4' (1 >= 4) evaluated to false}}
|
||||
struct is_same { static constexpr bool value = false; };
|
||||
|
||||
template<typename T> requires sizeof(T*) >= 4 && sizeof(T) >= 4
|
||||
template<typename T> requires (sizeof(T*) >= 4 && sizeof(T) >= 4)
|
||||
struct is_same<T*, T*> { static constexpr bool value = true; };
|
||||
|
||||
static_assert(!is_same<char*, char*>::value);
|
||||
|
@ -23,7 +23,7 @@ namespace class_templates
|
|||
// expected-note@-1{{while substituting template arguments into constraint expression here}}
|
||||
struct B<T*> {};
|
||||
|
||||
template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
template<typename T> requires (T{}) // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
struct B<T**> {};
|
||||
|
||||
static_assert((B<int**>{}, true)); // expected-note{{while checking constraint satisfaction for class template partial specialization 'B<int *>' required here}}
|
||||
|
@ -35,10 +35,10 @@ namespace class_templates
|
|||
|
||||
namespace variable_templates
|
||||
{
|
||||
template<typename T, typename U> requires sizeof(T) >= 4
|
||||
template<typename T, typename U> requires (sizeof(T) >= 4)
|
||||
constexpr bool is_same_v = false;
|
||||
|
||||
template<typename T> requires sizeof(T*) >= 4 && sizeof(T) >= 4
|
||||
template<typename T> requires (sizeof(T*) >= 4 && sizeof(T) >= 4)
|
||||
constexpr bool is_same_v<T*, T*> = true;
|
||||
|
||||
static_assert(!is_same_v<char*, char*>);
|
||||
|
@ -55,7 +55,7 @@ namespace variable_templates
|
|||
// expected-note@-1{{while substituting template arguments into constraint expression here}}
|
||||
constexpr bool v1<T*> = true;
|
||||
|
||||
template<typename T> requires T{} // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
template<typename T> requires (T{}) // expected-error{{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
constexpr bool v1<T**> = true;
|
||||
|
||||
static_assert(v1<int**>); // expected-note{{while checking constraint satisfaction for variable template partial specialization 'v1<int *>' required here}}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace nodiag {
|
||||
|
||||
template <typename T> requires bool(T())
|
||||
template <typename T> requires (bool(T()))
|
||||
struct A;
|
||||
template <typename U> requires bool(U())
|
||||
template <typename U> requires (bool(U()))
|
||||
struct A;
|
||||
|
||||
} // end namespace nodiag
|
||||
|
@ -21,7 +21,7 @@ struct B;
|
|||
|
||||
template <typename T> requires true // expected-note{{previous template declaration is here}}
|
||||
struct C;
|
||||
template <typename T> requires !0 // expected-error{{requires clause differs in template redeclaration}}
|
||||
template <typename T> requires (!0) // expected-error{{requires clause differs in template redeclaration}}
|
||||
struct C;
|
||||
|
||||
} // end namespace diag
|
||||
|
@ -29,15 +29,15 @@ struct C;
|
|||
namespace nodiag {
|
||||
|
||||
struct AA {
|
||||
template <typename T> requires someFunc(T())
|
||||
template <typename T> requires (someFunc(T()))
|
||||
struct A;
|
||||
};
|
||||
|
||||
template <typename U> requires someFunc(U())
|
||||
template <typename U> requires (someFunc(U()))
|
||||
struct AA::A { };
|
||||
|
||||
struct AAF {
|
||||
template <typename T> requires someFunc(T())
|
||||
template <typename T> requires (someFunc(T()))
|
||||
friend struct AA::A;
|
||||
};
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace nodiag {
|
||||
|
||||
template <typename T> requires bool(T())
|
||||
template <typename T> requires (bool(T()))
|
||||
int A();
|
||||
template <typename U> requires bool(U())
|
||||
template <typename U> requires (bool(U()))
|
||||
int A();
|
||||
|
||||
} // end namespace nodiag
|
||||
|
@ -26,7 +26,7 @@ int orig::A();
|
|||
template <typename T> requires true
|
||||
int orig::B();
|
||||
// expected-error@-1{{out-of-line declaration of 'B' does not match any declaration in namespace 'diag::orig'}}
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
int orig::C();
|
||||
// expected-error@-1{{out-of-line declaration of 'C' does not match any declaration in namespace 'diag::orig'}}
|
||||
|
||||
|
@ -35,11 +35,11 @@ int orig::C();
|
|||
namespace nodiag {
|
||||
|
||||
struct AA {
|
||||
template <typename T> requires someFunc(T())
|
||||
template <typename T> requires (someFunc(T()))
|
||||
int A();
|
||||
};
|
||||
|
||||
template <typename T> requires someFunc(T())
|
||||
template <typename T> requires (someFunc(T()))
|
||||
int AA::A() { return sizeof(T); }
|
||||
|
||||
} // end namespace nodiag
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
namespace nodiag {
|
||||
|
||||
struct B {
|
||||
template <typename T> requires bool(T())
|
||||
template <typename T> requires (bool(T()))
|
||||
static int A;
|
||||
};
|
||||
|
||||
template <typename U> requires bool(U())
|
||||
template <typename U> requires (bool(U()))
|
||||
int B::A = int(U());
|
||||
|
||||
} // end namespace nodiag
|
||||
|
@ -15,11 +15,11 @@ int B::A = int(U());
|
|||
namespace diag {
|
||||
|
||||
struct B {
|
||||
template <typename T> requires bool(T()) // expected-note{{previous template declaration is here}}
|
||||
template <typename T> requires (bool(T())) // expected-note{{previous template declaration is here}}
|
||||
static int A;
|
||||
};
|
||||
|
||||
template <typename U> requires !bool(U()) // expected-error{{requires clause differs in template redeclaration}}
|
||||
template <typename U> requires (!bool(U())) // expected-error{{requires clause differs in template redeclaration}}
|
||||
int B::A = int(U());
|
||||
|
||||
} // end namespace diag
|
|
@ -1,9 +1,12 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template<typename T> requires sizeof(T) >= 4
|
||||
template<typename T> requires (sizeof(T) >= 4)
|
||||
// expected-note@-1{{similar constraint expressions not considered equivalen}}
|
||||
class A{}; // expected-note{{template is declared here}}
|
||||
|
||||
template<typename T> requires sizeof(T) >= 4 && sizeof(T) <= 10
|
||||
template<typename T> requires (sizeof(T) >= 4 && sizeof(T) <= 10)
|
||||
// expected-note@-1{{similar constraint expression here}}
|
||||
|
||||
class A<T>{}; // expected-error{{class template partial specialization is not more specialized than the primary template}}
|
||||
|
||||
template<typename T>
|
||||
|
@ -12,7 +15,7 @@ concept C1 = sizeof(T) >= 4;
|
|||
template<typename T> requires C1<T>
|
||||
class B{};
|
||||
|
||||
template<typename T> requires C1<T> && sizeof(T) <= 10
|
||||
template<typename T> requires (C1<T> && sizeof(T) <= 10)
|
||||
class B<T>{};
|
||||
|
||||
template<typename T>
|
||||
|
@ -48,3 +51,15 @@ struct F<T>{ enum{ value = 3 }; };
|
|||
static_assert(F<unsigned>::value == 2);
|
||||
static_assert(F<char[10]>::value == 3);
|
||||
static_assert(F<char>::value == 1);
|
||||
|
||||
// Make sure atomic constraints subsume each other only if their parameter
|
||||
// mappings are identical.
|
||||
|
||||
template<typename T, typename U> requires C2<T>
|
||||
struct I { }; // expected-note {{template is declared here}}
|
||||
|
||||
template<typename T, typename U> requires C2<U>
|
||||
struct I<T, U> { }; // expected-error {{class template partial specialization is not more specialized than the primary template}}
|
||||
|
||||
template<typename T, typename U> requires C2<T> && C2<U>
|
||||
struct I<T, U> { };
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template<typename T> requires sizeof(T) >= 4
|
||||
template<typename T> requires (sizeof(T) >= 4)
|
||||
// expected-note@-1{{similar constraint expressions not considered equivalent}}
|
||||
bool a() { return false; } // expected-note {{candidate function [with T = unsigned int]}}
|
||||
|
||||
template<typename T> requires sizeof(T) >= 4 && sizeof(T) <= 10
|
||||
template<typename T> requires (sizeof(T) >= 4 && sizeof(T) <= 10)
|
||||
// expected-note@-1{{similar constraint expression here}}
|
||||
bool a() { return true; } // expected-note {{candidate function [with T = unsigned int]}}
|
||||
|
||||
bool av = a<unsigned>(); // expected-error {{call to 'a' is ambiguous}}
|
||||
|
@ -14,7 +16,7 @@ concept C1 = sizeof(T) >= 4;
|
|||
template<typename T> requires C1<T>
|
||||
constexpr bool b() { return false; }
|
||||
|
||||
template<typename T> requires C1<T> && sizeof(T) <= 10
|
||||
template<typename T> requires (C1<T> && sizeof(T) <= 10)
|
||||
constexpr bool b() { return true; }
|
||||
|
||||
static_assert(b<int>());
|
||||
|
@ -86,4 +88,4 @@ static_assert(sizeof(g<int>()));
|
|||
template <unsigned> struct X {};
|
||||
template <class...> int h(X<0>);
|
||||
template <unsigned b, class...> int h(X<b>);
|
||||
static_assert(sizeof(h(X<0>{})));
|
||||
static_assert(sizeof(h(X<0>{})));
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template<typename T> requires sizeof(T) >= 4
|
||||
template<typename T> requires (sizeof(T) >= 4)
|
||||
// expected-note@-1{{similar constraint expressions not considered equivalent}}
|
||||
bool a = false; // expected-note{{template is declared here}}
|
||||
|
||||
template<typename T> requires sizeof(T) >= 4 && sizeof(T) <= 10
|
||||
template<typename T> requires (sizeof(T) >= 4 && sizeof(T) <= 10)
|
||||
// expected-note@-1{{similar constraint expression here}}
|
||||
bool a<T> = true; // expected-error{{variable template partial specialization is not more specialized than the primary template}}
|
||||
|
||||
template<typename T>
|
||||
|
@ -12,7 +14,7 @@ concept C1 = sizeof(T) >= 4;
|
|||
template<typename T> requires C1<T>
|
||||
bool b = false;
|
||||
|
||||
template<typename T> requires C1<T> && sizeof(T) <= 10
|
||||
template<typename T> requires (C1<T> && sizeof(T) <= 10)
|
||||
bool b<T> = true;
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template<typename T, typename S = char> requires (sizeof(T) + sizeof(S) < 10)
|
||||
// expected-note@-1{{because 'sizeof(char [100]) + sizeof(char) < 10' (101 < 10) evaluated to false}}
|
||||
void f(T t, S s) requires (sizeof(t) == 1 && sizeof(s) == 1) { };
|
||||
// expected-note@-1{{candidate template ignored: constraints not satisfied [with T = int, S = char]}}
|
||||
// expected-note@-2{{because 'sizeof (t) == 1' (4 == 1) evaluated to false}}
|
||||
// expected-note@-3{{candidate template ignored: constraints not satisfied [with T = char, S = short]}}
|
||||
// expected-note@-4{{because 'sizeof (s) == 1' (2 == 1) evaluated to false}}
|
||||
// expected-note@-5{{candidate template ignored: constraints not satisfied [with T = char [100], S = char]}}
|
||||
|
||||
template<>
|
||||
void f<int>(int t, char s) { };
|
||||
// expected-error@-1{{no function template matches function template specialization 'f'}}
|
||||
|
||||
template<>
|
||||
void f<char, short>(char t, short s) { };
|
||||
// expected-error@-1{{no function template matches function template specialization 'f'}}
|
||||
|
||||
template<>
|
||||
void f<char[100]>(char t[100], char s) { };
|
||||
// expected-error@-1{{no function template matches function template specialization 'f'}}
|
|
@ -5,25 +5,5 @@
|
|||
// the syntax is consumed without backtracking.
|
||||
|
||||
// type-specifier-seq in conversion-type-id
|
||||
template <typename T> requires (bool)&T::operator short
|
||||
unsigned int foo(); // expected-error {{C++ requires a type specifier for all declarations}}
|
||||
|
||||
// type-specifier-seq in new-type-id
|
||||
template <typename T> requires (bool)sizeof new (T::f()) short
|
||||
unsigned int bar(); // expected-error {{C++ requires a type specifier for all declarations}}
|
||||
|
||||
template<typename T> requires (bool)sizeof new (T::f()) unsigned // expected-error {{'struct' cannot be signed or unsigned}}
|
||||
struct X { }; // expected-error {{'X' cannot be defined in a type specifier}}
|
||||
|
||||
// C-style cast
|
||||
// of function call on function-style cast
|
||||
template <typename T> requires (bool(T()))
|
||||
T (*fp)(); // expected-error {{use of undeclared identifier 'fp'}}
|
||||
|
||||
// function-style cast
|
||||
// as the callee in a function call
|
||||
struct A {
|
||||
static int t;
|
||||
template <typename T> requires bool(T())
|
||||
(A(T (&t))) { } // expected-error {{called object type 'bool' is not a function or function pointer}}
|
||||
};
|
||||
template <typename T> requires T::operator short
|
||||
unsigned int foo(); // expected-error {{C++ requires a type specifier for all declarations}}
|
|
@ -1,13 +1,11 @@
|
|||
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ %s -verify
|
||||
// expected-no-diagnostics
|
||||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||
|
||||
// Test parsing of the optional requires-clause in a template-declaration.
|
||||
|
||||
template <typename T> requires true
|
||||
void foo() { }
|
||||
|
||||
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
struct A {
|
||||
void foo();
|
||||
struct AA;
|
||||
|
@ -27,31 +25,30 @@ struct A {
|
|||
using MQ = M<TT>;
|
||||
};
|
||||
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
void A<T>::foo() { }
|
||||
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
struct A<T>::AA { };
|
||||
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
enum A<T>::E : int { E0 };
|
||||
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
int A<T>::x = 0;
|
||||
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
template <typename> requires true
|
||||
void A<T>::Mfoo() { }
|
||||
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
template <typename> requires true
|
||||
struct A<T>::M { };
|
||||
|
||||
template <typename T> requires !0
|
||||
template <typename T> requires (!0)
|
||||
template <typename> requires true
|
||||
int A<T>::Mx = 0;
|
||||
|
||||
|
||||
template <typename T> requires true
|
||||
int x = 0;
|
||||
|
||||
|
@ -80,3 +77,81 @@ struct C::M { };
|
|||
|
||||
template <typename> requires true
|
||||
int C::Mx = 0;
|
||||
|
||||
// Test behavior with non-primary-expression requires clauses
|
||||
|
||||
template<typename T> requires foo<T>()
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
struct B1 { };
|
||||
|
||||
int func() { }
|
||||
|
||||
template<typename T> requires func()
|
||||
// expected-error@-1{{atomic constraint must be of type 'bool' (found '<overloaded function type>')}}
|
||||
// expected-note@-2{{parentheses are required around this expression in a requires clause}}
|
||||
struct B2 { };
|
||||
|
||||
template<typename T> requires (foo<T>())
|
||||
struct B3 { };
|
||||
|
||||
template<typename T> requires T{}
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
struct B4 { };
|
||||
|
||||
template<typename T> requires sizeof(T) == 0
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
struct B5 { };
|
||||
|
||||
template<typename T> requires (sizeof(T)) == 0
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
struct B6 { };
|
||||
|
||||
template<typename T> requires 0
|
||||
// expected-error@-1{{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
(int) bar() { };
|
||||
|
||||
template<typename T> requires foo<T>
|
||||
(int) bar() { };
|
||||
// expected-error@-1{{expected '(' for function-style cast or type construction}}
|
||||
|
||||
template<typename T>
|
||||
void bar() requires foo<T>();
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
|
||||
template<typename T>
|
||||
void bar() requires (foo<T>());
|
||||
|
||||
template<typename T>
|
||||
void bar() requires func();
|
||||
// expected-error@-1{{atomic constraint must be of type 'bool' (found '<overloaded function type>')}}
|
||||
// expected-note@-2{{parentheses are required around this expression in a requires clause}}
|
||||
|
||||
template<typename T>
|
||||
void bar() requires T{};
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
|
||||
template<typename T>
|
||||
void bar() requires sizeof(T) == 0;
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
|
||||
template<typename T>
|
||||
void bar() requires (sizeof(T)) == 0;
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
|
||||
void bar(int x, int y) requires (x, y, true);
|
||||
|
||||
struct B {
|
||||
int x;
|
||||
void foo(int y) requires (x, this, this->x, y, true);
|
||||
static void bar(int y) requires (x, true);
|
||||
// expected-error@-1{{'this' cannot be implicitly used in a static member function declaration}}
|
||||
static void baz(int y) requires (this, true);
|
||||
// expected-error@-1{{'this' cannot be used in a static member function declaration}}
|
||||
};
|
||||
|
||||
auto lambda1 = [] (auto x) requires (sizeof(decltype(x)) == 1) { };
|
||||
|
||||
auto lambda2 = [] (auto x) constexpr -> int requires (sizeof(decltype(x)) == 1) { return 0; };
|
||||
|
||||
auto lambda3 = [] requires (sizeof(char) == 1) { };
|
||||
// expected-error@-1{{lambda requires '()' before 'requires' clause}}
|
|
@ -0,0 +1,31 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||
|
||||
template <typename... Args> requires ((sizeof(Args) == 1), ...)
|
||||
// expected-note@-1 {{because '(sizeof(int) == 1) , (sizeof(char) == 1) , (sizeof(int) == 1)' evaluated to false}}
|
||||
void f1(Args&&... args) { }
|
||||
// expected-note@-1 {{candidate template ignored: constraints not satisfied [with Args = <int, char, int>]}}
|
||||
|
||||
using f11 = decltype(f1('a'));
|
||||
using f12 = decltype(f1(1, 'b'));
|
||||
using f13 = decltype(f1(1, 'b', 2));
|
||||
// expected-error@-1 {{no matching function for call to 'f1'}}
|
||||
|
||||
template <typename... Args>
|
||||
void f2(Args&&... args) requires ((sizeof(args) == 1), ...) { }
|
||||
// expected-note@-1 {{candidate template ignored: constraints not satisfied [with Args = <int, char, int>]}}
|
||||
// expected-note@-2 {{because '(sizeof (args) == 1) , (sizeof (args) == 1) , (sizeof (args) == 1)' evaluated to false}}
|
||||
|
||||
using f21 = decltype(f2('a'));
|
||||
using f22 = decltype(f2(1, 'b'));
|
||||
using f23 = decltype(f2(1, 'b', 2));
|
||||
// expected-error@-1 {{no matching function for call to 'f2'}}
|
||||
|
||||
template <typename... Args> requires ((sizeof(Args) == 1), ...)
|
||||
// expected-note@-1 {{because '(sizeof(int) == 1) , (sizeof(char) == 1) , (sizeof(int) == 1)' evaluated to false}}
|
||||
void f3(Args&&... args) requires ((sizeof(args) == 1), ...) { }
|
||||
// expected-note@-1 {{candidate template ignored: constraints not satisfied [with Args = <int, char, int>]}}
|
||||
|
||||
using f31 = decltype(f3('a'));
|
||||
using f32 = decltype(f3(1, 'b'));
|
||||
using f33 = decltype(f3(1, 'b', 2));
|
||||
// expected-error@-1 {{no matching function for call to 'f3'}}
|
Loading…
Reference in New Issue