The design implemented in this patch is that we lower the types of accessors with pattern substitutions when lowering them against a different accessor, which happens with class overrides and protocol witnesses, and that we introduce pattern substitutions when substituting into a non-patterned coroutine type. This seems to achieve consistent abstraction without introduce a ton of new complexity.
An earlier version of this patch tried to define witness thunks (conservatively, just for accessors) by simply applying the requirement substitutions directly to the requirement. Conceptually that should work, but I ran into a lot of trouble with things that assumed that pattern substitutions didn't conceal significant substitution work. for example, resolving a dependent member in a component type could find a new use of an opaque archetype when the code assumed that such types had already been substituted away. So while I think that is definiteely a promising direction, I had to back that out in order to make the number of changes manageable for a single PR.
As part of this, I had to fix a number of little bugs here and there, some of which I just introduced. One of these bugfixes is a place where the substitution code was trying to improperly abstract function types when substituting them in for a type parameter, and it's been in the code for a really long time, and I'm really not sure how it's never blown up before.
I'm increasingly of the opinion that invocation substitutions are not actually necessary, but that --- after we've solved the substitution issues above --- we may want the ability to build multiple levels of pattern substitution so that we can guarantee that e.g. witness thunks always have the exact component structure of the requirement before a certain level of substitution, thus allowing the witness substitutions to be easily extracted.
In order to allow this, I've had to rework the syntax of substituted function types; what was previously spelled `<T> in () -> T for <X>` is now spelled `@substituted <T> () -> T for <X>`. I think this is a nice improvement for readability, but it did require me to churn a lot of test cases.
Distinguishing the substitutions has two chief advantages over the existing representation. First, the semantics seem quite a bit clearer at use points; the `implicit` bit was very subtle and not always obvious how to use. More importantly, it allows the expression of generic function types that must satisfy a particular generic abstraction pattern, which was otherwise impossible to express.
As an example of the latter, consider the following protocol conformance:
```
protocol P { func foo() }
struct A<T> : P { func foo() {} }
```
The lowered signature of `P.foo` is `<Self: P> (@in_guaranteed Self) -> ()`. Without this change, the lowered signature of `A.foo`'s witness would be `<T> (@in_guaranteed A<T>) -> ()`, which does not preserve information about the conformance substitution in any useful way. With this change, the lowered signature of this witness could be `<T> @substituted <Self: P> (@in_guaranteed Self) -> () for <A<T>>`, which nicely preserves the exact substitutions which relate the witness to the requirement.
When we adopt this, it will both obviate the need for the special witness-table conformance field in SILFunctionType and make it far simpler for the SILOptimizer to devirtualize witness methods. This patch does not actually take that step, however; it merely makes it possible to do so.
As another piece of unfinished business, while `SILFunctionType::substGenericArgs()` conceptually ought to simply set the given substitutions as the invocation substitutions, that would disturb a number of places that expect that method to produce an unsubstituted type. This patch only set invocation arguments when the generic type is a substituted type, which we currently never produce in type-lowering.
My plan is to start by producing substituted function types for accessors. Accessors are an important case because the coroutine continuation function is essentially an implicit component of the function type which the current substitution rules simply erase the intended abstraction of. They're also used in narrower ways that should exercise less of the optimizer.
Creating a @_dynamicReplacement function requires the creation of a
reference to the original function. We need to call SILGenModule's
getFunction to satisfy all the assertions in place.
rdar://59774606
If we're emitting a designated constructor inside a constrained extension,
we have to use the correct substitution map for calling the property wrapper
backing initializer.
Factor out the computation of this substitution map and use it consistently.
Fixes <rdar://problem/59245068>.
A keypath component for a stored property can take one of several forms:
- The property offset is known to be constant at compile-time.
This is used in the simplest cases for classes and structs.
- The property offset is not constant, but can be loaded from a global.
This is used for classes that require runtime resilient layout, but where
the offsets do not depend on the generic context.
- The property offset is not constant, and must be loaded from metadata.
This is the case where the offset depends on the generic context. Here,
we were only set up to load it from a fixed offset in the metadata.
This works for generic structs, or generic classes where the superclass
chain does not cross a resilience boundary.
However, if a resilience boundary is crossed, the offset of the field
offset in the metadata must itself be obtained at runtime by adding a
constant to a value loaded from a global. This case is not supported by
the current keypath ABI due to an oversight.
I filed <rdar://problem/59777983> to track extending the ABI to handle
this more elegantly in the future.
Fixes <rdar://problem/59617119>.
Even if differently-substituted function types have different value representations,
we can still share reabstraction and bridging thunks among types that are equivalent after
substitution, so handle these by generating thunks in terms of the unsubstituted type and
converting to the needed substitution form at the use site.
emitKeyPathComponentForDecl was only checking if the setter was
accessible from the current module, not the current function.
This failed when accessing an internal setter from a module
imported for testing.
Use the substitution map for the LValue component to properly handle the
case where we need to re-abstract the argument to the assign_by_wrapper
setter.
Resolves rdar://59553318
Codegen for the assign_by_wrapper instruction emits prepared arguments
directly into the initializer or accessor functions. This means it
misses out on the CC matching code that the general apply path uses.
In this particular case, an enum value was constructed within the
resilience boundary, but passed to the setter for a wrapper property
that expected it to be in memory.
Check the calling convention before we emit the assign_by_wrapper
instruction, and materialize an address for indirect CCs as required.
resolves rdar://59071930
emitKeyPathComponentForDecl was only checking if the setter was
accessible from the current module, not the current function.
This failed when accessing an internal setter from a module
imported for testing.
Since resilient class metadata is built at runtime, we don't actually
care if there are missing vtable entries. The restriction was relaxed
in Sema in 9117c5728a, but SILGen still
had an assertion here.
Add a test and relax the assertion.
Fixes <rdar://problem/58644615>.
(BaseT, @inout @unowned(unsafe) T) -> @guaranteed T
The reason for the weird signature is that currently the Builtin infrastructure
does not handle results well. Also, note that we are not actually performing a
call here. We are SILGening directly so we can create a guaranteed result.
The intended semantics is that one passes in a base value that guarantees the
lifetime of the unowned(unsafe) value. The builtin then:
1. Borrows the base.
2. Loads the trivial unowned (unsafe), converts that value to a guaranteed ref
after unsafely unwrapping the optional.
3. Uses mark dependence to tie the lifetimes of the guaranteed base to the
guaranteed ref.
I also updated my small UnsafeValue.swift test to make sure we get the codegen
we expect.
The signature is:
(T, @inout @unowned(unsafe) Optional<T>) -> ()
The reason for the weird signature is that currently the Builtin infrastructure
does not handle results well.
The semantics of this builtin is that it enables one to store the first argument
into an unowned unsafe address without any reference counting operations. It
does this just by SILGening the relevant code. The optimizer chews through this
code well, so we get the expected behavior.
I also included a small proof of concept to validate that this builtin works as
expected.
If a protocol has a superclass constraint, the existential type can be
upcast to the class type given by the constraint. This wasn't implemented
in the function conversion code path, leading to a crash.
Fixes <https://bugs.swift.org/browse/SR-12018> / <rdar://problem/58861162>.
For those who are unaware, a transformation terminator is a terminator like
switch_enum/checked_cast_br that always dominate their successor blocks. Since
they dominate their successor blocks by design and transform their input into
the args form, we can validate that they obey guaranteed ownership semantics
just like a forwarding instruction.
Beyond removing unnecessary code bloat, this also makes it significantly more
easier to optimize/work with transformation terminators when converting @owned
-> @guaranteed since we do not need to find end_borrow points when the owned
value is consumed.
<rdar://problem/59097063>
The original design was to make it so that end_borrow tied at the use level its
original/borrowed value. So we would have:
```
%borrowedVal = begin_borrow %original
...
end_borrow %borrowedVal from %original
```
In the end we decided not to use that design and instead just use:
```
%borrowedVal = begin_borrow %original
...
end_borrow %borrowedVal
```
In order to enable that transition, I left the old API for end_borrow that took
both original and borrowedVal and reimplemented it on top of the new API that
just took the borrowedVal (i.e. the original was just a dead arg).
Now given where we are in the development, it makes sense to get rid of that
transition API and move to just use the new API.
This reverts commit a3b68e6df5.
Speculative revert because I believe it is the cause of the failures on
the swift-master-source-compat-suite-enable-verify-exclusivity bot.
rdar://58529726