Skip to content

SE-0075 Implement canImport #5778

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 14, 2016
Merged

Conversation

CodaFi
Copy link
Contributor

@CodaFi CodaFi commented Nov 14, 2016

SE-0075 requests that we offer a build-time check for the importability of modules. This check, however, is particularly expensive so we can no longer resolve every condition statically. Conditions that are not essential to the health of the parser are left unevaluated and deferred to a new phase post-parse called "Condition Resolution". Condition Resolution will also skip functions that have been deferred to keep Code Completion speedy; resolution can be done on-demand.

Resolves SR-1560.

@CodaFi CodaFi changed the title [WIP][SR-0075][1/2] Transfer Build Config Clause Resolution To NameBinding [WIP][SE-0075][1/2] Transfer Build Config Clause Resolution To NameBinding Nov 14, 2016
@CodaFi
Copy link
Contributor Author

CodaFi commented Nov 14, 2016

@jrose-apple for Parse, @DougGregor for Sema.

Come On Down

📯 📯 📯

// Evaluate conditions until we find the active clause.
if (!clause.Cond || evaluateCondition(clause.Cond)) {
for (auto &e : clause.Elements) {
if (auto innerStmt = e.dyn_cast<Stmt *>()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to un-pyramid-of-doom this mess.

@CodaFi CodaFi force-pushed the fine-imported-goods branch 2 times, most recently from 37b2b17 to 4423b19 Compare November 15, 2016 02:46
Copy link
Contributor

@jrose-apple jrose-apple left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preliminary comments on your WIP.

@@ -1895,32 +1890,24 @@ class IfConfigDecl : public Decl {
/// An array of clauses controlling each of the #if/#elseif/#else conditions.
/// The array is ASTContext allocated.
ArrayRef<IfConfigDeclClause> Clauses;
SourceLoc StartLoc;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why bother storing this separately?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At one point I had caused destructive updates to the clause array, but that was ridiculous and I reverted it. Going to revert this part too.

@@ -2177,7 +2173,7 @@ ParserStatus Parser::parseDecl(ParseDeclOptions Flags,
}

if (auto SF = CurDeclContext->getParentSourceFile()) {
if (!getScopeInfo().isInactiveConfigBlock()) {
if (!(Flags & PD_InIfConfig)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment mentioning that other attributes will be handled later?

@@ -5092,6 +5089,9 @@ ParserResult<ClassDecl> Parser::parseDeclClass(SourceLoc ClassLoc,
Scope S(this, ScopeKind::ClassBody);
ParseDeclOptions Options(PD_HasContainerType | PD_AllowDestructor |
PD_InClass);
if (Flags & PD_InIfConfig) {
Options |= PD_InIfConfig;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. The old flag was used for immediate children of the #if, no? Using it this way is definitely changing the meaning. Is everywhere else that checks it equipped to handle that?

(The answer might be "yes"; just checking.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was no flag for this before. It used to be a function of "scope", but I felt it was inappropriate for scope to ask the question "is this a condition" let alone "is this an inactive condition" like it did before.

ConditionalBlockKind ==
BraceItemListKind::InactiveConditionalBlock);
}
initScope.emplace(this,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole trick was just because we were conditionally constructing the scope. It's okay to just do it normally now instead.

// Add the #if block itself as a TLCD if necessary
if (Kind == BraceItemListKind::TopLevelCode) {
// Add the #if block itself as a TLCD if necessary. They will be
// flattened away in namebinding.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: "name binding".

(which, as a pass, needs a new name anyway; maybe "import resolution"? but that's for later)

}
} else if (AbstractFunctionDecl *FD = dyn_cast<AbstractFunctionDecl>(D)) {
// For functions, transform the statements in the body.
// FIXME: This can be deferred until type checking.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we do need to figure out how to do this. Code completion doesn't parse function bodies unless it needs to, but this will probably fault them all in eagerly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DougGregor Any thoughts on this? I was thinking of either exposing this class in a separate file (ConditionResolution.cpp?) or adding a kind of decl that closes over the resolution procedure for AbstractFunctionDecls so TC can just un-thunk-ify on the spot.

SmallVector<Decl *, 8> scratch;
auto processedD = resolveConditionalClauses(d, Binder, scratch);
assert(scratch.empty());
SF.Decls.push_back(processedD);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TopLevelCodeDecls definitely need to stay in source order.

@@ -91,6 +91,7 @@ func testError() {
} catch is TestError {
} catch {}

print(testErrorNSError)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to the test/decl/var/usage.swift regression.

// PASS_PRINT_AST: #elseif
// PASS_PRINT_AST: #else
// PASS_PRINT_AST: #endif
// PASS_PRINT_AST-NOT: #if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct either. It's important to have the document structure in place for SourceKit and other tools.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmph, re-inserting IfConfigStmts should do the trick, then.

let abc = 42 // no warning.
var mut = 18 // no warning.
let abc = 42 // expected-warning{{initialization of immutable value 'abc' was never used; consider replacing with assignment to '_' or removing it}}.
var mut = 18 // expected-warning{{initialization of variable 'mut' was never used; consider replacing with assignment to '_' or removing it}}.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make it clear, this is a regression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The usage clause is inactive, why shouldn't we warn here exactly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it might be active under another build configuration, and there's no way to silence it otherwise.

@CodaFi CodaFi force-pushed the fine-imported-goods branch 9 times, most recently from 0ce3c44 to 4a12d3d Compare November 22, 2016 03:54
@CodaFi CodaFi changed the title [WIP][SE-0075][1/2] Transfer Build Config Clause Resolution To NameBinding SE-0075 Implement canImport Nov 22, 2016
@CodaFi CodaFi force-pushed the fine-imported-goods branch 12 times, most recently from ac91a4c to 82405d6 Compare November 25, 2016 02:12
@jrose-apple
Copy link
Contributor

Hm. I didn't think about tying it to delaying a whole function body, but that might be good enough…although we still need to actually do that during normal compilation. (IIRC we currently only do it for code completion.)

I don't like the idea of Parse being able to import modules, but the benefit of having parseDelayedDecl Just Work might outweigh that.

Please do add some test cases that show #if canImport(…) in function bodies, in top-level contexts under -parse-as-library, and in closure bodies used to initialize a variable.

@CodaFi CodaFi force-pushed the fine-imported-goods branch from 524f584 to a7706d3 Compare December 14, 2016 19:06
@CodaFi
Copy link
Contributor Author

CodaFi commented Dec 14, 2016

@jrose-apple How do the tests look to you now?

Copy link
Contributor

@jrose-apple jrose-apple left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, but you're still missing

  • #if at the top level of a -parse-as-library file.
  • #if in the initializer of a property.
  • #if in a non-primary file (which I forgot to ask for but which is worth testing)

}

/// Create an instance initialized to `value`.
@_transparent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: please don't use @_transparent in tests where it's not relevant.

Implemented in the naive way by way of the current ASTContext’s
‘LoadedModules’.  In theory this list should be in one-to-one
correspondence with the definition of “importable” as any module that
is required for compilation is loaded *before* we start parsing the
file.  This means we don’t have to load the module, just check that
it’s in the set of loaded modules.

Note that this approach will most likely fall apart if submodules are
introduced and are lazily loaded.  At that point this will need to be
refactored into a format that defers the checking of conditions
sometime before or during typechecking (after namebinding).
This completely removes Parse’s ability to make any judgement calls
about compilation conditions, instead the parser-relevant parts of
‘evaluateConditionalCompilationExpr’ have been moved into
‘classifyConditionalCompilationExpr’ where they exist to make sure only
decls that we want to parse actually parse later.

The condition-evaluation parts have been moved into NameBinding in the
form of a Walker that evaluates and collapses IfConfigs.  This walker
is meant as an homage to PlaygroundLogger.  It should probably be
factored out into a common walker at some point in the future.
@CodaFi CodaFi force-pushed the fine-imported-goods branch from a7706d3 to 848edbe Compare December 14, 2016 19:59
@CodaFi
Copy link
Contributor Author

CodaFi commented Dec 14, 2016

OK, patched.

@jrose-apple
Copy link
Contributor

Thanks! This is the property one I'm thinking of, sorry.

let value: Int = {
#if canImport(Swift)
  error() // expected-error {{blah blah blah}}
#else
  error() // should not happen
#endif
}()

@CodaFi CodaFi force-pushed the fine-imported-goods branch from 848edbe to 0320e1e Compare December 14, 2016 20:37
An unfortunately necessary thing to delay defrosting function bodies as
long as we can.
@CodaFi CodaFi force-pushed the fine-imported-goods branch from 0320e1e to cededef Compare December 14, 2016 20:39
var _description: String
#endif

public let magicConstant: Int = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrose-apple Is this what you had in mind?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, thanks!

@jrose-apple
Copy link
Contributor

And since Doug already approved this I think that means it's good to go. :-)

@swift-ci Please test

@CodaFi
Copy link
Contributor Author

CodaFi commented Dec 14, 2016

@swift-ci please smoke test.

@CodaFi
Copy link
Contributor Author

CodaFi commented Dec 14, 2016

Full test happening here for OS X

@CodaFi
Copy link
Contributor Author

CodaFi commented Dec 14, 2016

Thanks for sticking through. I'm going to prepare the optimization patch for Doug and update the CHANGELOG in future PRs.

⛵️

@CodaFi CodaFi merged commit 8e4e857 into swiftlang:master Dec 14, 2016
@CodaFi CodaFi deleted the fine-imported-goods branch December 14, 2016 21:57
jrose-apple added a commit to jrose-apple/swift that referenced this pull request Dec 20, 2016
…oods"

This reverts commit 8e4e857, reversing
changes made to eaa7cb9.
CodaFi added a commit to CodaFi/swift that referenced this pull request Jan 6, 2017
This reverts the contents of swiftlang#5778 and replaces it with a far simpler
implementation of condition resolution along with canImport.  When
combined with the optimizations in swiftlang#6279 we get the best of both worlds
with a performance win and a simpler implementation.
ematejska pushed a commit to ematejska/swift that referenced this pull request Jan 11, 2017
This reverts the contents of swiftlang#5778 and replaces it with a far simpler
implementation of condition resolution along with canImport.  When
combined with the optimizations in swiftlang#6279 we get the best of both worlds
with a performance win and a simpler implementation.
benrimmington added a commit to swiftlang/swift-evolution that referenced this pull request Sep 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants