From e2e29432b7fd3d035249643fdff936f292ba5ccc Mon Sep 17 00:00:00 2001 From: tomasz-kaminski-sonarsource <79814193+tomasz-kaminski-sonarsource@users.noreply.github.com> Date: Tue, 24 Sep 2024 09:34:39 +0200 Subject: [PATCH] [C++20][Modules] NFC Reworked handling of inline for functions defined in class (#109470) Reworked handling of implicit inline marking for member and friend function defined in class. Now, we handle it in an additive manner, i.e. if such in-class functions are inline implicitly by language rules, we mark the as `setImplicitInline`, and perform no action otherwise. As we never remove inline specifier, the implementation is orthogonal to other sources of inline (like `inline`, `constexpr`, e.t.c), and we do not need to handle them specially. Also included test for `constexpr`, `consteval` and global module cases. --- clang/lib/Sema/SemaDecl.cpp | 16 +++---- .../test/CXX/class/class.friend/p7-cxx20.cpp | 47 +++++++++++++++++-- clang/test/CXX/class/class.mfct/p1-cxx20.cpp | 38 +++++++++++++-- 3 files changed, 85 insertions(+), 16 deletions(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 77fe4e2894306c..1bf0e800a36228 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9762,11 +9762,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, if (getLangOpts().CPlusPlus) { // The rules for implicit inlines changed in C++20 for methods and friends // with an in-class definition (when such a definition is not attached to - // the global module). User-specified 'inline' overrides this (set when - // the function decl is created above). + // the global module). This does not affect declarations that are already + // inline (whether explicitly or implicitly by being declared constexpr, + // consteval, etc). // FIXME: We need a better way to separate C++ standard and clang modules. bool ImplicitInlineCXX20 = !getLangOpts().CPlusPlusModules || - NewFD->isConstexpr() || NewFD->isConsteval() || !NewFD->getOwningModule() || NewFD->isFromGlobalModule() || NewFD->getOwningModule()->isHeaderLikeModule(); @@ -9774,14 +9774,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, bool isVirtual = D.getDeclSpec().isVirtualSpecified(); bool hasExplicit = D.getDeclSpec().hasExplicitSpecifier(); isFriend = D.getDeclSpec().isFriendSpecified(); - if (isFriend && !isInline && D.isFunctionDefinition()) { + if (ImplicitInlineCXX20 && isFriend && D.isFunctionDefinition()) { // Pre-C++20 [class.friend]p5 // A function can be defined in a friend declaration of a // class . . . . Such a function is implicitly inline. // Post C++20 [class.friend]p7 // Such a function is implicitly an inline function if it is attached // to the global module. - NewFD->setImplicitlyInline(ImplicitInlineCXX20); + NewFD->setImplicitlyInline(); } // If this is a method defined in an __interface, and is not a constructor @@ -10083,15 +10083,15 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, break; } - if (isa(NewFD) && DC == CurContext && - D.isFunctionDefinition() && !isInline) { + if (ImplicitInlineCXX20 && isa(NewFD) && DC == CurContext && + D.isFunctionDefinition()) { // Pre C++20 [class.mfct]p2: // A member function may be defined (8.4) in its class definition, in // which case it is an inline member function (7.1.2) // Post C++20 [class.mfct]p1: // If a member function is attached to the global module and is defined // in its class definition, it is inline. - NewFD->setImplicitlyInline(ImplicitInlineCXX20); + NewFD->setImplicitlyInline(); } if (!isFriend && SC != SC_None) { diff --git a/clang/test/CXX/class/class.friend/p7-cxx20.cpp b/clang/test/CXX/class/class.friend/p7-cxx20.cpp index 8843d55910ea2d..d034fa491d0fd7 100644 --- a/clang/test/CXX/class/class.friend/p7-cxx20.cpp +++ b/clang/test/CXX/class/class.friend/p7-cxx20.cpp @@ -46,14 +46,51 @@ module; export module M; class Z { - friend void z(){}; + friend void z1(){}; }; + +class Inline { + friend inline void z2(){}; +}; + +class Constexpr { + friend constexpr void z3(){}; +}; + +class Consteval { + friend consteval void z4(){}; +}; + +extern "C++" class GlobalModule { + friend void z5(){}; +}; + // CHECK-MOD: |-CXXRecordDecl {{.*}} <.{{/|\\\\?}}header.h:2:1, line:4:1> line:2:7 in M. hidden class A definition // CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M. hidden implicit class A // CHECK-MOD-NEXT: | `-FriendDecl {{.*}} col:15 in M. // CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} col:15 in M. hidden friend_undeclared a 'void ()' implicit-inline -// CHECK-MOD: `-CXXRecordDecl {{.*}} line:6:7 in M hidden class Z{{( ReachableWhenImported)?}} definition -// CHECK-MOD: |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}} -// CHECK-MOD-NEXT: `-FriendDecl {{.*}} col:15 in M{{( ReachableWhenImported)?}} -// CHECK-MOD-NEXT: `-FunctionDecl {{.*}} parent {{.*}} col:15 in M hidden friend_undeclared z 'void ()'{{( ReachableWhenImported)?}} +// CHECK-MOD: |-CXXRecordDecl {{.*}} line:6:7 in M hidden class Z{{( ReachableWhenImported)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-FriendDecl {{.*}} col:15 in M{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} col:15 in M hidden friend_undeclared z1 'void ()'{{( ReachableWhenImported)?}} + +// CHECK-MOD: |-CXXRecordDecl {{.*}} line:10:7 in M hidden class Inline{{( ReachableWhenImported)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Inline{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-FriendDecl {{.*}} col:22 in M{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} col:22 in M hidden friend_undeclared z2 'void ()'{{( ReachableWhenImported)?}} inline + +// CHECK-MOD: |-CXXRecordDecl {{.*}} line:14:7 in M hidden class Constexpr{{( ReachableWhenImported)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Constexpr{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-FriendDecl {{.*}} col:25 in M{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} col:25 in M hidden constexpr friend_undeclared z3 'void ()'{{( ReachableWhenImported)?}} implicit-inline + +// CHECK-MOD: |-CXXRecordDecl {{.*}} line:18:7 in M hidden class Consteval{{( ReachableWhenImported)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Consteval{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-FriendDecl {{.*}} col:25 in M{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} col:25 in M hidden consteval friend_undeclared z4 'void ()'{{( ReachableWhenImported)?}} implicit-inline + +// CHECK-MOD: `-CXXRecordDecl {{.*}} line:22:20 in M. hidden class GlobalModule{{( ReachableWhenImported)?}} definition +// CHECK-MOD: |-CXXRecordDecl {{.*}} col:20 in M. hidden implicit class GlobalModule{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: `-FriendDecl {{.*}} col:15 in M.{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: `-FunctionDecl {{.*}} parent {{.*}} col:15 in M. hidden friend_undeclared z5 'void ()'{{( ReachableWhenImported)?}} implicit-inline diff --git a/clang/test/CXX/class/class.mfct/p1-cxx20.cpp b/clang/test/CXX/class/class.mfct/p1-cxx20.cpp index 5b24668d7b6617..ce6e58ef0a15d0 100644 --- a/clang/test/CXX/class/class.mfct/p1-cxx20.cpp +++ b/clang/test/CXX/class/class.mfct/p1-cxx20.cpp @@ -48,10 +48,42 @@ class Z { void z(){}; }; +class Inline { + inline void z(){}; +}; + +class Constexpr { + constexpr void z(){}; +}; + +class Consteval { + consteval void z(){}; +}; + +extern "C++" class GlobalModule { + void z(){}; +}; + // CHECK-MOD: |-CXXRecordDecl {{.*}} <.{{/|\\\\?}}header.h:2:1, line:4:1> line:2:7 in M. hidden class A definition // CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M. hidden implicit class A // CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} col:8 in M. hidden a 'void ()' implicit-inline -// CHECK-MOD: `-CXXRecordDecl {{.*}} line:6:7 in M hidden class Z{{( ReachableWhenImported)?}} definition -// CHECK-MOD: |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}} -// CHECK-MOD-NEXT: `-CXXMethodDecl {{.*}} col:8 in M hidden z 'void ()'{{( ReachableWhenImported)?}} +// CHECK-MOD: |-CXXRecordDecl {{.*}} line:6:7 in M hidden class Z{{( ReachableWhenImported)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} col:8 in M hidden z 'void ()'{{( ReachableWhenImported)?}} + +// CHECK-MOD: |-CXXRecordDecl {{.*}} line:10:7 in M hidden class Inline{{( ReachableWhenImported)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Inline{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} col:15 in M hidden z 'void ()'{{( ReachableWhenImported)?}} inline + +// CHECK-MOD: |-CXXRecordDecl {{.*}} line:14:7 in M hidden class Constexpr{{( ReachableWhenImported)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Constexpr{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} col:18 in M hidden constexpr z 'void ()'{{( ReachableWhenImported)?}} implicit-inline + +// CHECK-MOD: |-CXXRecordDecl {{.*}} line:18:7 in M hidden class Consteval{{( ReachableWhenImported)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Consteval{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} col:18 in M hidden consteval z 'void ()'{{( ReachableWhenImported)?}} implicit-inline + +// CHECK-MOD: `-CXXRecordDecl {{.*}} line:22:20 in M. hidden class GlobalModule{{( ReachableWhenImported)?}} definition +// CHECK-MOD: |-CXXRecordDecl {{.*}} col:20 in M. hidden implicit class GlobalModule{{( ReachableWhenImported)?}} +// CHECK-MOD-NEXT: `-CXXMethodDecl {{.*}} col:8 in M. hidden z 'void ()'{{( ReachableWhenImported)?}} implicit-inline