-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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
Conversation
@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 *>()) { |
There was a problem hiding this comment.
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.
37b2b17
to
4423b19
Compare
There was a problem hiding this 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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 AbstractFunctionDecl
s 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); |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated change?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmph, re-inserting IfConfigStmt
s 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}}. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
0ce3c44
to
4a12d3d
Compare
ac91a4c
to
82405d6
Compare
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 |
524f584
to
a7706d3
Compare
@jrose-apple How do the tests look to you now? |
There was a problem hiding this 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 |
There was a problem hiding this comment.
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.
a7706d3
to
848edbe
Compare
OK, patched. |
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
}() |
848edbe
to
0320e1e
Compare
An unfortunately necessary thing to delay defrosting function bodies as long as we can.
0320e1e
to
cededef
Compare
var _description: String | ||
#endif | ||
|
||
public let magicConstant: Int = { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, thanks!
And since Doug already approved this I think that means it's good to go. :-) @swift-ci Please test |
@swift-ci please smoke test. |
Full test happening here for OS X |
Thanks for sticking through. I'm going to prepare the optimization patch for Doug and update the CHANGELOG in future PRs. ⛵️ |
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.
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.
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.