Skip to content

Commit 6d0d09a

Browse files
committed
[C++20][Modules] Add a test for namespace lookup optimization.
1 parent 7d381f2 commit 6d0d09a

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

clang/unittests/Serialization/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ add_clang_unittest(SerializationTests
22
ForceCheckFileInputTest.cpp
33
InMemoryModuleCacheTest.cpp
44
ModuleCacheTest.cpp
5+
NamespaceLookupTest.cpp
56
NoCommentsTest.cpp
67
PreambleInNamedModulesTest.cpp
78
LoadSpecLazilyTest.cpp
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
//== unittests/Serialization/NamespaceLookupOptimizationTest.cpp =======//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/Driver/CreateInvocationFromArgs.h"
10+
#include "clang/Frontend/CompilerInstance.h"
11+
#include "clang/Frontend/FrontendAction.h"
12+
#include "clang/Frontend/FrontendActions.h"
13+
#include "clang/Parse/ParseAST.h"
14+
#include "clang/Serialization/ASTReader.h"
15+
#include "clang/Tooling/Tooling.h"
16+
#include "gtest/gtest.h"
17+
18+
using namespace llvm;
19+
using namespace clang;
20+
using namespace clang::tooling;
21+
22+
namespace {
23+
24+
class NamespaceLookupTest : public ::testing::Test {
25+
void SetUp() override {
26+
ASSERT_FALSE(
27+
sys::fs::createUniqueDirectory("namespace-lookup-test", TestDir));
28+
}
29+
30+
void TearDown() override { sys::fs::remove_directories(TestDir); }
31+
32+
public:
33+
SmallString<256> TestDir;
34+
35+
void addFile(StringRef Path, StringRef Contents) {
36+
ASSERT_FALSE(sys::path::is_absolute(Path));
37+
38+
SmallString<256> AbsPath(TestDir);
39+
sys::path::append(AbsPath, Path);
40+
41+
ASSERT_FALSE(
42+
sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
43+
44+
std::error_code EC;
45+
llvm::raw_fd_ostream OS(AbsPath, EC);
46+
ASSERT_FALSE(EC);
47+
OS << Contents;
48+
}
49+
50+
std::string GenerateModuleInterface(StringRef ModuleName, StringRef Contents) {
51+
std::string FileName = llvm::Twine(ModuleName + ".cppm").str();
52+
addFile(FileName, Contents);
53+
54+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
55+
llvm::vfs::createPhysicalFileSystem();
56+
DiagnosticOptions DiagOpts;
57+
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
58+
CompilerInstance::createDiagnostics(*VFS, DiagOpts);
59+
CreateInvocationOptions CIOpts;
60+
CIOpts.Diags = Diags;
61+
CIOpts.VFS = VFS;
62+
63+
std::string CacheBMIPath =
64+
llvm::Twine(TestDir + "/" + ModuleName + ".pcm").str();
65+
std::string PrebuiltModulePath =
66+
"-fprebuilt-module-path=" + TestDir.str().str();
67+
const char *Args[] = {"clang++",
68+
"-std=c++20",
69+
"--precompile",
70+
PrebuiltModulePath.c_str(),
71+
"-working-directory",
72+
TestDir.c_str(),
73+
"-I",
74+
TestDir.c_str(),
75+
FileName.c_str(),
76+
"-o",
77+
CacheBMIPath.c_str()};
78+
std::shared_ptr<CompilerInvocation> Invocation =
79+
createInvocation(Args, CIOpts);
80+
EXPECT_TRUE(Invocation);
81+
82+
CompilerInstance Instance(std::move(Invocation));
83+
Instance.setDiagnostics(Diags);
84+
Instance.getFrontendOpts().OutputFile = CacheBMIPath;
85+
// Avoid memory leaks.
86+
Instance.getFrontendOpts().DisableFree = false;
87+
GenerateModuleInterfaceAction Action;
88+
EXPECT_TRUE(Instance.ExecuteAction(Action));
89+
EXPECT_FALSE(Diags->hasErrorOccurred());
90+
91+
return CacheBMIPath;
92+
}
93+
};
94+
95+
struct NamespaceLookupResult {
96+
int NumLocalNamespaces = 0;
97+
int NumExternalNamespaces = 0;
98+
};
99+
100+
class NamespaceLookupConsumer : public ASTConsumer {
101+
NamespaceLookupResult &Result;
102+
public:
103+
104+
explicit NamespaceLookupConsumer(NamespaceLookupResult &Result)
105+
: Result(Result) {}
106+
107+
void HandleTranslationUnit(ASTContext &Context) override {
108+
TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
109+
ASSERT_TRUE(TU);
110+
ASTReader *Chain = dyn_cast_or_null<ASTReader>(Context.getExternalSource());
111+
ASSERT_TRUE(Chain);
112+
for (const Decl *D : TU->lookup(DeclarationName(&Context.Idents.get("N")))) {
113+
if (!isa<NamespaceDecl>(D))
114+
continue;
115+
if (!D->isFromASTFile()) {
116+
++Result.NumLocalNamespaces;
117+
} else {
118+
++Result.NumExternalNamespaces;
119+
EXPECT_EQ(D, Chain->getKeyDeclaration(D));
120+
}
121+
}
122+
}
123+
};
124+
125+
class NamespaceLookupAction : public ASTFrontendAction {
126+
NamespaceLookupResult &Result;
127+
public:
128+
explicit NamespaceLookupAction(NamespaceLookupResult &Result)
129+
: Result(Result) {}
130+
131+
std::unique_ptr<ASTConsumer>
132+
CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override {
133+
return std::make_unique<NamespaceLookupConsumer>(Result);
134+
}
135+
};
136+
137+
TEST_F(NamespaceLookupTest, ExternalNamespacesOnly) {
138+
GenerateModuleInterface("M1", R"cpp(
139+
export module M1;
140+
namespace N {}
141+
)cpp");
142+
GenerateModuleInterface("M2", R"cpp(
143+
export module M2;
144+
namespace N {}
145+
)cpp");
146+
GenerateModuleInterface("M3", R"cpp(
147+
export module M3;
148+
namespace N {}
149+
)cpp");
150+
const char *test_file_contents = R"cpp(
151+
import M1;
152+
import M2;
153+
import M3;
154+
)cpp";
155+
std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
156+
NamespaceLookupResult Result;
157+
EXPECT_TRUE(
158+
runToolOnCodeWithArgs(std::make_unique<NamespaceLookupAction>(Result),
159+
test_file_contents,
160+
{
161+
"-std=c++20",
162+
DepArg.c_str(),
163+
"-I",
164+
TestDir.c_str(),
165+
},
166+
"main.cpp"));
167+
168+
EXPECT_EQ(0, Result.NumLocalNamespaces);
169+
EXPECT_EQ(1, Result.NumExternalNamespaces);
170+
}
171+
172+
TEST_F(NamespaceLookupTest, ExternalReplacedByLocal) {
173+
GenerateModuleInterface("M1", R"cpp(
174+
export module M1;
175+
namespace N {}
176+
)cpp");
177+
GenerateModuleInterface("M2", R"cpp(
178+
export module M2;
179+
namespace N {}
180+
)cpp");
181+
GenerateModuleInterface("M3", R"cpp(
182+
export module M3;
183+
namespace N {}
184+
)cpp");
185+
const char *test_file_contents = R"cpp(
186+
import M1;
187+
import M2;
188+
import M3;
189+
190+
namespace N {}
191+
)cpp";
192+
std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
193+
NamespaceLookupResult Result;
194+
EXPECT_TRUE(
195+
runToolOnCodeWithArgs(std::make_unique<NamespaceLookupAction>(Result),
196+
test_file_contents,
197+
{
198+
"-std=c++20",
199+
DepArg.c_str(),
200+
"-I",
201+
TestDir.c_str(),
202+
},
203+
"main.cpp"));
204+
205+
EXPECT_EQ(1, Result.NumLocalNamespaces);
206+
EXPECT_EQ(0, Result.NumExternalNamespaces);
207+
}
208+
209+
TEST_F(NamespaceLookupTest, LocalAndExternalInterleaved) {
210+
GenerateModuleInterface("M1", R"cpp(
211+
export module M1;
212+
namespace N {}
213+
)cpp");
214+
GenerateModuleInterface("M2", R"cpp(
215+
export module M2;
216+
namespace N {}
217+
)cpp");
218+
GenerateModuleInterface("M3", R"cpp(
219+
export module M3;
220+
namespace N {}
221+
)cpp");
222+
const char *test_file_contents = R"cpp(
223+
import M1;
224+
225+
namespace N {}
226+
227+
import M2;
228+
import M3;
229+
)cpp";
230+
std::string DepArg = "-fprebuilt-module-path=" + TestDir.str().str();
231+
NamespaceLookupResult Result;
232+
EXPECT_TRUE(
233+
runToolOnCodeWithArgs(std::make_unique<NamespaceLookupAction>(Result),
234+
test_file_contents,
235+
{
236+
"-std=c++20",
237+
DepArg.c_str(),
238+
"-I",
239+
TestDir.c_str(),
240+
},
241+
"main.cpp"));
242+
243+
EXPECT_EQ(1, Result.NumLocalNamespaces);
244+
EXPECT_EQ(1, Result.NumExternalNamespaces);
245+
}
246+
247+
} // namespace

0 commit comments

Comments
 (0)