Skip to content

Commit cededef

Browse files
committed
Add condition resolution as a new phrase post-parse
An unfortunately necessary thing to delay defrosting function bodies as long as we can.
1 parent 72beb9d commit cededef

17 files changed

+683
-335
lines changed

include/swift/AST/Module.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -831,13 +831,13 @@ class SourceFile final : public FileUnit {
831831
std::vector<Decl*> Decls;
832832

833833
/// The list of local type declarations in the source file.
834-
SmallPtrSet<TypeDecl *, 2> LocalTypeDecls;
834+
llvm::SetVector<TypeDecl *> LocalTypeDecls;
835835

836836
/// A set of special declaration attributes which require the
837837
/// Foundation module to be imported to work. If the foundation
838838
/// module is still not imported by the time type checking is
839839
/// complete, we diagnose.
840-
SmallPtrSet<const DeclAttribute *, 4> AttrsRequiringFoundation;
840+
llvm::SetVector<const DeclAttribute *> AttrsRequiringFoundation;
841841

842842
/// A mapping from Objective-C selectors to the methods that have
843843
/// those selectors.

include/swift/AST/StmtTransformer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- Stmt.h - Swift Language Statement ASTs -----------------*- C++ -*-===//
1+
//===--- StmtTransformer.h - Class for transforming Stmts -------*- C++ -*-===//
22
//
33
// This source file is part of the Swift.org open source project
44
//

include/swift/Subsystems.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ namespace swift {
127127
bool TokenizeInterpolatedString = true,
128128
ArrayRef<Token> SplitTokens = ArrayRef<Token>());
129129

130+
/// Once parsing is complete, this walks the AST to resolve condition clauses
131+
/// and other top-level validation.
132+
void performConditionResolution(SourceFile &SF);
133+
134+
/// \brief Finish condition resolution for the bodies of function nodes that
135+
/// were delayed during the first parsing pass.
136+
void performDelayedConditionResolution(Decl *D, SourceFile &BSF,
137+
SmallVectorImpl<Decl *> &ExtraTLCDs);
138+
130139
/// Once parsing is complete, this walks the AST to resolve imports, record
131140
/// operators, and do other top-level validation.
132141
///

lib/Frontend/Frontend.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ void CompilerInstance::performSema() {
446446

447447
Diags.setSuppressWarnings(DidSuppressWarnings);
448448

449+
performConditionResolution(*NextInput);
449450
performNameBinding(*NextInput);
450451
}
451452

@@ -508,8 +509,10 @@ void CompilerInstance::performSema() {
508509
if (mainIsPrimary && !Context->hadError() &&
509510
Invocation.getFrontendOptions().PlaygroundTransform)
510511
performPlaygroundTransform(MainFile, Invocation.getFrontendOptions().PlaygroundHighPerformance);
511-
if (!mainIsPrimary)
512+
if (!mainIsPrimary) {
513+
performConditionResolution(MainFile);
512514
performNameBinding(MainFile);
515+
}
513516
}
514517

515518
// Type-check each top-level input besides the main source file.

lib/Parse/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_swift_library(swiftParse STATIC
2+
ConditionResolver.cpp
23
Lexer.cpp
34
ParseDecl.cpp
45
ParseExpr.cpp

lib/Parse/ConditionResolver.cpp

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
//===--- ConditionResolver.cpp - Condition Resolution ---------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file implements condition resolution for Swift.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/Subsystems.h"
18+
#include "swift/AST/AST.h"
19+
#include "swift/AST/ASTWalker.h"
20+
#include "swift/Parse/Parser.h"
21+
#include "llvm/ADT/TinyPtrVector.h"
22+
23+
using namespace swift;
24+
25+
namespace {
26+
class ConditionClauseResolver : public StmtTransformer {
27+
public:
28+
// A declaration is "notable" if we must
29+
// 1) Warn about it if the user has not imported Foundation
30+
// 2) Insert it into the local type declaration list
31+
class NotableDeclFinder : public ASTWalker {
32+
SourceFile &SF;
33+
public:
34+
NotableDeclFinder(SourceFile &SF) : SF(SF) {}
35+
36+
bool walkToDeclPre(Decl *D) override {
37+
if (D->isImplicit()) return false;
38+
if (isa<DestructorDecl>(D)) return false;
39+
40+
for (auto Attr : D->getAttrs()) {
41+
if (Attr->isImplicit()) continue;
42+
if (isa<ObjCAttr>(Attr) || isa<DynamicAttr>(Attr))
43+
SF.AttrsRequiringFoundation.insert(Attr);
44+
}
45+
46+
if (D->getDeclContext()->isLocalContext()) {
47+
if (auto tyDecl = dyn_cast<TypeDecl>(D)){
48+
if (!isa<TypeAliasDecl>(tyDecl))
49+
SF.LocalTypeDecls.insert(tyDecl);
50+
}
51+
}
52+
return true;
53+
}
54+
};
55+
NotableDeclFinder NDF;
56+
SmallVectorImpl<Decl *> &ExtraTLCDs;
57+
SourceFile &SF;
58+
ASTContext &Context;
59+
60+
ConditionClauseResolver(SmallVectorImpl<Decl *> &ExtraTLCDs,
61+
SourceFile &SF)
62+
: NDF(SF), ExtraTLCDs(ExtraTLCDs), SF(SF),
63+
Context(SF.getASTContext()) {}
64+
65+
void resolveClausesAndInsertMembers(IterableDeclContext *Nom,
66+
IfConfigDecl *Cond) {
67+
if (Cond->isResolved()) return;
68+
for (auto &clause : Cond->getClauses()) {
69+
// Evaluate conditions until we find the active clause.
70+
DiagnosticTransaction DT(Context.Diags);
71+
auto classification =
72+
Parser::classifyConditionalCompilationExpr(clause.Cond, Context,
73+
Context.Diags,
74+
/*fullCheck*/ true);
75+
DT.abort();
76+
if (clause.Cond && !classification.getValueOr(false)) {
77+
continue;
78+
}
79+
80+
for (auto &member : clause.Members) {
81+
if (auto innerConfig = dyn_cast<IfConfigDecl>(member)) {
82+
resolveClausesAndInsertMembers(Nom, innerConfig);
83+
} else {
84+
Nom->addMember(member);
85+
}
86+
87+
// For newly-inserted aggregates and extensions we need to warn
88+
// if they're marked '@objc'.
89+
member->walk(NDF);
90+
}
91+
92+
// Now that we've evaluated the active clause, we no longer need to
93+
// look into further clauses.
94+
break;
95+
}
96+
Cond->setResolved();
97+
}
98+
99+
BraceStmt *transformBraceStmt(BraceStmt *BS, bool TopLevel) override {
100+
SmallVector<ASTNode, 16> Elements;
101+
bool didEvaluateCondition = false;
102+
for (const auto &elt : BS->getElements()) {
103+
// If this is a declaration we may have to insert it into the local
104+
// type decl list.
105+
if (Decl *D = elt.dyn_cast<Decl *>()) {
106+
// Dig for, and warn about, decls that require Foundation.
107+
D->walk(NDF);
108+
if (auto *PBD = dyn_cast<PatternBindingDecl>(D)) {
109+
for (unsigned i = 0, e = PBD->getNumPatternEntries(); i != e; ++i) {
110+
if (auto e = PBD->getInit(i)) {
111+
PBD->setInit(i, e->walk(CF));
112+
}
113+
}
114+
}
115+
Elements.push_back(elt);
116+
continue;
117+
}
118+
119+
// If this is an expression we have to walk it to transform any interior
120+
// closure expression bodies.
121+
if (Expr *E = elt.dyn_cast<Expr *>()) {
122+
Elements.push_back(E->walk(CF));
123+
continue;
124+
}
125+
126+
// We only transform statements.
127+
Stmt *stmt = elt.dyn_cast<Stmt *>();
128+
assert(stmt && "Unhandled ASTNode Kind?");
129+
(void)stmt;
130+
131+
// Build configurations are handled later. If this isn't a build
132+
// configuration transform the statement only.
133+
IfConfigStmt *config = dyn_cast<IfConfigStmt>(stmt);
134+
if (!config) {
135+
Elements.push_back(transformStmt(stmt));
136+
continue;
137+
}
138+
139+
// Insert the configuration and evaluate its clauses if we haven't seen
140+
// it before.
141+
Elements.push_back(config);
142+
if (config->isResolved()) continue;
143+
didEvaluateCondition = true;
144+
for (auto &clause : config->getClauses()) {
145+
// Evaluate conditions until we find the active clause.
146+
DiagnosticTransaction DT(Context.Diags);
147+
auto classification =
148+
Parser::classifyConditionalCompilationExpr(clause.Cond, Context,
149+
Context.Diags,
150+
/*fullCheck*/ true);
151+
DT.abort();
152+
if (clause.Cond && !classification.getValueOr(false)) {
153+
continue;
154+
}
155+
156+
// Now that we have the active clause, look into its elements for
157+
// further statements to transform.
158+
for (auto &clauseElt : clause.Elements) {
159+
// If this is a declaration we may have to insert it into the local
160+
// type decl list.
161+
if (Decl *D = clauseElt.dyn_cast<Decl *>()) {
162+
// Nested top-level code declarations are a special case that
163+
// require extra transformation.
164+
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
165+
if (TLCD->isImplicit()) {
166+
continue;
167+
}
168+
169+
if (BraceStmt *Body = TLCD->getBody()) {
170+
BraceStmt *NewBody = transformBraceStmt(Body, true);
171+
if (NewBody != Body) {
172+
TLCD->setBody(NewBody);
173+
}
174+
}
175+
ExtraTLCDs.push_back(TLCD);
176+
} else {
177+
// For newly-inserted aggregates and extensions we need to warn
178+
// if they're marked '@objc'.
179+
D->walk(NDF);
180+
181+
if (TopLevel) {
182+
ExtraTLCDs.push_back(D);
183+
} else {
184+
Elements.push_back(D);
185+
}
186+
}
187+
continue;
188+
}
189+
190+
auto innerStmt = clauseElt.dyn_cast<Stmt *>();
191+
if (!innerStmt) {
192+
Elements.push_back(clauseElt);
193+
continue;
194+
}
195+
196+
auto innerConfig = dyn_cast<IfConfigStmt>(innerStmt);
197+
if (!innerConfig) {
198+
Elements.push_back(transformStmt(innerStmt));
199+
continue;
200+
}
201+
202+
// Nested build configurations require an additional transform to
203+
// evaluate completely.
204+
auto BS = BraceStmt::create(Context, SourceLoc(),
205+
{innerConfig}, SourceLoc());
206+
for (auto &es : transformBraceStmt(BS, TopLevel)->getElements()) {
207+
if (auto innerInnerStmt = es.dyn_cast<Stmt *>()) {
208+
Elements.push_back(transformStmt(innerInnerStmt));
209+
} else {
210+
Elements.push_back(es);
211+
}
212+
}
213+
}
214+
215+
// Now that we've evaluated the active clause, we no longer need to
216+
// look into further clauses.
217+
break;
218+
}
219+
config->setResolved();
220+
}
221+
// If we didn't have to evaluate any condition clauses, the brace
222+
// statement does not require reallocation.
223+
if (!didEvaluateCondition) {
224+
return BS;
225+
}
226+
return BraceStmt::create(Context, BS->getStartLoc(),
227+
Context.AllocateCopy(Elements),
228+
BS->getEndLoc());
229+
}
230+
};
231+
232+
class ConditionClauseWalker : public ASTWalker {
233+
public:
234+
ConditionClauseResolver R;
235+
ConditionClauseWalker(SmallVectorImpl<Decl *> &ExtraTLCDs,
236+
SourceFile &SF)
237+
: R(ExtraTLCDs, SF) {}
238+
239+
bool walkToDeclPre(Decl *D) override {
240+
if (TopLevelCodeDecl *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
241+
// For top-level declarations in libraries or in the interpreter we
242+
// extract and evaluate any conditions and their corresponding blocks.
243+
if (TLCD->isImplicit()) {
244+
return false;
245+
}
246+
247+
if (BraceStmt *Body = TLCD->getBody()) {
248+
BraceStmt *NewBody = R.transformBraceStmt(Body, true);
249+
if (NewBody != Body) {
250+
TLCD->setBody(NewBody);
251+
}
252+
}
253+
} else if (AbstractFunctionDecl *FD = dyn_cast<AbstractFunctionDecl>(D)) {
254+
// For functions, transform the body.
255+
if (FD->isImplicit()) {
256+
return false;
257+
}
258+
259+
// Take care not to force the body of delayed functions.
260+
auto Body = FD->getBody();
261+
if (!Body) {
262+
return false;
263+
}
264+
265+
BraceStmt *NewBody = R.transformBraceStmt(Body, false);
266+
if (NewBody != Body) {
267+
FD->setBody(NewBody);
268+
}
269+
} else if (auto *IDC = dyn_cast<IterableDeclContext>(D)) {
270+
// Dig for, and warn about, decls that require Foundation.
271+
D->walk(R.NDF);
272+
273+
// For aggregates and extensions, gather members under the scope of
274+
// build conditions and insert them if they're active.
275+
SmallPtrSet<IfConfigDecl *, 4> configsToResolve;
276+
for (auto member : IDC->getMembers()) {
277+
auto condition = dyn_cast<IfConfigDecl>(member);
278+
if (!condition)
279+
continue;
280+
configsToResolve.insert(condition);
281+
}
282+
for (auto condition : configsToResolve) {
283+
R.resolveClausesAndInsertMembers(IDC, condition);
284+
}
285+
}
286+
return true;
287+
}
288+
};
289+
}
290+
291+
//===----------------------------------------------------------------------===//
292+
// performConditionResolution
293+
//===----------------------------------------------------------------------===//
294+
295+
static Decl *resolveConditionalClauses(Decl *TLD, SourceFile &File,
296+
SmallVectorImpl<Decl *> &ExtraTLCD) {
297+
ConditionClauseWalker CCR(ExtraTLCD, File);
298+
TLD->walk(CCR);
299+
return TLD;
300+
}
301+
302+
void swift::performDelayedConditionResolution(Decl *D, SourceFile &BSF,
303+
SmallVectorImpl<Decl *> &Extras) {
304+
resolveConditionalClauses(D, BSF, Extras);
305+
}
306+
307+
void swift::performConditionResolution(SourceFile &SF) {
308+
// Preprocess the declarations to evaluate compilation condition clauses.
309+
SmallVector<Decl *, 8> resolvedDecls;
310+
for (auto *decl : SF.Decls) {
311+
// This first pass will unpack any top level declarations.
312+
// The next will resolve any further conditions in these
313+
// new top level declarations.
314+
resolvedDecls.push_back(decl);
315+
auto idx = resolvedDecls.size();
316+
resolvedDecls[idx - 1] = resolveConditionalClauses(decl, SF, resolvedDecls);
317+
}
318+
SF.Decls.clear();
319+
for (auto &d : resolvedDecls) {
320+
if (!SF.isScriptMode()) {
321+
if (auto TLCD = dyn_cast<TopLevelCodeDecl>(d)) {
322+
// When parsing library files, skip empty top level code declarations.
323+
auto elements = TLCD->getBody()->getElements();
324+
if (elements.empty()) continue;
325+
// These are artifacts of the condition clause transform.
326+
auto stmt = elements.front().dyn_cast<Stmt *>();
327+
if ((elements.size() == 1) && stmt && isa<IfConfigStmt>(stmt)) continue;
328+
}
329+
}
330+
SmallVector<Decl *, 2> scratch;
331+
auto processedD = resolveConditionalClauses(d, SF, scratch);
332+
assert(scratch.empty());
333+
SF.Decls.push_back(processedD);
334+
}
335+
}

0 commit comments

Comments
 (0)