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