https://forums.swift.org/t/improving-the-representation-of-polymorphic-interfaces-in-sil-with-substituted-function-types/29711
This prepares SIL to be able to more accurately preserve the calling convention of
polymorphic generic interfaces by letting the type system represent "substituted function types".
We add a couple of fields to SILFunctionType to support this:
- A substitution map, accessed by `getSubstitutions()`, which maps the generic signature
of the function to its concrete implementation. This will allow, for instance, a protocol
witness for a requirement of type `<Self: P> (Self, ...) -> ...` for a concrete conforming
type `Foo` to express its type as `<Self: P> (Self, ...) -> ... for <Foo>`, preserving the relation
to the protocol interface without relying on the pile of hacks that is the `witness_method`
protocol.
- A bool for whether the generic signature of the function is "implied" by the substitutions.
If true, the generic signature isn't really part of the calling convention of the function.
This will allow closure types to distinguish a closure being passed to a generic function, like
`<T, U> in (*T, *U) -> T for <Int, String>`, from the concrete type `(*Int, *String) -> Int`,
which will make it easier for us to differentiate the representation of those as types, for
instance by giving them different pointer authentication discriminators to harden arm64e
code.
This patch is currently NFC, it just introduces the new APIs and takes a first pass at updating
code to use them. Much more work will need to be done once we start exercising these new
fields.
This does bifurcate some existing APIs:
- SILFunctionType now has two accessors to get its generic signature.
`getSubstGenericSignature` gets the generic signature that is used to apply its
substitution map, if any. `getInvocationGenericSignature` gets the generic signature
used to invoke the function at apply sites. These differ if the generic signature is
implied.
- SILParameterInfo and SILResultInfo values carry the unsubstituted types of the parameters
and results of the function. They now have two APIs to get that type. `getInterfaceType`
returns the unsubstituted type of the generic interface, and
`getArgumentType`/`getReturnValueType` produce the substituted type that is used at
apply sites.
Structurally prevent a number of common anti-patterns involving generic
signatures by separating the interface into GenericSignature and the
implementation into GenericSignatureBase. In particular, this allows
the comparison operators to be deleted which forces callers to
canonicalize the signature or ask to compare pointers explicitly.
This memoizes the result, which is fine for all callers; the only
exception is open existential types where each new open existential
now explicitly gets a unique generic environment, allocated by
calling GenericEnvironment::getIncomplete().
If an override B.f() is more visible than a base method A.f(), it is
possible that an override C.f() of B.f() cannot see the original method
A.f().
In this case, we would encounter linker errors if we referenced the
method descriptor or method dispatch thunk for A.f().
Make this work by treating B.f() as the least derived method in this
case, and ensuring that the vtable thunk for B.f() dispatches through
the vtable again.
Fixes <rdar://problem/48330571>, <https://bugs.swift.org/browse/SR-10648>.
When checking if a vtable override is ABI compatible with the
base class method, make sure to check yields too.
Also, add support for coroutines to vtable thunks, using code
that I've copy and pasted and tweaked from witness thunks.
(It would be nice to combine witness thunks and vtable thunks
into a single code path for 'method thunks', but that requires
some additional refactoring, so live with the copy and paste
for now).
The machinery assumes that it will always have a +1 value. I am attempting to do
the minimal fix here for cherry-picking purposes.
There isn't a test case with this commit since the immutable address verifier
(in the next commit) verifies that this is done correctly. The specific test
that will trip is vtable_thunks_reabstraction.swift.
rdar://50212579
This was partially implemented but the check looked at the lowered
types and not the AST types, and DynamicSelfType is erased at the
top level of a lowered type.
Also use the new mangling for reabstraction thunks with self, to
ensure we don't emit the same symbol with two different lowered
types.
Fixes <https://bugs.swift.org/browse/SR-10309>, <rdar://problem/49703441>.
OpaqueValueState used to store a SILValue, so back then the IsConsumable flag
was meaningful. But now we can just check if the ManagedValue has a cleanup
or not.
Also, we were passing around an opened ArchetypeType for no good reason.
The problem here is that without this patch we emit code like this:
bb0(%0 : @owned $T):
%1 = partial_apply %foo(%0)
%2 = mark_dependence %1 on %0
Since a partial_apply consumes the object, the mark_dependence is a use after
free (especially if one has any further uses of %0 after the mark_dependence).
So what I did was I copied the value before creating the partial_apply. So
now we get this:
bb0(%0 : @owned $T):
%1 = copy_value %0
%2 = partial_apply %foo(%1)
%3 = mark_dependence %2 on %0
...
destroy_value %0
This ensures that one can actually have uses after the mark_dependence of both
operands.
This enables ownership verification to be enabled on
Interpreter/enforce_exclusive_access.
rdar://48521061
Found via the ownership verifier running on IRGen/outlined_copy_addr.swift.
We treat initialization of an existential as a +1 operation, so there is the
possibility that we would have double destroyed a value. In fixing this I
followed what manageOpaqueValue did on the first code path, since it is handling
this case correctly (and using the SGFContext to do so to boot!).
To ensure this is tested, I enabled ownership verification on the test and since
it seems to be exposing a SILGen code path that we aren't testing currently,
converted it into a SILGen test.
There are instances of currying that require full reabstraction,
such as when partially-applying a concrete override of a generic
class method.
Fixes rdar://45671537 and SR-4425.
Specifically we add a groups of APIs for destructure operations. The destructure
helpers are a family of functions built around
emitDestructureValueOperation. These in ossa produce destructures and pass the
results off to the caller in some manner that hides the internal destructure
instruction. In non-ossa, the appropriate projections are created and passed off
to the caller.
We lower away a top-level DynamicSelfType, so let's just strip them off
before trying the other conversions. For now this is NFC, but we can end
up here after the next patch.
Even then we shouldn't end up here except when emitting protocol witnesses.
The relevant case is when a base class method (return @dynamic_self Base)
is used by a derived class to witness a requirement on a protocol
(which returns @dynamic_self Derived since its a derived class conformance).
In this case, we know both values have the same type, but the lowered
types are $Base and $Derived, respectively, so we must emit an unchecked
downcast.
In a previous commit, I banned in the verifier any SILValue from producing
ValueOwnershipKind::Any in preparation for this.
This change arises out of discussions in between John, Andy, and I around
ValueOwnershipKind::Trivial. The specific realization was that this ownership
kind was an unnecessary conflation of the a type system idea (triviality) with
an ownership idea (@any, an ownership kind that is compatible with any other
ownership kind at value merge points and can only create). This caused the
ownership model to have to contort to handle the non-payloaded or trivial cases
of non-trivial enums. This is unnecessary if we just eliminate the any case and
in the verifier separately verify that trivial => @any (notice that we do not
verify that @any => trivial).
NOTE: This is technically an NFC intended change since I am just replacing
Trivial with Any. That is why if you look at the tests you will see that I
actually did not need to update anything except removing some @trivial ownership
since @any ownership is represented without writing @any in the parsed sil.
rdar://46294760
Dynamic replacements are currently written in extensions as
extension ExtendedType {
@_dynamicReplacement(for: replacedFun())
func replacement() { }
}
The runtime implementation allows an implementation in the future where
dynamic replacements are gather in a scope and can be dynamically
enabled and disabled.
For example:
dynamic_extension_scope CollectionOfReplacements {
extension ExtentedType {
func replacedFun() {}
}
extension ExtentedType2 {
func replacedFun() {}
}
}
CollectionOfReplacements.enable()
CollectionOfReplacements.disable()
To make that work, enter appropriate scopes (ArgumentScopes and
FormalEvaluationScopes) at a bunch of places. But note that l-value
emission generally can't enter such a scope, so in generic routines
like emitOpenExistentialExpr we have to just assert that we're
already in a scope.
Instead of using composeInput(), build a tuple type containing
the element types only, dropping ownership qualifiers and
asserting that there are no inout or vararg elements.
This is correct because we already promote +0 values to +1, or
clean up +1 values that are only used as +0 as needed.
Fixes <rdar://problem/44915136>.
The constraint solver support for the Swift 3 function type behavior
has been removed, so it's no longer possible to pun the same value as
both a function taking multiple parameters and a function taking a
single tuple argument.
This means the entire parameter list is no longer a target for
substitution as a single value, so the most general form of a function
value passes each parameter indirectly instead of passing a single
tuple parameter indirectly.
Now that we handle the top-level parameter list specially, we never
end up with non-materializable tuples here, or other odd cases like
one-element tuples, so a bunch of complexity can be eliminated.
Treat the top level of the parameter list specially, instead of
looking at FunctionType::getInput().
There are three cases where we don't translate parameters 1-1:
- Imploding the parameter list into a single value when reabstracting
a function with the opaque abstraction pattern. This will go away
once Swift 3 compatibility is fully removed.
- Exploding the parameter list from a single value. This is the dual of
the above.
- Exploding the parameter list for an SE-0110 "tuple splat" conversion.
This is the one case that will need to be supported on an on-going
basis, but it is not too bad to handle directly.
This eliminates the only usage of AbstractionPattern::dropLastTupleElement().
For now, we keep the argument tuple-based entry points in TransformArguments
as well, because they're used by re-abstraction thunks. Those require a bit more
refactoring.
Most of this patch is just removing special cases for materializeForSet
or other fairly mechanical replacements. Unfortunately, the rest is
still a fairly big change, and not one that can be easily split apart
because of the quite reasonable reliance on metaprogramming throughout
the compiler. And, of course, there are a bunch of test updates that
have to be sync'ed with the actual change to code-generation.
This is SR-7134.
This is a longstanding gap in the implementation that quite possibly
never bit anyone in the wild. I've only gotten around to implementing
it now because this same code path will be used to reabstract inout
yields. That means that, rather than only affecting functions with
an abstraction difference in an inout parameter type, this can affect
all storage with any abstraction difference in the stored value type.
So it's time to fill in this gap.
- getAsDeclOrDeclExtensionContext -> getAsDecl
This is basically the same as a dyn_cast, so it should use a 'getAs'
name like TypeBase does.
- getAsNominalTypeOrNominalTypeExtensionContext -> getSelfNominalTypeDecl
- getAsClassOrClassExtensionContext -> getSelfClassDecl
- getAsEnumOrEnumExtensionContext -> getSelfEnumDecl
- getAsStructOrStructExtensionContext -> getSelfStructDecl
- getAsProtocolOrProtocolExtensionContext -> getSelfProtocolDecl
- getAsTypeOrTypeExtensionContext -> getSelfTypeDecl (private)
These do /not/ return some form of 'this'; instead, they get the
extended types when 'this' is an extension. They started off life with
'is' names, which makes sense, but changed to this at some point. The
names I went with match up with getSelfInterfaceType and
getSelfTypeInContext, even though strictly speaking they're closer to
what getDeclaredInterfaceType does. But it didn't seem right to claim
that an extension "declares" the ClassDecl here.
- getAsProtocolExtensionContext -> getExtendedProtocolDecl
Like the above, this didn't return the ExtensionDecl; it returned its
extended type.
This entire commit is a mechanical change: find-and-replace, followed
by manual reformatted but no code changes.
ConvertFunction and reabstraction thunks need this attribute. Otherwise,
there is no way to identify that withoutActuallyEscaping was used
to explicitly perform a conversion.
The destination of a [without_actually_escaping] conversion always has
an escaping function type. The source may have either an escaping or
@noescape function type. The conversion itself may be a nop, and there
is nothing distinctive about it. The thing that is special about these
conversions is that the source function type may have unboxed
captures. i.e. they have @inout_aliasable parameters. Exclusivity
requires that the compiler enforce a SIL data flow invariant that
nonescaping closures with unboxed captures can never be stored or
passed as an @escaping function argument. Adding this attribute allows
the compiler to enforce the invariant in general with an escape hatch
for withoutActuallyEscaping.
1) It's possible to materialize a tuple value with an @escaping or
@autoclosure element in it.
I don't think this causes any bad behavior in 4.2 because these
flags have no semantic effect after the type checker, but now
I'm adding an assertion that will fire when such types are
serialized, so let's make sure it doesn't happen by explicitly
clearing out these flags when lowering tuples types.
2) It's also possible to materialize a tuple with a single vararg
element. Again, this was not a problem in 4.2, but with the above
change to start clearing tuple flags, we now end up in a
situation where the lowered type is not a tuple, because
TupleType::get() returns a ParenType if the tuple has one
element that is not vararg (which it no longer is, because we
just cleared all the flags).
Fix the second problem by treating one-element vararg tuples just
like tuples with inout, __shared and __owned elements, that is,
by always exploding them when they appear at the top level of a
function parameter list, ensuring we never try to materialize
a value whose type is the entire tuple type.
These problems all stem from the fact that lowering a function type
with the opaque abstraction pattern treats the top level argument
list as a single tuple argument. Once that is fixed, much of the
above will simplify down to assertions.
Even with an opaque abstraction pattern, we must explode a
parameter list containing __shared and __owned elements.
Otherwise, we produce invalid lowered SIL types.
Note that this is already an issue, because the stdlib has a
handful of declarations using __owned.