From 79fa0ec8c4bfeeb21f7b44ebd9a66e7ec9781798 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Tue, 13 Sep 2022 13:03:34 +0800 Subject: [PATCH] [C++20] [Modules] Make member functions with a in-class definition in HU implicitly inline According to [dcl.inline]p7/note4, > In the global module, a function defined within a class definition is > implicitly inline. And the declarations in the header unit are attached to the global module fragment. So the function defined within a class definition in header units should be implicitly inline too. This fixes https://github.com/llvm/llvm-project/issues/57571. --- clang/lib/Sema/SemaDecl.cpp | 2 +- .../header-unit-friend-within-class-linkage.cpp | 27 ++++++++++++++ .../CodeGenCXX/header-unit-member-func-linkage.cpp | 24 ++++++++++++ clang/unittests/AST/DeclTest.cpp | 43 ++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGenCXX/header-unit-friend-within-class-linkage.cpp create mode 100644 clang/test/CodeGenCXX/header-unit-member-func-linkage.cpp diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9d0e676..b1c4539 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9503,7 +9503,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, bool ImplicitInlineCXX20 = !getLangOpts().CPlusPlusModules || !NewFD->getOwningModule() || NewFD->getOwningModule()->isGlobalModule() || - NewFD->getOwningModule()->isModuleMapModule(); + NewFD->getOwningModule()->isHeaderLikeModule(); bool isInline = D.getDeclSpec().isInlineSpecified(); bool isVirtual = D.getDeclSpec().isVirtualSpecified(); bool hasExplicit = D.getDeclSpec().hasExplicitSpecifier(); diff --git a/clang/test/CodeGenCXX/header-unit-friend-within-class-linkage.cpp b/clang/test/CodeGenCXX/header-unit-friend-within-class-linkage.cpp new file mode 100644 index 0000000..cfee317 --- /dev/null +++ b/clang/test/CodeGenCXX/header-unit-friend-within-class-linkage.cpp @@ -0,0 +1,27 @@ +// Tests that the friend function with-in an class definition in the header unit is still implicit inline. +// RUN: rm -rf %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -xc++-user-header -emit-header-unit %t/foo.h -o %t/foo.pcm +// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -fmodule-file=%t/foo.pcm %t/user.cpp \ +// RUN: -S -emit-llvm -disable-llvm-passes -o - | FileCheck %t/user.cpp + +//--- foo.h +class foo { + int value; +public: + foo(int v) : value(v) {} + + friend int getFooValue(foo f) { + return f.value; + } +}; + +//--- user.cpp +import "foo.h"; +int use() { + foo f(43); + return getFooValue(f); +} + +// CHECK: define{{.*}}linkonce_odr{{.*}}@_Z11getFooValue3foo diff --git a/clang/test/CodeGenCXX/header-unit-member-func-linkage.cpp b/clang/test/CodeGenCXX/header-unit-member-func-linkage.cpp new file mode 100644 index 0000000..5ab15df --- /dev/null +++ b/clang/test/CodeGenCXX/header-unit-member-func-linkage.cpp @@ -0,0 +1,24 @@ +// Tests that the member function with-in an class definition in the header unit is still implicit inline. +// RUN: rm -rf %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -xc++-user-header -emit-header-unit %t/foo.h -o %t/foo.pcm +// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -fmodule-file=%t/foo.pcm %t/user.cpp \ +// RUN: -S -emit-llvm -disable-llvm-passes -o - | FileCheck %t/user.cpp + +//--- foo.h +class foo { +public: + int getValue() { + return 43; + } +}; + +//--- user.cpp +import "foo.h"; +int use() { + foo f; + return f.getValue(); +} + +// CHECK: define{{.*}}linkonce_odr{{.*}}@_ZN3foo8getValueEv diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp index 7fe715db..43a3798 100644 --- a/clang/unittests/AST/DeclTest.cpp +++ b/clang/unittests/AST/DeclTest.cpp @@ -311,3 +311,46 @@ TEST(Decl, MemberFunctionInModules) { EXPECT_TRUE(bar->isInlined()); } +TEST(Decl, MemberFunctionInHeaderUnit) { + llvm::Annotations Code(R"( + class foo { + public: + int memFn() { + return 43; + } + }; + )"); + + auto AST = tooling::buildASTFromCodeWithArgs( + Code.code(), {"-std=c++20", " -xc++-user-header ", "-emit-header-unit"}); + ASTContext &Ctx = AST->getASTContext(); + + auto *memFn = selectFirst( + "memFn", match(functionDecl(hasName("memFn")).bind("memFn"), Ctx)); + + EXPECT_TRUE(memFn->isInlined()); +} + +TEST(Decl, FriendFunctionWithinClassInHeaderUnit) { + llvm::Annotations Code(R"( + class foo { + int value; + public: + foo(int v) : value(v) {} + + friend int getFooValue(foo f) { + return f.value; + } + }; + )"); + + auto AST = tooling::buildASTFromCodeWithArgs( + Code.code(), {"-std=c++20", " -xc++-user-header ", "-emit-header-unit"}); + ASTContext &Ctx = AST->getASTContext(); + + auto *getFooValue = selectFirst( + "getFooValue", + match(functionDecl(hasName("getFooValue")).bind("getFooValue"), Ctx)); + + EXPECT_TRUE(getFooValue->isInlined()); +} -- 2.7.4