[clang] 97af17c - re-land [C++20][Modules] Update handling of implicit inlines [P1779R3]
Iain Sandoe via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 21 01:18:17 PDT 2022
Author: Iain Sandoe
Date: 2022-07-21T09:17:01+01:00
New Revision: 97af17c5cae64f5fd5e89806e8cf20f50fec30ec
URL: https://github.com/llvm/llvm-project/commit/97af17c5cae64f5fd5e89806e8cf20f50fec30ec
DIFF: https://github.com/llvm/llvm-project/commit/97af17c5cae64f5fd5e89806e8cf20f50fec30ec.diff
LOG: re-land [C++20][Modules] Update handling of implicit inlines [P1779R3]
re-land fixes an unwanted interaction with module-map modules, seen in
Greendragon testing.
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.
Differential Revision: https://reviews.llvm.org/D129045
Added:
clang/test/CXX/class/class.friend/p7-cxx20.cpp
clang/test/CXX/class/class.mfct/p1-cxx20.cpp
Modified:
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
Removed:
################################################################################
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 79e9fa6ab86fc..22643d4edbecb 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1720,6 +1720,9 @@ void TextNodeDumper::VisitFunctionDecl(const FunctionDecl *D) {
}
}
+ 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
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5a546503ccedc..736c299f38bb8 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9411,15 +9411,27 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
NewFD->setLocalExternDecl();
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).
+ // FIXME: We need a better way to separate C++ standard and clang modules.
+ bool ImplicitInlineCXX20 = !getLangOpts().CPlusPlusModules ||
+ !NewFD->getOwningModule() ||
+ NewFD->getOwningModule()->isGlobalModule() ||
+ NewFD->getOwningModule()->isModuleMapModule();
bool isInline = D.getDeclSpec().isInlineSpecified();
bool isVirtual = D.getDeclSpec().isVirtualSpecified();
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.
+ NewFD->setImplicitlyInline(ImplicitInlineCXX20);
}
// If this is a method defined in an __interface, and is not a constructor
@@ -9702,11 +9714,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
if (isa<CXXMethodDecl>(NewFD) && DC == CurContext &&
- D.isFunctionDefinition()) {
- // C++ [class.mfct]p2:
+ D.isFunctionDefinition() && !isInline) {
+ // 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.
+ NewFD->setImplicitlyInline(ImplicitInlineCXX20);
}
if (SC == SC_Static && isa<CXXMethodDecl>(NewFD) &&
diff --git a/clang/test/AST/ast-dump-constant-expr.cpp b/clang/test/AST/ast-dump-constant-expr.cpp
index 79cdfc639af5b..302f562eadd96 100644
--- a/clang/test/AST/ast-dump-constant-expr.cpp
+++ b/clang/test/AST/ast-dump-constant-expr.cpp
@@ -58,7 +58,7 @@ struct Test {
// 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 @@ struct Test {
// 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
diff --git a/clang/test/AST/ast-dump-lambda.cpp b/clang/test/AST/ast-dump-lambda.cpp
index d93cf4a96b493..bd1b1beef15fb 100644
--- a/clang/test/AST/ast-dump-lambda.cpp
+++ b/clang/test/AST/ast-dump-lambda.cpp
@@ -51,7 +51,7 @@ template <typename... Ts> void test(Ts... a) {
// 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
diff --git a/clang/test/CXX/class/class.friend/p7-cxx20.cpp b/clang/test/CXX/class/class.friend/p7-cxx20.cpp
new file mode 100644
index 0000000000000..367e64f233576
--- /dev/null
+++ b/clang/test/CXX/class/class.friend/p7-cxx20.cpp
@@ -0,0 +1,59 @@
+// 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
+
+// A textually-included header
+//--- header.h
+
+class A {
+ friend void a(){};
+};
+
+//--- module.cpp
+module;
+#include "header.h"
+
+export module M;
+
+class Z {
+ friend void z(){};
+};
+// CHECK-MOD: |-CXXRecordDecl {{.*}} <./header.h:2:1, line:4:1> line:2:7 in M.<global> hidden class A definition
+// CHECK-MOD: | |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 in M.<global> hidden implicit class A
+// CHECK-MOD-NEXT: | `-FriendDecl {{.*}} <line:3:3, col:19> col:15 in M.<global>
+// CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} <col:3, col:19> col:15 in M.<global> hidden a 'void ()' implicit-inline
+
+// CHECK-MOD: `-CXXRecordDecl {{.*}} <module.cpp:6:1, line:8:1> line:6: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:7: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)?}}
diff --git a/clang/test/CXX/class/class.mfct/p1-cxx20.cpp b/clang/test/CXX/class/class.mfct/p1-cxx20.cpp
new file mode 100644
index 0000000000000..f8c91b139e3ec
--- /dev/null
+++ b/clang/test/CXX/class/class.mfct/p1-cxx20.cpp
@@ -0,0 +1,57 @@
+// 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
+
+// A header unit header
+//--- 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
+
+// A textually-included header
+//--- header.h
+
+class A {
+ void a(){};
+};
+
+//--- module.cpp
+module;
+#include "header.h"
+
+export module M;
+
+class Z {
+ void z(){};
+};
+
+// CHECK-MOD: |-CXXRecordDecl {{.*}} <./header.h:2:1, line:4:1> line:2:7 in M.<global> hidden class A definition
+// CHECK-MOD: | |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 in M.<global> hidden implicit class A
+// CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} <line:3:3, col:12> col:8 in M.<global> hidden a 'void ()' implicit-inline
+
+// CHECK-MOD: `-CXXRecordDecl {{.*}} <module.cpp:6:1, line:8:1> line:6: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:7:3, col:12> col:8 in M hidden z 'void ()'{{( ReachableWhenImported)?}}
More information about the cfe-commits
mailing list