[Clang] Adjust concept definition locus (#103867)
Per [basic.scope], the locus of a concept is immediately after the introduction of its name. This let us provide better diagnostics for attempt to define recursive concepts. Note that recursive concepts are not supported per https://eel.is/c++draft/basic#scope.pdecl-note-3, but there is no normative wording for that restriction. This is a known defect introduced by [p1787r6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1787r6.html). Fixes #55875
This commit is contained in:
@@ -220,8 +220,10 @@ Bug Fixes to C++ Support
|
||||
- Clang now preserves the unexpanded flag in a lambda transform used for pack expansion. (#GH56852), (#GH85667),
|
||||
(#GH99877).
|
||||
- Fixed a bug when diagnosing ambiguous explicit specializations of constrained member functions.
|
||||
- Fixed an assertion failure when selecting a function from an overload set that includes a
|
||||
- Fixed an assertion failure when selecting a function from an overload set that includes a
|
||||
specialization of a conversion function template.
|
||||
- Correctly diagnose attempts to use a concept name in its own definition;
|
||||
A concept name is introduced to its scope sooner to match the C++ standard. (#GH55875)
|
||||
|
||||
Bug Fixes to AST Handling
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -3146,19 +3146,24 @@ protected:
|
||||
: TemplateDecl(Concept, DC, L, Name, Params),
|
||||
ConstraintExpr(ConstraintExpr) {};
|
||||
public:
|
||||
static ConceptDecl *Create(ASTContext &C, DeclContext *DC,
|
||||
SourceLocation L, DeclarationName Name,
|
||||
static ConceptDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L,
|
||||
DeclarationName Name,
|
||||
TemplateParameterList *Params,
|
||||
Expr *ConstraintExpr);
|
||||
Expr *ConstraintExpr = nullptr);
|
||||
static ConceptDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
|
||||
|
||||
Expr *getConstraintExpr() const {
|
||||
return ConstraintExpr;
|
||||
}
|
||||
|
||||
bool hasDefinition() const { return ConstraintExpr != nullptr; }
|
||||
|
||||
void setDefinition(Expr *E) { ConstraintExpr = E; }
|
||||
|
||||
SourceRange getSourceRange() const override LLVM_READONLY {
|
||||
return SourceRange(getTemplateParameters()->getTemplateLoc(),
|
||||
ConstraintExpr->getEndLoc());
|
||||
ConstraintExpr ? ConstraintExpr->getEndLoc()
|
||||
: SourceLocation());
|
||||
}
|
||||
|
||||
bool isTypeConcept() const {
|
||||
|
||||
@@ -3012,6 +3012,8 @@ def err_concept_no_parameters : Error<
|
||||
"specialization of concepts is not allowed">;
|
||||
def err_concept_extra_headers : Error<
|
||||
"extraneous template parameter list in concept definition">;
|
||||
def err_recursive_concept : Error<
|
||||
"a concept definition cannot refer to itself">;
|
||||
def err_concept_no_associated_constraints : Error<
|
||||
"concept cannot have associated constraints">;
|
||||
def err_non_constant_constraint_expression : Error<
|
||||
|
||||
@@ -12033,14 +12033,17 @@ public:
|
||||
|
||||
void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD);
|
||||
|
||||
Decl *ActOnConceptDefinition(Scope *S,
|
||||
MultiTemplateParamsArg TemplateParameterLists,
|
||||
const IdentifierInfo *Name,
|
||||
SourceLocation NameLoc, Expr *ConstraintExpr,
|
||||
const ParsedAttributesView &Attrs);
|
||||
ConceptDecl *ActOnStartConceptDefinition(
|
||||
Scope *S, MultiTemplateParamsArg TemplateParameterLists,
|
||||
const IdentifierInfo *Name, SourceLocation NameLoc);
|
||||
|
||||
ConceptDecl *ActOnFinishConceptDefinition(Scope *S, ConceptDecl *C,
|
||||
Expr *ConstraintExpr,
|
||||
const ParsedAttributesView &Attrs);
|
||||
|
||||
void CheckConceptRedefinition(ConceptDecl *NewDecl, LookupResult &Previous,
|
||||
bool &AddToScope);
|
||||
bool CheckConceptUseInDefinition(ConceptDecl *Concept, SourceLocation Loc);
|
||||
|
||||
TypeResult ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
||||
const CXXScopeSpec &SS,
|
||||
|
||||
@@ -320,6 +320,11 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
|
||||
const IdentifierInfo *Id = Result.Identifier;
|
||||
SourceLocation IdLoc = Result.getBeginLoc();
|
||||
|
||||
// [C++26][basic.scope.pdecl]/p13
|
||||
// The locus of a concept-definition is immediately after its concept-name.
|
||||
ConceptDecl *D = Actions.ActOnStartConceptDefinition(
|
||||
getCurScope(), *TemplateInfo.TemplateParams, Id, IdLoc);
|
||||
|
||||
ParsedAttributes Attrs(AttrFactory);
|
||||
MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, Attrs);
|
||||
|
||||
@@ -339,9 +344,12 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
|
||||
DeclEnd = Tok.getLocation();
|
||||
ExpectAndConsumeSemi(diag::err_expected_semi_declaration);
|
||||
Expr *ConstraintExpr = ConstraintExprResult.get();
|
||||
return Actions.ActOnConceptDefinition(getCurScope(),
|
||||
*TemplateInfo.TemplateParams, Id, IdLoc,
|
||||
ConstraintExpr, Attrs);
|
||||
|
||||
if (!D)
|
||||
return nullptr;
|
||||
|
||||
return Actions.ActOnFinishConceptDefinition(getCurScope(), D, ConstraintExpr,
|
||||
Attrs);
|
||||
}
|
||||
|
||||
/// ParseTemplateParameters - Parses a template-parameter-list enclosed in
|
||||
|
||||
@@ -306,6 +306,10 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
|
||||
|
||||
}
|
||||
|
||||
if (auto *Concept = dyn_cast<ConceptDecl>(D);
|
||||
Concept && CheckConceptUseInDefinition(Concept, Loc))
|
||||
return true;
|
||||
|
||||
if (auto *MD = dyn_cast<CXXMethodDecl>(D)) {
|
||||
// Lambdas are only default-constructible or assignable in C++2a onwards.
|
||||
if (MD->getParent()->isLambda() &&
|
||||
|
||||
@@ -1079,6 +1079,9 @@ bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckConceptUseInDefinition(CD, TypeConstr->TemplateNameLoc))
|
||||
return true;
|
||||
|
||||
bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid();
|
||||
|
||||
if (!WereArgsSpecified &&
|
||||
@@ -8447,10 +8450,9 @@ Decl *Sema::ActOnTemplateDeclarator(Scope *S,
|
||||
return NewDecl;
|
||||
}
|
||||
|
||||
Decl *Sema::ActOnConceptDefinition(
|
||||
ConceptDecl *Sema::ActOnStartConceptDefinition(
|
||||
Scope *S, MultiTemplateParamsArg TemplateParameterLists,
|
||||
const IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr,
|
||||
const ParsedAttributesView &Attrs) {
|
||||
const IdentifierInfo *Name, SourceLocation NameLoc) {
|
||||
DeclContext *DC = CurContext;
|
||||
|
||||
if (!DC->getRedeclContext()->isFileContext()) {
|
||||
@@ -8486,11 +8488,8 @@ Decl *Sema::ActOnConceptDefinition(
|
||||
}
|
||||
}
|
||||
|
||||
if (DiagnoseUnexpandedParameterPack(ConstraintExpr))
|
||||
return nullptr;
|
||||
|
||||
ConceptDecl *NewDecl =
|
||||
ConceptDecl::Create(Context, DC, NameLoc, Name, Params, ConstraintExpr);
|
||||
ConceptDecl::Create(Context, DC, NameLoc, Name, Params);
|
||||
|
||||
if (NewDecl->hasAssociatedConstraints()) {
|
||||
// C++2a [temp.concept]p4:
|
||||
@@ -8499,25 +8498,65 @@ Decl *Sema::ActOnConceptDefinition(
|
||||
NewDecl->setInvalidDecl();
|
||||
}
|
||||
|
||||
// Check for conflicting previous declaration.
|
||||
DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NameLoc);
|
||||
DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NewDecl->getBeginLoc());
|
||||
LookupResult Previous(*this, NameInfo, LookupOrdinaryName,
|
||||
forRedeclarationInCurContext());
|
||||
LookupName(Previous, S);
|
||||
FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage=*/false,
|
||||
/*AllowInlineNamespace*/false);
|
||||
bool AddToScope = true;
|
||||
CheckConceptRedefinition(NewDecl, Previous, AddToScope);
|
||||
FilterLookupForScope(Previous, CurContext, S, /*ConsiderLinkage=*/false,
|
||||
/*AllowInlineNamespace*/ false);
|
||||
|
||||
ActOnDocumentableDecl(NewDecl);
|
||||
if (AddToScope)
|
||||
PushOnScopeChains(NewDecl, S);
|
||||
|
||||
ProcessDeclAttributeList(S, NewDecl, Attrs);
|
||||
// We cannot properly handle redeclarations until we parse the constraint
|
||||
// expression, so only inject the name if we are sure we are not redeclaring a
|
||||
// symbol
|
||||
if (Previous.empty())
|
||||
PushOnScopeChains(NewDecl, S, true);
|
||||
|
||||
return NewDecl;
|
||||
}
|
||||
|
||||
static bool RemoveLookupResult(LookupResult &R, NamedDecl *C) {
|
||||
bool Found = false;
|
||||
LookupResult::Filter F = R.makeFilter();
|
||||
while (F.hasNext()) {
|
||||
NamedDecl *D = F.next();
|
||||
if (D == C) {
|
||||
F.erase();
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
F.done();
|
||||
return Found;
|
||||
}
|
||||
|
||||
ConceptDecl *
|
||||
Sema::ActOnFinishConceptDefinition(Scope *S, ConceptDecl *C,
|
||||
Expr *ConstraintExpr,
|
||||
const ParsedAttributesView &Attrs) {
|
||||
assert(!C->hasDefinition() && "Concept already defined");
|
||||
if (DiagnoseUnexpandedParameterPack(ConstraintExpr))
|
||||
return nullptr;
|
||||
C->setDefinition(ConstraintExpr);
|
||||
ProcessDeclAttributeList(S, C, Attrs);
|
||||
|
||||
// Check for conflicting previous declaration.
|
||||
DeclarationNameInfo NameInfo(C->getDeclName(), C->getBeginLoc());
|
||||
LookupResult Previous(*this, NameInfo, LookupOrdinaryName,
|
||||
forRedeclarationInCurContext());
|
||||
LookupName(Previous, S);
|
||||
FilterLookupForScope(Previous, CurContext, S, /*ConsiderLinkage=*/false,
|
||||
/*AllowInlineNamespace*/ false);
|
||||
bool WasAlreadyAdded = RemoveLookupResult(Previous, C);
|
||||
bool AddToScope = true;
|
||||
CheckConceptRedefinition(C, Previous, AddToScope);
|
||||
|
||||
ActOnDocumentableDecl(C);
|
||||
if (!WasAlreadyAdded && AddToScope)
|
||||
PushOnScopeChains(C, S);
|
||||
|
||||
return C;
|
||||
}
|
||||
|
||||
void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
|
||||
LookupResult &Previous, bool &AddToScope) {
|
||||
AddToScope = true;
|
||||
@@ -8560,6 +8599,16 @@ void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
|
||||
Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl());
|
||||
}
|
||||
|
||||
bool Sema::CheckConceptUseInDefinition(ConceptDecl *Concept,
|
||||
SourceLocation Loc) {
|
||||
if (!Concept->isInvalidDecl() && !Concept->hasDefinition()) {
|
||||
Diag(Loc, diag::err_recursive_concept) << Concept;
|
||||
Diag(Concept->getLocation(), diag::note_declared_at);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Strips various properties off an implicit instantiation
|
||||
/// that has just been explicitly specialized.
|
||||
static void StripImplicitInstantiation(NamedDecl *D, bool MinGW) {
|
||||
|
||||
@@ -201,7 +201,9 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
|
||||
|
||||
template<typename T>
|
||||
concept ErrorRequires = requires (ErrorRequires auto x) {
|
||||
// since-cxx20-error@-1 {{unknown type name 'ErrorRequires'}}
|
||||
// since-cxx20-error@-1 {{a concept definition cannot refer to itself}} \
|
||||
// since-cxx20-error@-1 {{'auto' not allowed in requires expression parameter}} \
|
||||
// since-cxx20-note@-1 {{declared here}}
|
||||
x;
|
||||
};
|
||||
static_assert(ErrorRequires<int>);
|
||||
@@ -209,9 +211,11 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
|
||||
// since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
||||
|
||||
template<typename T>
|
||||
concept NestedErrorInRequires = requires (T x) {
|
||||
concept NestedErrorInRequires = requires (T x) { //
|
||||
// since-cxx20-note@-1 {{declared here}}
|
||||
requires requires (NestedErrorInRequires auto y) {
|
||||
// since-cxx20-error@-1 {{unknown type name 'NestedErrorInRequires'}}
|
||||
// since-cxx20-error@-1 {{a concept definition cannot refer to itself}} \
|
||||
// since-cxx20-error@-1 {{'auto' not allowed in requires expression parameter}}
|
||||
y;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1006,7 +1006,14 @@ template<class>
|
||||
concept Irrelevant = false;
|
||||
|
||||
template <typename T>
|
||||
concept ErrorRequires = requires(ErrorRequires auto x) { x; }; // expected-error {{unknown type name 'ErrorRequires'}}
|
||||
concept ErrorRequires = requires(ErrorRequires auto x) { x; };
|
||||
// expected-error@-1 {{a concept definition cannot refer to itself}} \
|
||||
// expected-error@-1 {{'auto' not allowed in requires expression parameter}} \
|
||||
// expected-note@-1 {{declared here}}
|
||||
|
||||
template<typename T> concept C1 = C1<T> && []<C1>(C1 auto) -> C1 auto {};
|
||||
//expected-error@-1 4{{a concept definition cannot refer to itself}} \
|
||||
//expected-note@-1 4{{declared here}}
|
||||
|
||||
template<class T> void aaa(T t) // expected-note {{candidate template ignored: constraints not satisfied}}
|
||||
requires (False<T> || False<T>) || False<T> {} // expected-note 3 {{'int' does not satisfy 'False'}}
|
||||
|
||||
Reference in New Issue
Block a user