The top-level scope for a conditional clause with a pattern is now
ConditionalClausePatternUseScope, which introduces the pattern's
bindings.
Since the patterns are not visible in their own initializer, a new
ConditionalClauseInitializerScope is used for the initializer.
While it is nested inside of the ConditionalClausePatternUseScope,
it's lookup parent skips one level, giving us the desired behavior.
Today, the reported source range of a GuardStmtScope is just that of
the statement itself, ending after the 'else' block. Similarly, a
PatternEntryDeclScope ends immediately after the initializer
expression.
Since these scopes introduce names until the end of the parent
BraceStmt (meaning they change the insertion point), we were
relying on the addition of child scopes to extend the source
range.
While we still need the hack for extending source ranges to deal
with string interpolation, at least in the other cases we can
get rid of it.
Note that this fixes a bug where a GuardStmtScope would end
before an inactive '#if' block if the inactive '#if' block was
the last element of a BraceStmt.
It wasn't used for anything, and it was always set based on whether
the declaration in question was a GenericTypeParamDecl, a ParamDecl,
or something else.
The old behavior was that ASTScope would introduce all VarDecls
defined in a BraceStmt at the beginning of the BraceStmt.
I recently enabled the use of PatternEntryDeclScopes, which
introduce the binding at its actual source location instead of
at the beginning of the parent statement.
This patch now makes use of the new information by having
UnqualifiedLookupFlags::IncludeOuterResults toggle between
the two behaviors. When searching for outer results, we also
consider all VarDecls in a BraceStmt, not just those in scope.
This is implemented by giving AbstractASTScopeDeclConsumer a
new entry point, consumePossiblyNotInScope(). When looking up
into a BraceStmt, all VarDecls are passed in to this entry
point.
The default implementation does nothing, which means that
ASTScope::lookupSingleLocalDecl() now respects source locations
when searching for bindings, just like parse-time lookup.
However, Sema's preCheckExpression() pass, which sets
Flags::IgnoreOuterResults, will continue to find
forward-referenced VarDecls, just as it did with the old
context-based DeclContext lookup.
If we're searching for a declaration with a given name, the name
should be entirely encapsulated inside the DeclConsumer.
Otherwise, there might not be a specific name at all, if we're
performing code completion for example (once LookupVisibleDecls
starts to use ASTScope, anyway).
Bindings are not visible from inside their own initializer, eg this
is invalid:
var (x, y) = (y, x)
The PatternEntryDeclScope which starts after the 'var' introduces x
and y, and it contains the PatternEntryInitializerScope which starts
after the '='.
To model this properly in ASTScope, the PatternEntryInitializerScope
overrides getLookupParent() to skip the PatternEntryDeclScope that
contains it.
I believe this is simpler than having PatternEntryDeclScope begin
at the source location following the initializer, because it is hard
to compute this source location in a way that works with invalid
code, for example the 'var' might be nested inside of a BraceStmt
with the closing '}' missing.
DifferentiableAttr, SpecializeAttr and CustomAttr create scopes
because lookups can be performed from inside them. Previously
we would sort DifferentiableAttr and SpecializeAttr by source
order, but we consider that a declaration may also have more
than one _kind_ of attribute requiring special handling.
The case where this comes up is properties that have both a
DifferentiableAttr and CustomAttr. This fixes test failures in
AutoDiff tests with subsequent commits in this PR.
This centralizes some invariants around the 'self' parameter.
While all ConstructorDecls and DestructorDecls have a 'self',
even if they're invalid because they're not nested inside a type,
we don't want to consider this as the base 'self' for lookups.
Eg, consider this invalid code:
class C {
func f() {
init() {
x
}
}
}
The base for the lookup should be f.self, not f.init.self.
Let's use a ClosureParametersScope for all closures, even those
without an 'in' keyword. This eliminates the need for the
ClosureBodyScope and WholeClosureScope.
Also, let's move the lookup of capture list bindings from
CaptureParametersScope to CaptureListScope. This eliminates the
need for CaptureParametersScope to store a reference to the
capture list, which allows us to remove the AbstractClosureScope
base class entirely.
In a code snippet like the following,
static func ==(a: Foo, b: Foo) -> Bool {
switch (a, b) {
case (.x(let aa), .x(let bb)) where condition(aa, bb),
(.y(let aa), .y(let bb)) where condition(aa, bb):
return aa == bb
default:
return false
}
}
The CaseStmt defines two patterns, both of which bind
'aa' and 'bb'. The first 'aa'/'bb' are in scope inside the
first 'where' clause, and the second 'aa'/'bb' are in scope
inside the second 'where' clause.
Furthermore, the parser creates a "fake" VarDecl for
'aa' and 'bb' to represent the phi node merging the two
values along the two control flow paths; these are in scope
inside the body.
Model this situation by introducing a new CaseLabelItemScope
for the 'where' clauses, and a CaseStmtBodyScope for the
body.
Rather than depending on the tracking of state in switch cases to
remember the case statements that are the source and destination for a
`fallthrough` statement, compute them using ASTScope to find the
nearest enclosing case (the source) and then find the next case in the
same `switch` statement (the destination).
`guard` statements are prohibited from having labels, and aren't
actually break/continue targets. Stop producing them as results from
ASTScope-based labeled statement lookup and don't add them as a
labeled statement in the recursive walk.
Use the scope map to implement lookup for the labeled statements that
are visible from a given source location, which is a lexical property
that is currently handled with stateful tracking in the type checker.
For now, merely assert that the results of this approach are identical
to the state tracked in the statement type checker.
Today, ASTScope only creates NominalTypeWhereScopes for the 'where'
clause of an extension or a protocol. All other generic declarations
model the 'where' clause as part of the declaration's scope itself
(but it's not part of the body scope).
Lookups into the NominalTypeWhereScope would look inside the type,
because this is how we want protocol and protocol extension scopes
to work -- you should be able to refer to associated types without
a 'Self' prefix.
Let's add a conditional check for protocols and extensions to the
implementation of NominalTypeWhereScope, so that we can use this
scope for all other 'where' clauses and keep the old behavior.
Note that today, even _concrete_ extensions have this behavior,
which is perhaps slightly unexpected:
class C<T> {
typealias U = Int
}
extension C where T == U {}
It would be nice to tighten up the rule here but there's already a
test in the suite that depends on it.
Like switch cases, a catch clause may now include a comma-
separated list of patterns. The body will be executed if any
one of those patterns is matched.
This patch replaces `CatchStmt` with `CaseStmt` as the children
of `DoCatchStmt` in the AST. This necessitates a number of changes
throughout the compiler, including:
- Parser & libsyntax support for the new syntax and AST structure
- Typechecking of multi-pattern catches, including those which
contain bindings.
- SILGen support
- Code completion updates
- Profiler updates
- Name lookup changes
`@differentiable` attributes may contain `where` clauses referencing generic
parameters from some generic context, just like `@_specialize` attributes.
Without special ASTScope support for `@differentiable` attributes,
ASTScopeLookup.cpp logic tries to resolve the generic parameter `DeclName`s in
`where` clauses based on source location alone
(`ASTScopeImpl::findChildContaining`) and fails.
The fix is to add a special `DifferentiableAttributeScope`, mimicking
`SpecializeAttributeScope`. Every `@differentiable` attribute has its own scope,
derived from the declaration on which it is declared. Unlike `@_specialize`,
`@differentiable` may also be declared on `AbstractStorageDecl` declarations
(subscripts and variables).
Upstreams https://github.com/apple/swift/pull/27451.
Progress towards TF-828: upstream `@differentiable` attribute type-checking.
* WIP implementation
* Cleanup implementation
* Install backedge rather than storing array reference
* Add diagnostics
* Add missing parameter to ResultFinderForTypeContext constructor
* Fix tests for correct fix-it language
* Change to solution without backedge, change lookup behavior
* Improve diagnostics for weak captures and captures under different names
* Remove ghosts of implementations past
* Address review comments
* Reorder member variable initialization
* Fix typos
* Exclude value types from explicit self requirements
* Add tests
* Add implementation for AST lookup
* Add tests
* Begin addressing review comments
* Re-enable AST scope lookup
* Add fixme
* Pull fix-its into a separate function
* Remove capturedSelfContext tracking from type property initializers
* Add const specifiers to arguments
* Address review comments
* Fix string literals
* Refactor implicit self diagnostics
* Add comment
* Remove trailing whitespace
* Add tests for capture list across multiple lines
* Add additional test
* Fix typo
* Remove use of ?: to fix linux build
* Remove second use of ?:
* Rework logic for finding nested self contexts