Skip to content

Commit 0dedd6f

Browse files
authored
[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
1 parent c4ae8b1 commit 0dedd6f

File tree

9 files changed

+118
-34
lines changed

9 files changed

+118
-34
lines changed

clang/docs/ReleaseNotes.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,10 @@ Bug Fixes to C++ Support
220220
- Clang now preserves the unexpanded flag in a lambda transform used for pack expansion. (#GH56852), (#GH85667),
221221
(#GH99877).
222222
- Fixed a bug when diagnosing ambiguous explicit specializations of constrained member functions.
223-
- Fixed an assertion failure when selecting a function from an overload set that includes a
223+
- Fixed an assertion failure when selecting a function from an overload set that includes a
224224
specialization of a conversion function template.
225+
- Correctly diagnose attempts to use a concept name in its own definition;
226+
A concept name is introduced to its scope sooner to match the C++ standard. (#GH55875)
225227

226228
Bug Fixes to AST Handling
227229
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/DeclTemplate.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -3146,19 +3146,24 @@ class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> {
31463146
: TemplateDecl(Concept, DC, L, Name, Params),
31473147
ConstraintExpr(ConstraintExpr) {};
31483148
public:
3149-
static ConceptDecl *Create(ASTContext &C, DeclContext *DC,
3150-
SourceLocation L, DeclarationName Name,
3149+
static ConceptDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L,
3150+
DeclarationName Name,
31513151
TemplateParameterList *Params,
3152-
Expr *ConstraintExpr);
3152+
Expr *ConstraintExpr = nullptr);
31533153
static ConceptDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
31543154

31553155
Expr *getConstraintExpr() const {
31563156
return ConstraintExpr;
31573157
}
31583158

3159+
bool hasDefinition() const { return ConstraintExpr != nullptr; }
3160+
3161+
void setDefinition(Expr *E) { ConstraintExpr = E; }
3162+
31593163
SourceRange getSourceRange() const override LLVM_READONLY {
31603164
return SourceRange(getTemplateParameters()->getTemplateLoc(),
3161-
ConstraintExpr->getEndLoc());
3165+
ConstraintExpr ? ConstraintExpr->getEndLoc()
3166+
: SourceLocation());
31623167
}
31633168

31643169
bool isTypeConcept() const {

clang/include/clang/Basic/DiagnosticSemaKinds.td

+2
Original file line numberDiff line numberDiff line change
@@ -3012,6 +3012,8 @@ def err_concept_no_parameters : Error<
30123012
"specialization of concepts is not allowed">;
30133013
def err_concept_extra_headers : Error<
30143014
"extraneous template parameter list in concept definition">;
3015+
def err_recursive_concept : Error<
3016+
"a concept definition cannot refer to itself">;
30153017
def err_concept_no_associated_constraints : Error<
30163018
"concept cannot have associated constraints">;
30173019
def err_non_constant_constraint_expression : Error<

clang/include/clang/Sema/Sema.h

+8-5
Original file line numberDiff line numberDiff line change
@@ -12033,14 +12033,17 @@ class Sema final : public SemaBase {
1203312033

1203412034
void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD);
1203512035

12036-
Decl *ActOnConceptDefinition(Scope *S,
12037-
MultiTemplateParamsArg TemplateParameterLists,
12038-
const IdentifierInfo *Name,
12039-
SourceLocation NameLoc, Expr *ConstraintExpr,
12040-
const ParsedAttributesView &Attrs);
12036+
ConceptDecl *ActOnStartConceptDefinition(
12037+
Scope *S, MultiTemplateParamsArg TemplateParameterLists,
12038+
const IdentifierInfo *Name, SourceLocation NameLoc);
12039+
12040+
ConceptDecl *ActOnFinishConceptDefinition(Scope *S, ConceptDecl *C,
12041+
Expr *ConstraintExpr,
12042+
const ParsedAttributesView &Attrs);
1204112043

1204212044
void CheckConceptRedefinition(ConceptDecl *NewDecl, LookupResult &Previous,
1204312045
bool &AddToScope);
12046+
bool CheckConceptUseInDefinition(ConceptDecl *Concept, SourceLocation Loc);
1204412047

1204512048
TypeResult ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
1204612049
const CXXScopeSpec &SS,

clang/lib/Parse/ParseTemplate.cpp

+11-3
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
320320
const IdentifierInfo *Id = Result.Identifier;
321321
SourceLocation IdLoc = Result.getBeginLoc();
322322

323+
// [C++26][basic.scope.pdecl]/p13
324+
// The locus of a concept-definition is immediately after its concept-name.
325+
ConceptDecl *D = Actions.ActOnStartConceptDefinition(
326+
getCurScope(), *TemplateInfo.TemplateParams, Id, IdLoc);
327+
323328
ParsedAttributes Attrs(AttrFactory);
324329
MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, Attrs);
325330

@@ -339,9 +344,12 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
339344
DeclEnd = Tok.getLocation();
340345
ExpectAndConsumeSemi(diag::err_expected_semi_declaration);
341346
Expr *ConstraintExpr = ConstraintExprResult.get();
342-
return Actions.ActOnConceptDefinition(getCurScope(),
343-
*TemplateInfo.TemplateParams, Id, IdLoc,
344-
ConstraintExpr, Attrs);
347+
348+
if (!D)
349+
return nullptr;
350+
351+
return Actions.ActOnFinishConceptDefinition(getCurScope(), D, ConstraintExpr,
352+
Attrs);
345353
}
346354

347355
/// ParseTemplateParameters - Parses a template-parameter-list enclosed in

clang/lib/Sema/SemaExpr.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
306306

307307
}
308308

309+
if (auto *Concept = dyn_cast<ConceptDecl>(D);
310+
Concept && CheckConceptUseInDefinition(Concept, Loc))
311+
return true;
312+
309313
if (auto *MD = dyn_cast<CXXMethodDecl>(D)) {
310314
// Lambdas are only default-constructible or assignable in C++2a onwards.
311315
if (MD->getParent()->isLambda() &&

clang/lib/Sema/SemaTemplate.cpp

+66-17
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,9 @@ bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
10791079
return true;
10801080
}
10811081

1082+
if (CheckConceptUseInDefinition(CD, TypeConstr->TemplateNameLoc))
1083+
return true;
1084+
10821085
bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid();
10831086

10841087
if (!WereArgsSpecified &&
@@ -8447,10 +8450,9 @@ Decl *Sema::ActOnTemplateDeclarator(Scope *S,
84478450
return NewDecl;
84488451
}
84498452

8450-
Decl *Sema::ActOnConceptDefinition(
8453+
ConceptDecl *Sema::ActOnStartConceptDefinition(
84518454
Scope *S, MultiTemplateParamsArg TemplateParameterLists,
8452-
const IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr,
8453-
const ParsedAttributesView &Attrs) {
8455+
const IdentifierInfo *Name, SourceLocation NameLoc) {
84548456
DeclContext *DC = CurContext;
84558457

84568458
if (!DC->getRedeclContext()->isFileContext()) {
@@ -8486,11 +8488,8 @@ Decl *Sema::ActOnConceptDefinition(
84868488
}
84878489
}
84888490

8489-
if (DiagnoseUnexpandedParameterPack(ConstraintExpr))
8490-
return nullptr;
8491-
84928491
ConceptDecl *NewDecl =
8493-
ConceptDecl::Create(Context, DC, NameLoc, Name, Params, ConstraintExpr);
8492+
ConceptDecl::Create(Context, DC, NameLoc, Name, Params);
84948493

84958494
if (NewDecl->hasAssociatedConstraints()) {
84968495
// C++2a [temp.concept]p4:
@@ -8499,23 +8498,63 @@ Decl *Sema::ActOnConceptDefinition(
84998498
NewDecl->setInvalidDecl();
85008499
}
85018500

8501+
DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NewDecl->getBeginLoc());
8502+
LookupResult Previous(*this, NameInfo, LookupOrdinaryName,
8503+
forRedeclarationInCurContext());
8504+
LookupName(Previous, S);
8505+
FilterLookupForScope(Previous, CurContext, S, /*ConsiderLinkage=*/false,
8506+
/*AllowInlineNamespace*/ false);
8507+
8508+
// We cannot properly handle redeclarations until we parse the constraint
8509+
// expression, so only inject the name if we are sure we are not redeclaring a
8510+
// symbol
8511+
if (Previous.empty())
8512+
PushOnScopeChains(NewDecl, S, true);
8513+
8514+
return NewDecl;
8515+
}
8516+
8517+
static bool RemoveLookupResult(LookupResult &R, NamedDecl *C) {
8518+
bool Found = false;
8519+
LookupResult::Filter F = R.makeFilter();
8520+
while (F.hasNext()) {
8521+
NamedDecl *D = F.next();
8522+
if (D == C) {
8523+
F.erase();
8524+
Found = true;
8525+
break;
8526+
}
8527+
}
8528+
F.done();
8529+
return Found;
8530+
}
8531+
8532+
ConceptDecl *
8533+
Sema::ActOnFinishConceptDefinition(Scope *S, ConceptDecl *C,
8534+
Expr *ConstraintExpr,
8535+
const ParsedAttributesView &Attrs) {
8536+
assert(!C->hasDefinition() && "Concept already defined");
8537+
if (DiagnoseUnexpandedParameterPack(ConstraintExpr))
8538+
return nullptr;
8539+
C->setDefinition(ConstraintExpr);
8540+
ProcessDeclAttributeList(S, C, Attrs);
8541+
85028542
// Check for conflicting previous declaration.
8503-
DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NameLoc);
8543+
DeclarationNameInfo NameInfo(C->getDeclName(), C->getBeginLoc());
85048544
LookupResult Previous(*this, NameInfo, LookupOrdinaryName,
85058545
forRedeclarationInCurContext());
85068546
LookupName(Previous, S);
8507-
FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage=*/false,
8508-
/*AllowInlineNamespace*/false);
8547+
FilterLookupForScope(Previous, CurContext, S, /*ConsiderLinkage=*/false,
8548+
/*AllowInlineNamespace*/ false);
8549+
bool WasAlreadyAdded = RemoveLookupResult(Previous, C);
85098550
bool AddToScope = true;
8510-
CheckConceptRedefinition(NewDecl, Previous, AddToScope);
8551+
CheckConceptRedefinition(C, Previous, AddToScope);
85118552

8512-
ActOnDocumentableDecl(NewDecl);
8513-
if (AddToScope)
8514-
PushOnScopeChains(NewDecl, S);
8515-
8516-
ProcessDeclAttributeList(S, NewDecl, Attrs);
8553+
ActOnDocumentableDecl(C);
8554+
if (!WasAlreadyAdded && AddToScope)
8555+
PushOnScopeChains(C, S);
85178556

8518-
return NewDecl;
8557+
return C;
85198558
}
85208559

85218560
void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
@@ -8560,6 +8599,16 @@ void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
85608599
Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl());
85618600
}
85628601

8602+
bool Sema::CheckConceptUseInDefinition(ConceptDecl *Concept,
8603+
SourceLocation Loc) {
8604+
if (!Concept->isInvalidDecl() && !Concept->hasDefinition()) {
8605+
Diag(Loc, diag::err_recursive_concept) << Concept;
8606+
Diag(Concept->getLocation(), diag::note_declared_at);
8607+
return true;
8608+
}
8609+
return false;
8610+
}
8611+
85638612
/// \brief Strips various properties off an implicit instantiation
85648613
/// that has just been explicitly specialized.
85658614
static void StripImplicitInstantiation(NamedDecl *D, bool MinGW) {

clang/test/CXX/drs/cwg25xx.cpp

+7-3
Original file line numberDiff line numberDiff line change
@@ -201,17 +201,21 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
201201

202202
template<typename T>
203203
concept ErrorRequires = requires (ErrorRequires auto x) {
204-
// since-cxx20-error@-1 {{unknown type name 'ErrorRequires'}}
204+
// since-cxx20-error@-1 {{a concept definition cannot refer to itself}} \
205+
// since-cxx20-error@-1 {{'auto' not allowed in requires expression parameter}} \
206+
// since-cxx20-note@-1 {{declared here}}
205207
x;
206208
};
207209
static_assert(ErrorRequires<int>);
208210
// since-cxx20-error@-1 {{static assertion failed}}
209211
// since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
210212

211213
template<typename T>
212-
concept NestedErrorInRequires = requires (T x) {
214+
concept NestedErrorInRequires = requires (T x) { //
215+
// since-cxx20-note@-1 {{declared here}}
213216
requires requires (NestedErrorInRequires auto y) {
214-
// since-cxx20-error@-1 {{unknown type name 'NestedErrorInRequires'}}
217+
// since-cxx20-error@-1 {{a concept definition cannot refer to itself}} \
218+
// since-cxx20-error@-1 {{'auto' not allowed in requires expression parameter}}
215219
y;
216220
};
217221
};

clang/test/SemaTemplate/concepts.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,14 @@ template<class>
10061006
concept Irrelevant = false;
10071007

10081008
template <typename T>
1009-
concept ErrorRequires = requires(ErrorRequires auto x) { x; }; // expected-error {{unknown type name 'ErrorRequires'}}
1009+
concept ErrorRequires = requires(ErrorRequires auto x) { x; };
1010+
// expected-error@-1 {{a concept definition cannot refer to itself}} \
1011+
// expected-error@-1 {{'auto' not allowed in requires expression parameter}} \
1012+
// expected-note@-1 {{declared here}}
1013+
1014+
template<typename T> concept C1 = C1<T> && []<C1>(C1 auto) -> C1 auto {};
1015+
//expected-error@-1 4{{a concept definition cannot refer to itself}} \
1016+
//expected-note@-1 4{{declared here}}
10101017

10111018
template<class T> void aaa(T t) // expected-note {{candidate template ignored: constraints not satisfied}}
10121019
requires (False<T> || False<T>) || False<T> {} // expected-note 3 {{'int' does not satisfy 'False'}}

0 commit comments

Comments
 (0)