|
| 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