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 <sdkryst...@gmail.com> Date: Mon, 29 Apr 2024 15:34:40 -0400 Subject: [PATCH 1/3] [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 <sdkryst...@gmail.com> Date: Tue, 30 Apr 2024 07:44:22 -0400 Subject: [PATCH 2/3] [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@-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 <sdkryst...@gmail.com> Date: Tue, 30 Apr 2024 08:57:13 -0400 Subject: [PATCH 3/3] [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 ^^^^^^^^^^^^^^^^^^^^^^^^^ _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits