Skip to content

Commit 0cfe9f7

Browse files
[Clang] Handle C++20 export declarations in -dump-minimization-hints (#151666)
Thanks to Justin Stitt for bringing this up, providing test cases and a direction for the fix!
1 parent 86727fe commit 0cfe9f7

File tree

2 files changed

+173
-27
lines changed

2 files changed

+173
-27
lines changed

clang/lib/Frontend/FrontendAction.cpp

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -84,29 +84,28 @@ class DeserializedDeclsSourceRangePrinter : public ASTConsumer,
8484
if (!IsCollectingDecls)
8585
return;
8686
if (!D || isa<TranslationUnitDecl>(D) || isa<LinkageSpecDecl>(D) ||
87-
isa<NamespaceDecl>(D)) {
87+
isa<NamespaceDecl>(D) || isa<ExportDecl>(D)) {
8888
// These decls cover a lot of nested declarations that might not be used,
8989
// reducing the granularity and making the output less useful.
9090
return;
9191
}
92-
auto *DC = D->getLexicalDeclContext();
93-
if (!DC || !DC->isFileContext()) {
94-
// We choose to work at namespace level to reduce complexity and the
95-
// number of cases we care about.
92+
if (isa<ParmVarDecl>(D)) {
93+
// Parameters are covered by their functions.
9694
return;
9795
}
96+
auto *DC = D->getLexicalDeclContext();
97+
if (!DC || !shouldIncludeDeclsIn(DC))
98+
return;
9899

99100
PendingDecls.push_back(D);
100-
if (auto *NS = dyn_cast<NamespaceDecl>(DC)) {
101-
// Add any namespaces we have not seen before.
102-
// Note that we filter out namespaces from DeclRead as it includes too
103-
// all redeclarations and we only want the ones that had other used
104-
// declarations.
105-
while (NS && ProcessedNamespaces.insert(NS).second) {
106-
PendingDecls.push_back(NS);
107-
108-
NS = dyn_cast<NamespaceDecl>(NS->getLexicalParent());
109-
}
101+
for (; (isa<ExportDecl>(DC) || isa<NamespaceDecl>(DC)) &&
102+
ProcessedDeclContexts.insert(DC).second;
103+
DC = DC->getLexicalParent()) {
104+
// Add any interesting decl contexts that we have not seen before.
105+
// Note that we filter them out from DeclRead as that would include all
106+
// redeclarations of namespaces, potentially those that do not have any
107+
// imported declarations.
108+
PendingDecls.push_back(cast<Decl>(DC));
110109
}
111110
}
112111

@@ -205,12 +204,38 @@ class DeserializedDeclsSourceRangePrinter : public ASTConsumer,
205204

206205
private:
207206
std::vector<const Decl *> PendingDecls;
208-
llvm::SmallPtrSet<const NamespaceDecl *, 0> ProcessedNamespaces;
207+
llvm::SmallPtrSet<const DeclContext *, 0> ProcessedDeclContexts;
209208
bool IsCollectingDecls = true;
210209
const SourceManager &SM;
211210
std::unique_ptr<llvm::raw_ostream> OS;
212211

212+
static bool shouldIncludeDeclsIn(const DeclContext *DC) {
213+
assert(DC && "DC is null");
214+
// We choose to work at namespace level to reduce complexity and the number
215+
// of cases we care about.
216+
// We still need to carefully handle composite declarations like
217+
// `ExportDecl`.
218+
for (; DC; DC = DC->getLexicalParent()) {
219+
if (DC->isFileContext())
220+
return true;
221+
if (isa<ExportDecl>(DC))
222+
continue; // Depends on the parent.
223+
return false;
224+
}
225+
llvm_unreachable("DeclContext chain must end with a translation unit");
226+
}
227+
213228
llvm::SmallVector<CharSourceRange, 2> getRangesToMark(const Decl *D) {
229+
if (auto *ED = dyn_cast<ExportDecl>(D)) {
230+
if (!ED->hasBraces())
231+
return {SM.getExpansionRange(ED->getExportLoc())};
232+
233+
return {SM.getExpansionRange(SourceRange(
234+
ED->getExportLoc(),
235+
lexForLBrace(ED->getExportLoc(), D->getLangOpts()))),
236+
SM.getExpansionRange(ED->getRBraceLoc())};
237+
}
238+
214239
auto *NS = dyn_cast<NamespaceDecl>(D);
215240
if (!NS)
216241
return {SM.getExpansionRange(D->getSourceRange())};
@@ -232,17 +257,7 @@ class DeserializedDeclsSourceRangePrinter : public ASTConsumer,
232257
}
233258
}
234259
}
235-
auto &LangOpts = D->getLangOpts();
236-
// Now skip one token, the next should be the lbrace.
237-
Token Tok;
238-
if (Lexer::getRawToken(TokenBeforeLBrace, Tok, SM, LangOpts, true) ||
239-
Lexer::getRawToken(Tok.getEndLoc(), Tok, SM, LangOpts, true) ||
240-
Tok.getKind() != tok::l_brace) {
241-
// On error or if we did not find the token we expected, avoid marking
242-
// everything inside the namespace as used.
243-
return {};
244-
}
245-
LBraceLoc = Tok.getLocation();
260+
LBraceLoc = lexForLBrace(TokenBeforeLBrace, D->getLangOpts());
246261
}
247262
return {SM.getExpansionRange(SourceRange(NS->getBeginLoc(), LBraceLoc)),
248263
SM.getExpansionRange(NS->getRBraceLoc())};
@@ -285,6 +300,20 @@ class DeserializedDeclsSourceRangePrinter : public ASTConsumer,
285300

286301
OS->flush();
287302
}
303+
304+
SourceLocation lexForLBrace(SourceLocation TokenBeforeLBrace,
305+
const LangOptions &LangOpts) {
306+
// Now skip one token, the next should be the lbrace.
307+
Token Tok;
308+
if (Lexer::getRawToken(TokenBeforeLBrace, Tok, SM, LangOpts, true) ||
309+
Lexer::getRawToken(Tok.getEndLoc(), Tok, SM, LangOpts, true) ||
310+
Tok.getKind() != tok::l_brace) {
311+
// On error or if we did not find the token we expected, avoid marking
312+
// everything inside the namespace as used.
313+
return SourceLocation();
314+
}
315+
return Tok.getLocation();
316+
}
288317
};
289318

290319
/// Dumps deserialized declarations.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
// RUN: %clang_cc1 -std=c++20 %t/foo.cppm -emit-module-interface -o %t/foo.pcm
5+
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -dump-minimization-hints=%t/decls
6+
// RUN: cat %t/decls
7+
// RUN: cat %t/decls | FileCheck -check-prefix=RANGE %s
8+
// RANGE:{
9+
// RANGE-NEXT:"required_ranges": [
10+
// RANGE-NEXT: {
11+
// RANGE-NEXT: "file": "{{.+}}foo.cppm",
12+
// RANGE-NEXT: "range": [
13+
// RANGE-NEXT: {
14+
// RANGE-NEXT: "from": {
15+
// RANGE-NEXT: "line": 3,
16+
// RANGE-NEXT: "column": 1
17+
// RANGE-NEXT: },
18+
// RANGE-NEXT: "to": {
19+
// RANGE-NEXT: "line": 3,
20+
// RANGE-NEXT: "column": 22
21+
// RANGE-NEXT: }
22+
// RANGE-NEXT: },
23+
// RANGE-NEXT: {
24+
// RANGE-NEXT: "from": {
25+
// RANGE-NEXT: "line": 4,
26+
// RANGE-NEXT: "column": 3
27+
// RANGE-NEXT: },
28+
// RANGE-NEXT: "to": {
29+
// RANGE-NEXT: "line": 4,
30+
// RANGE-NEXT: "column": 9
31+
// RANGE-NEXT: }
32+
// RANGE-NEXT: },
33+
// RANGE-NEXT: {
34+
// RANGE-NEXT: "from": {
35+
// RANGE-NEXT: "line": 4,
36+
// RANGE-NEXT: "column": 10
37+
// RANGE-NEXT: },
38+
// RANGE-NEXT: "to": {
39+
// RANGE-NEXT: "line": 4,
40+
// RANGE-NEXT: "column": 43
41+
// RANGE-NEXT: }
42+
// RANGE-NEXT: },
43+
// RANGE-NEXT: {
44+
// RANGE-NEXT: "from": {
45+
// RANGE-NEXT: "line": 6,
46+
// RANGE-NEXT: "column": 1
47+
// RANGE-NEXT: },
48+
// RANGE-NEXT: "to": {
49+
// RANGE-NEXT: "line": 6,
50+
// RANGE-NEXT: "column": 2
51+
// RANGE-NEXT: }
52+
// RANGE-NEXT: },
53+
// RANGE-NEXT: {
54+
// RANGE-NEXT: "from": {
55+
// RANGE-NEXT: "line": 8,
56+
// RANGE-NEXT: "column": 1
57+
// RANGE-NEXT: },
58+
// RANGE-NEXT: "to": {
59+
// RANGE-NEXT: "line": 8,
60+
// RANGE-NEXT: "column": 7
61+
// RANGE-NEXT: }
62+
// RANGE-NEXT: },
63+
// RANGE-NEXT: {
64+
// RANGE-NEXT: "from": {
65+
// RANGE-NEXT: "line": 8,
66+
// RANGE-NEXT: "column": 8
67+
// RANGE-NEXT: },
68+
// RANGE-NEXT: "to": {
69+
// RANGE-NEXT: "line": 8,
70+
// RANGE-NEXT: "column": 25
71+
// RANGE-NEXT: }
72+
// RANGE-NEXT: },
73+
// RANGE-NEXT: {
74+
// RANGE-NEXT: "from": {
75+
// RANGE-NEXT: "line": 9,
76+
// RANGE-NEXT: "column": 3
77+
// RANGE-NEXT: },
78+
// RANGE-NEXT: "to": {
79+
// RANGE-NEXT: "line": 9,
80+
// RANGE-NEXT: "column": 36
81+
// RANGE-NEXT: }
82+
// RANGE-NEXT: },
83+
// RANGE-NEXT: {
84+
// RANGE-NEXT: "from": {
85+
// RANGE-NEXT: "line": 11,
86+
// RANGE-NEXT: "column": 1
87+
// RANGE-NEXT: },
88+
// RANGE-NEXT: "to": {
89+
// RANGE-NEXT: "line": 11,
90+
// RANGE-NEXT: "column": 2
91+
// RANGE-NEXT: }
92+
// RANGE-NEXT: }
93+
// RANGE-NEXT: ]
94+
// RANGE-NEXT: }
95+
// RANGE-NEXT:]
96+
// RANGE-NEXT:}
97+
98+
//--- foo.cppm
99+
export module foo;
100+
101+
namespace piecemeal { // line 3
102+
export int used(int n) { return n + 1; }
103+
export int unused(int n) { return n + 2; }
104+
}
105+
106+
export namespace whole { // line 8
107+
int used(int n) { return n + 1; }
108+
int unused(int n) { return n + 3; }
109+
} // line 11
110+
111+
//--- use.cpp
112+
import foo;
113+
114+
int main() {
115+
piecemeal::used(4); // only one of the functions used from each namespace.
116+
whole::used(4);
117+
}

0 commit comments

Comments
 (0)