The idea here is to split up the logic into three parts:
- computation of the final ImplInfo for lazy, property wrappers
and @NSManaged
- deciding if we should add accessors or not
- actually adding the accessors
Instead of requiring that function body synthesizers will always call
setBody(), which is annoyingly stateful, have function body synthesizers
always return the synthesized brace statement along with a bit that
indicates whether the body was already type-checked. This takes us a
step closer to centralizing the mutation of the body of a function.
Also since we're lazier about validating accessors now, relax some
checks in the ASTVerifier so that it can tolerate implicit accessors
without interface types. All other declarations must still have
interface types in verifyChecked().
ASTVerifier: Tolerate implicit accessors without interface types
Extend handling of enclosing-self subscripts by differentiating
between the original wrapped property (which now goes through
`subscript(_enclosingInstance:wrapped:storage:)`) and the projected
property (which goes through
`subscript(_enclosingInstance:projected:storage:)`). The new middle
argument provides a key path to the property that was accessed,
allowing one to distinguish the property being updated.
Allow property wrapper types to support a second access pattern for
instance properties of classes. When supported, the property wrapper's
static subscript(_enclosingInstance:storage:) is provided with the
enclosing "self" and a reference-writable key path referring to the
backing storage property.
Implements rdar://problem/52222560.
Fix a bug with cross-file uses of the synthesized _foo/$foo for
properties with attached wrappers by implicitly triggering the
appropriate synthesis during name lookup.
Fixes rdar://problem/51725203.
When the outermost property wrapper associated with a property has a
`wrapperValue`, create the projection property (with the `$` prefix)
at the same access level as the original property. This puts the
wrapped-value interface and the projection interface at the same level.
The newly-introduced @_projectionValueProperty attribute is implicitly
created to establish the link between the original property and the
projection value within module interfaces, where both properties will
be explicitly written out.
My recent refactoring of default arguments for the memberwise initializer
accidentally dropped support for getting a default argument when the
attached property wrapper has an init(). Reinstate that support,
fixing rdar://problem/52116923.
If a stored property has a didSet/willSet, we currently synthesize
the getter and the setter inside the parser. I want to get rid of
the parser's code path for doing that and consolidate all accessor
synthesis here in Sema.
Note that the parser did not synthesize a modify. This is now more
explicit in the code.
Also, we have to relax an assertion since addMemberToContextIfNeeded()
now gets called on declarations in local contexts.
Allow both explicit initialization and initial values to work together, e.g.,
```
@Clamping(min: 0, max: 255) var red: Int = 127
```
gets initialized as
```
Clamping(initialValue: 127, min: 0, max: 255)
```
When multiple property wrapper attributes are provided on a declaration,
compose them outside-in to form a composite property wrapper type. For
example,
@A @B @C var foo = 17
will produce
var $foo = A(initialValue: B(initialValue: C(initialValue: 17)))
and foo's getter/setter will access "foo.value.value.value".
By calling isFinal() first in makeFinal(), we were triggering
computation of IsFinalRequest and caching a value of 'false'.
With lazy properties, we did direct storage access, and no
accessors were synthesized anyway, but add a test for that.
With property wrappers this now means the backing storage
property is final, as intended (?).
When the backing storage of a wrapped property is default-initialized via the
property wrapper type's init(), don't count that as a direct initialization
of the backing storage for the purposes of constructing the memberwise
initializer. Instead, treat this case the same as if there were no initializer,
keying the form of the memberwise initializer off the presence of
init(initialValue:).
Various optimizations / shortcuts in type checking property wrappers meant
that we weren't consistently verifying that a wrapped property type is
identical to the type of the 'value' property of the wrapper. Do so.
Fixes SR-10899 / rdar://problem/51588022.
Previously, we'd clone the superclass's initializer and update its
interface type, but we didn't reset the TypeLoc. This meant the
synthesized overridden initializer's parameter typeLocs would still be in
the parent decl context, which caused the ASTPrinter to print the
unsubstituted type.
rdar://50914733
Similarly to trivial accessors, the body of a synthesized
designated initializer override is simple enough that we can
build it directly without involving the constraint solver.
This includes all synthesized accessors except for lazy getters
and observer setters.
Building checked AST has certain advantages:
- It is faster, avoiding the need to create and solve ConstraintSystems
or performing various diagnostic and semantic walks over the AST.
- It allows us to postpone delayed body synthesis until needed in SILGen,
instead of having to walk a list of "external declarations" in Sema.
It also unblocks lazier conformance checking. Now that SILGen can
trigger conformance checking on its own, we need to be able to
synthesize bodies of accessors that witness protocol requirements.
This will allow us to eventually remove Sema's "used conformances"
list.
Note that for now, various utility functions in CodeSynthesis.cpp are
used for both checked and unchecked function bodies, so they take a
'typeChecked' boolean parameter that complicates some logic more than
necessary. Once the remaining body synthesizers are refactored to
build type-checked AST, the 'typeChecked' flag will go away.
When a property delegate type has a default initializer, use that to
implicitly initialize properties that use that delegate and do not have
their own initializers. For example, this would allow (e.g.), delegate
types like the DelayedImmutable and DelayedMutable examples to drop
the explicit initialization, e.g.,
```
@DelayedMutable var foo: Int
```
would be implicitly initialized via
```
$foo = DelayedMutable()
```
This is a simplistic implementation that does not yet propertly handle
definite initialization.
Fixes rdar://problem/50266039.