iains created this revision.
Herald added a project: All.
iains added reviewers: urnathan, ChuanqiXu.
iains added a subscriber: clang-modules.
iains published this revision for review.
iains added a comment.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

the other provisions of P1779 <https://reviews.llvm.org/P1779> are handled 
elsewhere:

1. private module fragment (which is also under discussion in core) and
2. implicit inline status for constexpr/consteval which has been implemented 
elsewhere (and is not modules-dependent).


This provides updates to
[class.mfct]:
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.

and
[class.friend]:
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.

We add the output of implicit-inline to the TextNodeDumper, and amend
a couple of existing tests to account for this, plus add tests for the
cases covered above.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129045

Files:
  clang/lib/AST/TextNodeDumper.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/test/AST/ast-dump-constant-expr.cpp
  clang/test/AST/ast-dump-lambda.cpp
  clang/test/CXX/class/class.friend/p7-cxx20.cpp
  clang/test/CXX/class/class.mfct/p1-cxx20.cpp

Index: clang/test/CXX/class/class.mfct/p1-cxx20.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/class/class.mfct/p1-cxx20.cpp
@@ -0,0 +1,42 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -std=c++20 no-modules.cpp -fsyntax-only -ast-dump | \
+// RUN: FileCheck --match-full-lines --check-prefix=CHECK-NM %s
+// RUN: %clang_cc1 -std=c++20 -xc++-user-header header-unit.h -ast-dump | \
+// RUN: FileCheck --match-full-lines --check-prefix=CHECK-HU %s
+// RUN: %clang_cc1 -std=c++20 module.cpp -ast-dump | \
+// RUN: FileCheck --match-full-lines --check-prefix=CHECK-MOD %s
+
+//--- no-modules.cpp
+
+class X {
+  void x(){};
+};
+
+// CHECK-NM: `-CXXRecordDecl {{.*}} <no-modules.cpp:2:1, line:4:1> line:2:7 class X definition
+// CHECK-NM:   |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 implicit class X
+// CHECK-NM-NEXT: `-CXXMethodDecl {{.*}} <line:3:3, col:12> col:8 x 'void ()' implicit-inline
+
+//--- header-unit.h
+
+class Y {
+  void y(){};
+};
+
+// CHECK-HU: `-CXXRecordDecl {{.*}} <./header-unit.h:2:1, line:4:1> line:2:7 class Y definition
+// CHECK-HU: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 implicit class Y
+// CHECK-HU-NEXT: `-CXXMethodDecl {{.*}} <line:3:3, col:12> col:8 y 'void ()' implicit-inline
+
+//--- module.cpp
+export module M;
+
+class Z {
+  void z(){};
+};
+
+// CHECK-MOD: `-CXXRecordDecl {{.*}} <module.cpp:3:1, line:5:1> line:3:7 in M hidden class Z{{( ReachableWhenImported)?}} definition
+// CHECK-MOD: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}}
+// CHECK-MOD-NEXT: `-CXXMethodDecl {{.*}} <line:4:3, col:12> col:8 in M hidden z 'void ()'{{( ReachableWhenImported)?}}
Index: clang/test/CXX/class/class.friend/p7-cxx20.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/class/class.friend/p7-cxx20.cpp
@@ -0,0 +1,45 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -std=c++20 no-modules.cpp -fsyntax-only -ast-dump | \
+// RUN: FileCheck --match-full-lines --check-prefix=CHECK-NM %s
+// RUN: %clang_cc1 -std=c++20 -xc++-user-header header-unit.h -ast-dump | \
+// RUN: FileCheck --match-full-lines --check-prefix=CHECK-HU %s
+// RUN: %clang_cc1 -std=c++20 module.cpp -ast-dump | \
+// RUN: FileCheck --match-full-lines --check-prefix=CHECK-MOD %s
+
+//--- no-modules.cpp
+
+class X {
+  friend void x(){};
+};
+
+// CHECK-NM: `-CXXRecordDecl {{.*}} <no-modules.cpp:2:1, line:4:1> line:2:7 class X definition
+// CHECK-NM:   |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 implicit class X
+// CHECK-NM-NEXT: `-FriendDecl {{.*}} <line:3:3, col:19> col:15
+// CHECK-NM-NEXT: `-FunctionDecl {{.*}} parent {{.*}} <col:3, col:19> col:15 x 'void ()' implicit-inline
+
+//--- header-unit.h
+
+class Y {
+  friend void y(){};
+};
+
+// CHECK-HU: `-CXXRecordDecl {{.*}} <./header-unit.h:2:1, line:4:1> line:2:7 class Y definition
+// CHECK-HU: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 implicit class Y
+// CHECK-HU-NEXT: `-FriendDecl {{.*}} <line:3:3, col:19> col:15
+// CHECK-HU-NEXT: `-FunctionDecl {{.*}} parent {{.*}} <col:3, col:19> col:15 y 'void ()' implicit-inline
+
+//--- module.cpp
+export module M;
+
+class Z {
+  friend void z(){};
+};
+
+// CHECK-MOD: `-CXXRecordDecl {{.*}} <module.cpp:3:1, line:5:1> line:3:7 in M hidden class Z{{( ReachableWhenImported)?}} definition
+// CHECK-MOD: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}}
+// CHECK-MOD-NEXT: `-FriendDecl {{.*}} <line:4:3, col:19> col:15 in M{{( ReachableWhenImported)?}}
+// CHECK-MOD-NEXT: `-FunctionDecl {{.*}} parent {{.*}} <col:3, col:19> col:15 in M hidden z 'void ()'{{( ReachableWhenImported)?}}
Index: clang/test/AST/ast-dump-lambda.cpp
===================================================================
--- clang/test/AST/ast-dump-lambda.cpp
+++ clang/test/AST/ast-dump-lambda.cpp
@@ -51,7 +51,7 @@
 // CHECK-NEXT:    |   | |-MoveAssignment exists simple trivial needs_implicit
 // CHECK-NEXT:    |   | `-Destructor simple irrelevant trivial needs_implicit
 // CHECK-NEXT:    |   |-CXXRecordDecl {{.*}} <col:3, col:10> col:10{{( imported)?}} implicit struct V
-// CHECK-NEXT:    |   `-CXXMethodDecl {{.*}} <line:17:5, line:20:5> line:17:10{{( imported)?}} f 'void ()'
+// CHECK-NEXT:    |   `-CXXMethodDecl {{.*}} <line:17:5, line:20:5> line:17:10{{( imported)?}} f 'void ()' implicit-inline
 // CHECK-NEXT:    |     `-CompoundStmt {{.*}} <col:14, line:20:5>
 // CHECK-NEXT:    |       |-LambdaExpr {{.*}} <line:18:7, col:15> '(lambda at {{.*}}ast-dump-lambda.cpp:18:7)'
 // CHECK-NEXT:    |       | |-CXXRecordDecl {{.*}} <col:7> col:7{{( imported)?}} implicit{{( <undeserialized declarations>)?}} class definition
Index: clang/test/AST/ast-dump-constant-expr.cpp
===================================================================
--- clang/test/AST/ast-dump-constant-expr.cpp
+++ clang/test/AST/ast-dump-constant-expr.cpp
@@ -58,7 +58,7 @@
 
 // CHECK:Dumping Test:
 // CHECK-NEXT:CXXRecordDecl {{.*}} <{{.*}}ast-dump-constant-expr.cpp:43:1, line:57:1> line:43:8 struct Test definition
-// CHECK:|-CXXMethodDecl {{.*}} <line:44:3, line:54:3> line:44:8 test 'void ()'
+// CHECK:|-CXXMethodDecl {{.*}} <line:44:3, line:54:3> line:44:8 test 'void ()' implicit-inline
 // CHECK-NEXT:| `-CompoundStmt {{.*}} <col:15, line:54:3>
 // CHECK-NEXT:|   |-CStyleCastExpr {{.*}} <line:45:5, col:20> 'void' <ToVoid>
 // CHECK-NEXT:|   | `-ConstantExpr {{.*}} <col:11, col:20> 'int'
@@ -90,4 +90,4 @@
 // CHECK-NEXT:|       `-CallExpr {{.*}} <col:11, col:23> '__int128'
 // CHECK-NEXT:|         `-ImplicitCastExpr {{.*}} <col:11> '__int128 (*)()' <FunctionToPointerDecay>
 // CHECK-NEXT:|           `-DeclRefExpr {{.*}} <col:11> '__int128 ()' lvalue Function {{.*}} 'test_Int128' '__int128 ()'
-// CHECK-NEXT:`-CXXMethodDecl {{.*}} <line:56:3, col:38> col:18 consteval consteval_method 'void ()'
\ No newline at end of file
+// CHECK-NEXT:`-CXXMethodDecl {{.*}} <line:56:3, col:38> col:18 consteval consteval_method 'void ()' implicit-inline
\ No newline at end of file
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -9353,10 +9353,18 @@
     bool hasExplicit = D.getDeclSpec().hasExplicitSpecifier();
     isFriend = D.getDeclSpec().isFriendSpecified();
     if (isFriend && !isInline && D.isFunctionDefinition()) {
-      // C++ [class.friend]p5
+      // Pre-C++20 [class.friend]p5
       //   A function can be defined in a friend declaration of a
       //   class . . . . Such a function is implicitly inline.
-      NewFD->setImplicitlyInline();
+      // Post C++20 [class.friend]p7
+      //   Such a function is implicitly an inline function if it is attached
+      //   to the global module.
+      if (getLangOpts().CPlusPlus20 || getLangOpts().CPlusPlus2b) {
+        Module *M = NewFD->getOwningModule();
+        if (!M || M->isGlobalModule())
+          NewFD->setImplicitlyInline();
+      } else
+        NewFD->setImplicitlyInline();
     }
 
     // If this is a method defined in an __interface, and is not a constructor
@@ -9640,10 +9648,18 @@
 
     if (isa<CXXMethodDecl>(NewFD) && DC == CurContext &&
         D.isFunctionDefinition()) {
-      // C++ [class.mfct]p2:
+      // 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)
-      NewFD->setImplicitlyInline();
+      // 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.
+      if (getLangOpts().CPlusPlus20 || getLangOpts().CPlusPlus2b) {
+        Module *M = NewFD->getOwningModule();
+        if (!M || M->isGlobalModule())
+          NewFD->setImplicitlyInline();
+      } else
+        NewFD->setImplicitlyInline();
     }
 
     if (SC == SC_Static && isa<CXXMethodDecl>(NewFD) &&
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -1720,6 +1720,9 @@
     }
   }
 
+  if (!D->isInlineSpecified() && D->isInlined()) {
+    OS << " implicit-inline";
+  }
   // Since NumParams comes from the FunctionProtoType of the FunctionDecl and
   // the Params are set later, it is possible for a dump during debugging to
   // encounter a FunctionDecl that has been created but hasn't been assigned
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to