Skip to content

Commit 97af17c

Browse files
committed
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
1 parent 2ed7c3f commit 97af17c

File tree

6 files changed

+142
-8
lines changed

6 files changed

+142
-8
lines changed

clang/lib/AST/TextNodeDumper.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,9 @@ void TextNodeDumper::VisitFunctionDecl(const FunctionDecl *D) {
17201720
}
17211721
}
17221722

1723+
if (!D->isInlineSpecified() && D->isInlined()) {
1724+
OS << " implicit-inline";
1725+
}
17231726
// Since NumParams comes from the FunctionProtoType of the FunctionDecl and
17241727
// the Params are set later, it is possible for a dump during debugging to
17251728
// encounter a FunctionDecl that has been created but hasn't been assigned

clang/lib/Sema/SemaDecl.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9411,15 +9411,27 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
94119411
NewFD->setLocalExternDecl();
94129412

94139413
if (getLangOpts().CPlusPlus) {
9414+
// The rules for implicit inlines changed in C++20 for methods and friends
9415+
// with an in-class definition (when such a definition is not attached to
9416+
// the global module). User-specified 'inline' overrides this (set when
9417+
// the function decl is created above).
9418+
// FIXME: We need a better way to separate C++ standard and clang modules.
9419+
bool ImplicitInlineCXX20 = !getLangOpts().CPlusPlusModules ||
9420+
!NewFD->getOwningModule() ||
9421+
NewFD->getOwningModule()->isGlobalModule() ||
9422+
NewFD->getOwningModule()->isModuleMapModule();
94149423
bool isInline = D.getDeclSpec().isInlineSpecified();
94159424
bool isVirtual = D.getDeclSpec().isVirtualSpecified();
94169425
bool hasExplicit = D.getDeclSpec().hasExplicitSpecifier();
94179426
isFriend = D.getDeclSpec().isFriendSpecified();
94189427
if (isFriend && !isInline && D.isFunctionDefinition()) {
9419-
// C++ [class.friend]p5
9428+
// Pre-C++20 [class.friend]p5
94209429
// A function can be defined in a friend declaration of a
94219430
// class . . . . Such a function is implicitly inline.
9422-
NewFD->setImplicitlyInline();
9431+
// Post C++20 [class.friend]p7
9432+
// Such a function is implicitly an inline function if it is attached
9433+
// to the global module.
9434+
NewFD->setImplicitlyInline(ImplicitInlineCXX20);
94239435
}
94249436

94259437
// 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,
97029714
}
97039715

97049716
if (isa<CXXMethodDecl>(NewFD) && DC == CurContext &&
9705-
D.isFunctionDefinition()) {
9706-
// C++ [class.mfct]p2:
9717+
D.isFunctionDefinition() && !isInline) {
9718+
// Pre C++20 [class.mfct]p2:
97079719
// A member function may be defined (8.4) in its class definition, in
97089720
// which case it is an inline member function (7.1.2)
9709-
NewFD->setImplicitlyInline();
9721+
// Post C++20 [class.mfct]p1:
9722+
// If a member function is attached to the global module and is defined
9723+
// in its class definition, it is inline.
9724+
NewFD->setImplicitlyInline(ImplicitInlineCXX20);
97109725
}
97119726

97129727
if (SC == SC_Static && isa<CXXMethodDecl>(NewFD) &&

clang/test/AST/ast-dump-constant-expr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ struct Test {
5858

5959
// CHECK:Dumping Test:
6060
// CHECK-NEXT:CXXRecordDecl {{.*}} <{{.*}}ast-dump-constant-expr.cpp:43:1, line:57:1> line:43:8 struct Test definition
61-
// CHECK:|-CXXMethodDecl {{.*}} <line:44:3, line:54:3> line:44:8 test 'void ()'
61+
// CHECK:|-CXXMethodDecl {{.*}} <line:44:3, line:54:3> line:44:8 test 'void ()' implicit-inline
6262
// CHECK-NEXT:| `-CompoundStmt {{.*}} <col:15, line:54:3>
6363
// CHECK-NEXT:| |-CStyleCastExpr {{.*}} <line:45:5, col:20> 'void' <ToVoid>
6464
// CHECK-NEXT:| | `-ConstantExpr {{.*}} <col:11, col:20> 'int'
@@ -90,4 +90,4 @@ struct Test {
9090
// CHECK-NEXT:| `-CallExpr {{.*}} <col:11, col:23> '__int128'
9191
// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} <col:11> '__int128 (*)()' <FunctionToPointerDecay>
9292
// CHECK-NEXT:| `-DeclRefExpr {{.*}} <col:11> '__int128 ()' lvalue Function {{.*}} 'test_Int128' '__int128 ()'
93-
// CHECK-NEXT:`-CXXMethodDecl {{.*}} <line:56:3, col:38> col:18 consteval consteval_method 'void ()'
93+
// CHECK-NEXT:`-CXXMethodDecl {{.*}} <line:56:3, col:38> col:18 consteval consteval_method 'void ()' implicit-inline

clang/test/AST/ast-dump-lambda.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ template <typename... Ts> void test(Ts... a) {
5151
// CHECK-NEXT: | | |-MoveAssignment exists simple trivial needs_implicit
5252
// CHECK-NEXT: | | `-Destructor simple irrelevant trivial needs_implicit
5353
// CHECK-NEXT: | |-CXXRecordDecl {{.*}} <col:3, col:10> col:10{{( imported)?}} implicit struct V
54-
// CHECK-NEXT: | `-CXXMethodDecl {{.*}} <line:17:5, line:20:5> line:17:10{{( imported)?}} f 'void ()'
54+
// CHECK-NEXT: | `-CXXMethodDecl {{.*}} <line:17:5, line:20:5> line:17:10{{( imported)?}} f 'void ()' implicit-inline
5555
// CHECK-NEXT: | `-CompoundStmt {{.*}} <col:14, line:20:5>
5656
// CHECK-NEXT: | |-LambdaExpr {{.*}} <line:18:7, col:15> '(lambda at {{.*}}ast-dump-lambda.cpp:18:7)'
5757
// CHECK-NEXT: | | |-CXXRecordDecl {{.*}} <col:7> col:7{{( imported)?}} implicit{{( <undeserialized declarations>)?}} class definition
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
// RUN: cd %t
5+
//
6+
// RUN: %clang_cc1 -std=c++20 no-modules.cpp -fsyntax-only -ast-dump | \
7+
// RUN: FileCheck --match-full-lines --check-prefix=CHECK-NM %s
8+
// RUN: %clang_cc1 -std=c++20 -xc++-user-header header-unit.h -ast-dump | \
9+
// RUN: FileCheck --match-full-lines --check-prefix=CHECK-HU %s
10+
// RUN: %clang_cc1 -std=c++20 module.cpp -ast-dump | \
11+
// RUN: FileCheck --match-full-lines --check-prefix=CHECK-MOD %s
12+
13+
//--- no-modules.cpp
14+
15+
class X {
16+
friend void x(){};
17+
};
18+
19+
// CHECK-NM: `-CXXRecordDecl {{.*}} <no-modules.cpp:2:1, line:4:1> line:2:7 class X definition
20+
// CHECK-NM: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 implicit class X
21+
// CHECK-NM-NEXT: `-FriendDecl {{.*}} <line:3:3, col:19> col:15
22+
// CHECK-NM-NEXT: `-FunctionDecl {{.*}} parent {{.*}} <col:3, col:19> col:15 x 'void ()' implicit-inline
23+
24+
//--- header-unit.h
25+
26+
class Y {
27+
friend void y(){};
28+
};
29+
30+
// CHECK-HU: `-CXXRecordDecl {{.*}} <./header-unit.h:2:1, line:4:1> line:2:7 class Y definition
31+
// CHECK-HU: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 implicit class Y
32+
// CHECK-HU-NEXT: `-FriendDecl {{.*}} <line:3:3, col:19> col:15
33+
// CHECK-HU-NEXT: `-FunctionDecl {{.*}} parent {{.*}} <col:3, col:19> col:15 y 'void ()' implicit-inline
34+
35+
// A textually-included header
36+
//--- header.h
37+
38+
class A {
39+
friend void a(){};
40+
};
41+
42+
//--- module.cpp
43+
module;
44+
#include "header.h"
45+
46+
export module M;
47+
48+
class Z {
49+
friend void z(){};
50+
};
51+
// CHECK-MOD: |-CXXRecordDecl {{.*}} <./header.h:2:1, line:4:1> line:2:7 in M.<global> hidden class A definition
52+
// CHECK-MOD: | |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 in M.<global> hidden implicit class A
53+
// CHECK-MOD-NEXT: | `-FriendDecl {{.*}} <line:3:3, col:19> col:15 in M.<global>
54+
// CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} <col:3, col:19> col:15 in M.<global> hidden a 'void ()' implicit-inline
55+
56+
// CHECK-MOD: `-CXXRecordDecl {{.*}} <module.cpp:6:1, line:8:1> line:6:7 in M hidden class Z{{( ReachableWhenImported)?}} definition
57+
// CHECK-MOD: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}}
58+
// CHECK-MOD-NEXT: `-FriendDecl {{.*}} <line:7:3, col:19> col:15 in M{{( ReachableWhenImported)?}}
59+
// CHECK-MOD-NEXT: `-FunctionDecl {{.*}} parent {{.*}} <col:3, col:19> col:15 in M hidden z 'void ()'{{( ReachableWhenImported)?}}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
// RUN: cd %t
5+
//
6+
// RUN: %clang_cc1 -std=c++20 no-modules.cpp -fsyntax-only -ast-dump | \
7+
// RUN: FileCheck --match-full-lines --check-prefix=CHECK-NM %s
8+
// RUN: %clang_cc1 -std=c++20 -xc++-user-header header-unit.h -ast-dump | \
9+
// RUN: FileCheck --match-full-lines --check-prefix=CHECK-HU %s
10+
// RUN: %clang_cc1 -std=c++20 module.cpp -ast-dump | \
11+
// RUN: FileCheck --match-full-lines --check-prefix=CHECK-MOD %s
12+
13+
//--- no-modules.cpp
14+
15+
class X {
16+
void x(){};
17+
};
18+
19+
// CHECK-NM: `-CXXRecordDecl {{.*}} <no-modules.cpp:2:1, line:4:1> line:2:7 class X definition
20+
// CHECK-NM: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 implicit class X
21+
// CHECK-NM-NEXT: `-CXXMethodDecl {{.*}} <line:3:3, col:12> col:8 x 'void ()' implicit-inline
22+
23+
// A header unit header
24+
//--- header-unit.h
25+
26+
class Y {
27+
void y(){};
28+
};
29+
30+
// CHECK-HU: `-CXXRecordDecl {{.*}} <./header-unit.h:2:1, line:4:1> line:2:7 class Y definition
31+
// CHECK-HU: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 implicit class Y
32+
// CHECK-HU-NEXT: `-CXXMethodDecl {{.*}} <line:3:3, col:12> col:8 y 'void ()' implicit-inline
33+
34+
// A textually-included header
35+
//--- header.h
36+
37+
class A {
38+
void a(){};
39+
};
40+
41+
//--- module.cpp
42+
module;
43+
#include "header.h"
44+
45+
export module M;
46+
47+
class Z {
48+
void z(){};
49+
};
50+
51+
// CHECK-MOD: |-CXXRecordDecl {{.*}} <./header.h:2:1, line:4:1> line:2:7 in M.<global> hidden class A definition
52+
// CHECK-MOD: | |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 in M.<global> hidden implicit class A
53+
// CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} <line:3:3, col:12> col:8 in M.<global> hidden a 'void ()' implicit-inline
54+
55+
// CHECK-MOD: `-CXXRecordDecl {{.*}} <module.cpp:6:1, line:8:1> line:6:7 in M hidden class Z{{( ReachableWhenImported)?}} definition
56+
// CHECK-MOD: |-CXXRecordDecl {{.*}} <col:1, col:7> col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}}
57+
// CHECK-MOD-NEXT: `-CXXMethodDecl {{.*}} <line:7:3, col:12> col:8 in M hidden z 'void ()'{{( ReachableWhenImported)?}}

0 commit comments

Comments
 (0)