Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang] Adjust concept definition locus #103867

Merged
merged 2 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,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
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
13 changes: 9 additions & 4 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -3146,19 +3146,24 @@ class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> {
: 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 {
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
13 changes: 8 additions & 5 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -12033,14 +12033,17 @@ class Sema final : public SemaBase {

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,
Expand Down
14 changes: 11 additions & 3 deletions clang/lib/Parse/ParseTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() &&
Expand Down
83 changes: 66 additions & 17 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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:
Expand All @@ -8499,23 +8498,63 @@ Decl *Sema::ActOnConceptDefinition(
NewDecl->setInvalidDecl();
}

DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NewDecl->getBeginLoc());
LookupResult Previous(*this, NameInfo, LookupOrdinaryName,
forRedeclarationInCurContext());
LookupName(Previous, S);
FilterLookupForScope(Previous, CurContext, S, /*ConsiderLinkage=*/false,
/*AllowInlineNamespace*/ false);

// 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);
Comment on lines +8511 to +8512
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to restore the scope chain outside of Sema::ActOnStartConceptDefinition() if the later parsed requires expression turns out to be invalid. Otherwise, the use of the concept would still find the invalid concept Decl, and since the declaration doesn't have the expression anymore, we would run into crashes in evaluation.

Copy link
Contributor

@zyn0217 zyn0217 Sep 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or rather, I think we should inject the concept decl to CurContext in ActOnFinishConceptDefinition() instead of here.


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(NewDecl->getDeclName(), NameLoc);
DeclarationNameInfo NameInfo(C->getDeclName(), C->getBeginLoc());
LookupResult Previous(*this, NameInfo, LookupOrdinaryName,
forRedeclarationInCurContext());
LookupName(Previous, S);
FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage=*/false,
/*AllowInlineNamespace*/false);
FilterLookupForScope(Previous, CurContext, S, /*ConsiderLinkage=*/false,
/*AllowInlineNamespace*/ false);
bool WasAlreadyAdded = RemoveLookupResult(Previous, C);
bool AddToScope = true;
CheckConceptRedefinition(NewDecl, Previous, AddToScope);
CheckConceptRedefinition(C, Previous, AddToScope);

ActOnDocumentableDecl(NewDecl);
if (AddToScope)
PushOnScopeChains(NewDecl, S);

ProcessDeclAttributeList(S, NewDecl, Attrs);
ActOnDocumentableDecl(C);
if (!WasAlreadyAdded && AddToScope)
PushOnScopeChains(C, S);

return NewDecl;
return C;
}

void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
Expand Down Expand Up @@ -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) {
Expand Down
10 changes: 7 additions & 3 deletions clang/test/CXX/drs/cwg25xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,17 +201,21 @@ 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>);
// since-cxx20-error@-1 {{static assertion failed}}
// since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}

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;
};
};
Expand Down
9 changes: 8 additions & 1 deletion clang/test/SemaTemplate/concepts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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'}}
Expand Down
Loading