[clang] [Clang][Sema][Parse] Delay parsing of noexcept-specifiers in friend function declarations (PR #90517)
Krystian Stasiowski via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 30 06:03:00 PDT 2024
https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/90517
>From eece4c22a0431ce64d159bf15218c1dfc1771c5f Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Mon, 29 Apr 2024 15:34:40 -0400
Subject: [PATCH 1/4] [Clang][Parse] Delay parsing of noexcept-specifiers in
friend function declarations
---
clang/lib/Parse/ParseDecl.cpp | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 7431c256d2c135..69fabf6acb84e6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7416,12 +7416,20 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
std::optional<Sema::CXXThisScopeRAII> ThisScope;
InitCXXThisScopeForDeclaratorIfRelevant(D, DS, ThisScope);
- // Parse exception-specification[opt].
- // FIXME: Per [class.mem]p6, all exception-specifications at class scope
- // should be delayed, including those for non-members (eg, friend
- // declarations). But only applying this to member declarations is
- // consistent with what other implementations do.
- bool Delayed = D.isFirstDeclarationOfMember() &&
+ // C++ [class.mem.general]p8:
+ // A complete-class context of a class (template) is a
+ // - function body,
+ // - default argument,
+ // - default template argument,
+ // - noexcept-specifier, or
+ // - default member initializer
+ // within the member-specification of the class or class template.
+ //
+ // Parse exception-specification[opt]. If we are in the
+ // member-specification of a class or class template, this is a
+ // complete-class context and parsing of the noexcept-specifier should be
+ // delayed (even if this is a friend declaration).
+ bool Delayed = D.getContext() == DeclaratorContext::Member &&
D.isFunctionDeclaratorAFunctionDeclaration();
if (Delayed && Actions.isLibstdcxxEagerExceptionSpecHack(D) &&
GetLookAheadToken(0).is(tok::kw_noexcept) &&
>From 738c5eaf1657a069cf0598f87f3ccaf336473780 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 30 Apr 2024 07:44:22 -0400
Subject: [PATCH 2/4] [FOLD] fix delayed parsing of exception specification for
friend functions
---
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/Sema/SemaDeclCXX.cpp | 38 ++++-----
clang/lib/Sema/SemaExceptionSpec.cpp | 13 ++--
.../class/class.mem/class.mem.general/p8.cpp | 78 +++++++++++++++++++
.../CXX/except/except.spec/p13-friend.cpp | 29 +++++++
5 files changed, 134 insertions(+), 26 deletions(-)
create mode 100644 clang/test/CXX/class/class.mem/class.mem.general/p8.cpp
create mode 100644 clang/test/CXX/except/except.spec/p13-friend.cpp
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 809b9c4498f697..5848c0642a03f3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4101,7 +4101,7 @@ class Sema final : public SemaBase {
/// (or member function template). The exception-specification was parsed
/// after the method itself was declared.
void actOnDelayedExceptionSpecification(
- Decl *Method, ExceptionSpecificationType EST,
+ Decl *D, ExceptionSpecificationType EST,
SourceRange SpecificationRange, ArrayRef<ParsedType> DynamicExceptions,
ArrayRef<SourceRange> DynamicExceptionRanges, Expr *NoexceptExpr);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 338b0ec1e099c0..e7a8c9ed078bfa 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -19172,21 +19172,19 @@ void Sema::checkExceptionSpecification(
}
}
-void Sema::actOnDelayedExceptionSpecification(Decl *MethodD,
- ExceptionSpecificationType EST,
- SourceRange SpecificationRange,
- ArrayRef<ParsedType> DynamicExceptions,
- ArrayRef<SourceRange> DynamicExceptionRanges,
- Expr *NoexceptExpr) {
- if (!MethodD)
+void Sema::actOnDelayedExceptionSpecification(
+ Decl *D, ExceptionSpecificationType EST, SourceRange SpecificationRange,
+ ArrayRef<ParsedType> DynamicExceptions,
+ ArrayRef<SourceRange> DynamicExceptionRanges, Expr *NoexceptExpr) {
+ if (!D)
return;
- // Dig out the method we're referring to.
- if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(MethodD))
- MethodD = FunTmpl->getTemplatedDecl();
+ // Dig out the function we're referring to.
+ if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(D))
+ D = FTD->getTemplatedDecl();
- CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(MethodD);
- if (!Method)
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+ if (!FD)
return;
// Check the exception specification.
@@ -19197,15 +19195,17 @@ void Sema::actOnDelayedExceptionSpecification(Decl *MethodD,
ESI);
// Update the exception specification on the function type.
- Context.adjustExceptionSpec(Method, ESI, /*AsWritten*/true);
+ Context.adjustExceptionSpec(FD, ESI, /*AsWritten*/ true);
- if (Method->isStatic())
- checkThisInStaticMemberFunctionExceptionSpec(Method);
+ if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (MD->isStatic())
+ checkThisInStaticMemberFunctionExceptionSpec(MD);
- if (Method->isVirtual()) {
- // Check overrides, which we previously had to delay.
- for (const CXXMethodDecl *O : Method->overridden_methods())
- CheckOverridingFunctionExceptionSpec(Method, O);
+ if (MD->isVirtual()) {
+ // Check overrides, which we previously had to delay.
+ for (const CXXMethodDecl *O : MD->overridden_methods())
+ CheckOverridingFunctionExceptionSpec(MD, O);
+ }
}
}
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index c9dd6bb2413e38..41bf273d12f2f3 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -258,13 +258,14 @@ Sema::UpdateExceptionSpec(FunctionDecl *FD,
}
static bool exceptionSpecNotKnownYet(const FunctionDecl *FD) {
- auto *MD = dyn_cast<CXXMethodDecl>(FD);
- if (!MD)
+ ExceptionSpecificationType EST =
+ FD->getType()->castAs<FunctionProtoType>()->getExceptionSpecType();
+ if (EST == EST_Unparsed)
+ return true;
+ else if (EST != EST_Unevaluated)
return false;
-
- auto EST = MD->getType()->castAs<FunctionProtoType>()->getExceptionSpecType();
- return EST == EST_Unparsed ||
- (EST == EST_Unevaluated && MD->getParent()->isBeingDefined());
+ const DeclContext *DC = FD->getLexicalDeclContext();
+ return DC->isRecord() && cast<RecordDecl>(DC)->isBeingDefined();
}
static bool CheckEquivalentExceptionSpecImpl(
diff --git a/clang/test/CXX/class/class.mem/class.mem.general/p8.cpp b/clang/test/CXX/class/class.mem/class.mem.general/p8.cpp
new file mode 100644
index 00000000000000..8cc9b41eaca919
--- /dev/null
+++ b/clang/test/CXX/class/class.mem/class.mem.general/p8.cpp
@@ -0,0 +1,78 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+namespace N0 {
+ struct A {
+ void f0() noexcept(x);
+ void g0() noexcept(y); // expected-error {{use of undeclared identifier 'y'}}
+
+ void f1() noexcept(A::x);
+ void g1() noexcept(A::y); // expected-error {{no member named 'y' in 'N0::A'}}
+
+ template<typename T>
+ void f2() noexcept(x);
+ template<typename T>
+ void g2() noexcept(y); // expected-error {{use of undeclared identifier 'y'}}
+
+ template<typename T>
+ void f3() noexcept(A::x);
+ template<typename T>
+ void g3() noexcept(A::y); // expected-error {{no member named 'y' in 'N0::A'}}
+
+ friend void f4() noexcept(x);
+ friend void g4() noexcept(y); // expected-error {{use of undeclared identifier 'y'}}
+
+ friend void f5() noexcept(A::x);
+ friend void g5() noexcept(A::y); // expected-error {{no member named 'y' in 'N0::A'}}
+
+ template<typename T>
+ friend void f6() noexcept(x);
+ template<typename T>
+ friend void g6() noexcept(y); // expected-error {{use of undeclared identifier 'y'}}
+
+ template<typename T>
+ friend void f7() noexcept(A::x);
+ template<typename T>
+ friend void g7() noexcept(A::y); // expected-error {{no member named 'y' in 'N0::A'}}
+
+ static constexpr bool x = true;
+ };
+} // namespace N0
+
+namespace N1 {
+ template<typename T>
+ struct A {
+ void f0() noexcept(x);
+ void g0() noexcept(y); // expected-error {{use of undeclared identifier 'y'}}
+
+ void f1() noexcept(A::x);
+ void g1() noexcept(A::y); // expected-error {{no member named 'y' in 'A<T>'}}
+
+ template<typename U>
+ void f2() noexcept(x);
+ template<typename U>
+ void g2() noexcept(y); // expected-error {{use of undeclared identifier 'y'}}
+
+ template<typename U>
+ void f3() noexcept(A::x);
+ template<typename U>
+ void g3() noexcept(A::y); // expected-error {{no member named 'y' in 'A<T>'}}
+
+ friend void f4() noexcept(x);
+ friend void g4() noexcept(y); // expected-error {{use of undeclared identifier 'y'}}
+
+ friend void f5() noexcept(A::x);
+ friend void g5() noexcept(A::y); // expected-error {{no member named 'y' in 'A<T>'}}
+
+ template<typename U>
+ friend void f6() noexcept(x);
+ template<typename U>
+ friend void g6() noexcept(y); // expected-error {{use of undeclared identifier 'y'}}
+
+ template<typename U>
+ friend void f7() noexcept(A::x);
+ template<typename U>
+ friend void g7() noexcept(A::y); // expected-error {{no member named 'y' in 'A<T>'}}
+
+ static constexpr bool x = true;
+ };
+} // namespace N1
diff --git a/clang/test/CXX/except/except.spec/p13-friend.cpp b/clang/test/CXX/except/except.spec/p13-friend.cpp
new file mode 100644
index 00000000000000..7f73a4ff431aa9
--- /dev/null
+++ b/clang/test/CXX/except/except.spec/p13-friend.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
+
+namespace N0 {
+ void f() noexcept;
+ void g() noexcept;
+
+ struct A {
+ friend void f() noexcept;
+ friend void g() noexcept(x);
+
+ static constexpr bool x = true;
+ };
+} // namespace N0
+
+namespace N1 {
+ void f() noexcept;
+ void g();
+
+ template<typename T>
+ struct A {
+ friend void f() noexcept;
+ // FIXME: This error is emitted if no other errors occured (i.e. Sema::hasUncompilableErrorOccurred() is false).
+ friend void g() noexcept(x); // expected-error {{no member 'x' in 'N1::A<int>'; it has not yet been instantiated}}
+ // expected-note at -1 {{in instantiation of exception specification}}
+ static constexpr bool x = false; // expected-note {{not-yet-instantiated member is declared here}}
+ };
+
+ template struct A<int>; // expected-note {{in instantiation of template class}}
+} // namespace N1
>From 78058dac5355ca8b226af2d72fba1afb5ac2db76 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 30 Apr 2024 08:57:13 -0400
Subject: [PATCH 3/4] [FOLD] add release note
---
clang/docs/ReleaseNotes.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 1abc00a25f1f42..635c964d25913f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -614,6 +614,7 @@ Bug Fixes to C++ Support
- Fix a bug on template partial specialization whose template parameter is `decltype(auto)`.
- Fix a bug on template partial specialization with issue on deduction of nontype template parameter
whose type is `decltype(auto)`. Fixes (#GH68885).
+- Clang now correctly treats the noexcept-specifier of a friend function to be a complete-class context.
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
>From 4bc4d3b4379e19f6580291a44b96a756e03d9c66 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 30 Apr 2024 09:02:47 -0400
Subject: [PATCH 4/4] [FOLD] cleanup comments
---
clang/lib/Sema/SemaDeclCXX.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index e7a8c9ed078bfa..83155c3216916e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -19190,12 +19190,12 @@ void Sema::actOnDelayedExceptionSpecification(
// Check the exception specification.
llvm::SmallVector<QualType, 4> Exceptions;
FunctionProtoType::ExceptionSpecInfo ESI;
- checkExceptionSpecification(/*IsTopLevel*/true, EST, DynamicExceptions,
+ checkExceptionSpecification(/*IsTopLevel=*/true, EST, DynamicExceptions,
DynamicExceptionRanges, NoexceptExpr, Exceptions,
ESI);
// Update the exception specification on the function type.
- Context.adjustExceptionSpec(FD, ESI, /*AsWritten*/ true);
+ Context.adjustExceptionSpec(FD, ESI, /*AsWritten=*/true);
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
if (MD->isStatic())
More information about the cfe-commits
mailing list