Certain dynamic casts cannot work safely with isolated conformances,
regardless of what executor the code runs on. For such cases, reject
all attempts to conform to the type.
Replace the pair of global actor type/conformance we are passing around with
a general "conformance execution context" that could grow new functionality
over time. Add three external symbols to the runtime:
* swift_conformsToProtocolWithExecutionContext: a conforms-to-protocol check
that also captures the execution context that should be checked before
using the conformance for anything. The only execution context right now
is for an isolated conformance.
* swift_isInConformanceExecutionContext: checks whether the function is
being executed in the given execution context, i.e., running on the
executor for the given global actor.
* swift_ConformanceExecutionContextSize: the size of the conformance
execution context. Client code outside of the Swift runtime can allocate
a pointer-aligned region of memory of this size to use with the runtime
functions above.
In the prior implementation of runtime resolution of isolated conformances,
the runtime had to look in both the protocol conformance descriptor and
in all conditional conformance requirements (recursively) to find any
isolated conformances. If it found one, it had to demangle the global
actor type to metadata. Since swift_conformsToProtocol is a hot path through
the runtime, we can't afford this non-constant-time work in the common
case.
Instead, cache the resolved global actor and witness table as part of the
conformance cache, so that we have access to this information every time
we look up a witness table for a conformance. Propagate this up through
various callers (e.g., generic requirement checking) to the point where
we either stash it in the cache or check it at runtime. This gets us down
to a very quick check (basically, NULL-or-not) for nonisolated conformances,
and just one check for isolated conformances.
When a C++ foreign reference type is conformed to a Swift protocol via a Swift extension, trying to cast `any MyProtocol` to the foreign reference type crashes the runtime.
This was because `selectCasterForDest` wasn't handling C++ foreign reference types, and we were hitting `swift_unreachable`.
This change makes sure the runtime doesn't crash for such casts.
Notably, Swift doesn't have enough metadata to determine if the conditional cast actually succeeded. This is also a problem for CF types. Casting CF types in a similar fashion triggers a typechecker diagnostic. That diagnostic will be amended in a follow-up patch to also trigger for foreign reference types.
rdar://141227849
The way that we include COMPATIBILITY_OVERRIDE_INCLUDE_PATH freaks out the
syntax highlighting of editors like emacs. It causes the whole file to be
highlighted like it is part of the include string.
To work around this, this patch creates a separate file called
CompatibilityOverrideIncludePath.h that just includes
COMPATIBILITY_OVERRIDE_INCLUDE_PATH. So its syntax highlighting is borked, but
at least in the actual files that contain real code, the syntax highlighting is
restored.
Track the key argument index separately from the generic parameter index when performing the invertible protocol checking in _checkGenericRequirements. This keeps the indexing correct when a non-key argument is followed by a key argument.
rdar://128774651
Introduce metadata and runtime support for describing conformances to
"suppressible" protocols such as `Copyable`. The metadata changes occur
in several different places:
* Context descriptors gain a flag bit to indicate when the type itself has
suppressed one or more suppressible protocols (e.g., it is `~Copyable`).
When the bit is set, the context will have a trailing
`SuppressibleProtocolSet`, a 16-bit bitfield that records one bit for
each suppressed protocol. Types with no suppressed conformances will
leave the bit unset (so the metadata is unchanged), and older runtimes
don't look at the bit, so they will ignore the extra data.
* Generic context descriptors gain a flag bit to indicate when the type
has conditional conformances to suppressible protocols. When set,
there will be trailing metadata containing another
`SuppressibleProtocolSet` (a subset of the one in the main context
descriptor) indicating which suppressible protocols have conditional
conformances, followed by the actual lists of generic requirements
for each of the conditional conformances. Again, if there are no
conditional conformances to suppressible protocols, the bit won't be
set. Old runtimes ignore the bit and any trailing metadata.
* Generic requirements get a new "kind", which provides an ignored
protocol set (another `SuppressibleProtocolSet`) stating which
suppressible protocols should *not* be checked for the subject type
of the generic requirement. For example, this encodes a requirement
like `T: ~Copyable`. These generic requirements can occur anywhere
that there is a generic requirement list, e.g., conditional
conformances and extended existentials. Older runtimes handle unknown
generic requirement kinds by stating that the requirement isn't
satisfied.
Extend the runtime to perform checking of the suppressible
conformances on generic arguments as part of checking generic
requirements. This checking follows the defaults of the language, which
is that every generic argument must conform to each of the suppressible
protocols unless there is an explicit generic requirement that states
which suppressible protocols to ignore. Thus, a generic parameter list
`<T, Y where T: ~Escapable>` will check that `T` is `Copyable` but
not that it is `Escapable`, and check that `U` is both `Copyable` and
`Escapable`. To implement this, we collect the ignored protocol sets
from these suppressed requirements while processing the generic
requirements, then check all of the generic arguments against any
conformances not suppressed.
Answering the actual question "does `X` conform to `Copyable`?" (for
any suppressible protocol) looks at the context descriptor metadata to
answer the question, e.g.,
1. If there is no "suppressed protocol set", then the type conforms.
This covers types that haven't suppressed any conformances, including
all types that predate noncopyable generics.
2. If the suppressed protocol set doesn't contain `Copyable`, then the
type conforms.
3. If the type is generic and has a conditional conformance to
`Copyable`, evaluate the generic requirements for that conditional
conformance to answer whether it conforms.
The procedure above handles the bits of a `SuppressibleProtocolSet`
opaquely, with no mapping down to specific protocols. Therefore, the
same implementation will work even with future suppressible protocols,
including back deployment.
The end result of this is that we can dynamically evaluate conditional
conformances to protocols that depend on conformances to suppressible
protocols.
Implements rdar://123466649.
The static bridgeableProtocol inherits the ptrauth_struct attribute which uses the B key, and that's not allowed on global data in the shared cache. Pass the value directly as a parameter instead.
Ensure that context descriptor pointers are signed in the runtime by putting the ptrauth_struct attribute on the types.
We use the new __builtin_ptrauth_struct_key/disc to conditionally apply ptrauth_struct to TrailingObjects based on the signing of the base type, so that pointers to TrailingObjects get signed when used with a context descriptor pointer.
We add new runtime entrypoints that take signed pointers where appropriate, and have the compiler emit calls to the new entrypoints when targeting a sufficiently new OS.
rdar://111480914
This PR changes the casting machinery to avoid casting `__SwiftValue` boxes
directly. This forces the caster to instead unwrap `__SwiftValue` boxes and
retry with the inner content. This results in boxed values being cast like the
inner content.
This fixes the behavior in situations like the following:
```
let t = ...
let s = t as Any as! AnyObject
// `s` is now a `__SwiftValue` box
// Next line should be true iff t conforms to NSCopying
// Prior to this change, it always succeeds
s is NSCopying
```
After this change, the above cast succeeds only if `t` actually
conforms to `NSCopying`.
This is a follow-on to PR#37683.
Related to: SR-14635
The fix here is two-fold:
1) Teach SILGen that it cannot use the scalar casting paths for extended existentials
2) Teach the runtime casting entrypoint to unwrap as much metatype structure as possible
before arriving at a 'Self' type bound for the requirement checking paths.
The code here mirrors the destructuring check we're doing in remote mirrors.
rdar://95166916
Implement casting to and from extended existentials. This is done by slightly generalizing the conditional conformances checking infrastructure.
Unfortunately, casts for reference types and metatypes are unsound because IRGen is peepholing all non-opaque existential conversions with a helper. I’ll disable that in a follow-up.
rdar://92197049
* Refactor Bincompat
Organize everything around internal functions that test for
a particular OS version.
Correctly handle cases where we don't know the version of the app.
Make all bincompat functions consistently return `true` for the
legacy semantics, `false` for new semantics. Consistently name
them all to reflect this.
* Conditionalize the support for SR-14635
SR-14635 pointed out a hole in the updated dynamic casting logic
that allowed certain casts that should have been illegal.
In particular, when casting certain types to Obj-C protocols,
the Swift value gets boxed; we would permit the cast to succeed
whenever the resulting box satisfied the protocol. For example,
this allowed any Swift value to be cast to `NSCopying` regardless of
whether or not it implemented the required `copy(with:)` method.
This was fixed in #37683 to reject such casts but of course some folks were
depending on this behavior to pass Swift data into Obj-C functions.
(The properly supported approach for passing arbitrary Swift data into
Obj-C functions is to cast the Swift value to `AnyObject`.)
This change makes that new behavior conditional. For now,
the legacy semantics are enabled on Apple platforms and the
new semantics are in use everywhere else. This will allow
us to gradually enable enforcement of the new behavior over
time.
* Just skip this test on Apple platforms, since it is inconsistently implemented there (and is therefore not really testable)
On Windows, std::max_align_t is only 8-byte aligned, but Swift assumes 16-byte alignment. MaximumAlignment is our notion of the maximum alignment of a type, so use that instead.
* SR-14635: Casts to NSCopying should not always succeed
The runtime dynamic casting logic explores a variety of strategies for
each cast request. One of the last options is to wrap the source
in a `__SwiftValue` box so it can bridge to Obj-C. The previous
code was overly aggressive about such boxing; it performed the boxing
for any source type and only checked to verify that the `__SwiftValue`
box itself was compatible with the destination.
Among other oddities, this results in the behavior discussed
in SR-14635, where any Swift or Obj-C type will always successfully cast
to NSCopying because `__SwiftValue` is compatible with NSCopying.
This is actually two subtly different issues:
* Class types should not be subject to `__SwiftValue` boxing at all.
Casting class types to class existentials is already handled elsewhere,
so this function should just reject any source with class type.
* Non-class types should be boxed only when being assigned to
an AnyObject (an "unconstrained class existential"). If
the class existential has constraints, it is by definition
a class-constrained existential which should not receive
any non-class object.
To solve these, this PR disables `__SwiftValue` boxing in two cases:
1. If the source is a class (reference) type.
2. If the destination has constraints
Resolves SR-14635
Resolves rdar://78224322
* Avoid boxing class metatypes on Darwin
But continue boxing
* Non-class metatypes on all platforms
* All metatypes on non-Darwin platforms
Obj-C interop requires that we do not box class metatypes;
those must be usable as simple pointers when passed to Obj-C.
But no other metatype is object-compatible, so we have to
continue boxing everything else.
* Split out ObjC-specific test cases
Take the existing CompatibilityOverride mechanism and generalize it so it can be used in both the runtime and Concurrency libraries. The mechanism is preprocessor-heavy, so this requires some tricks. Use the SWIFT_TARGET_LIBRARY_NAME define to distinguish the libraries, and use a different .def file and mach-o section name accordingly.
We want the global/main executor functions to be a little more flexible. Instead of using the override mechanism, we expose function pointers that can be set by the compatibility library, or by any other code that wants to use a custom implementation.
rdar://73726764
The new cast logic checks and aborts if a non-nullable pointer has a null value
at runtime. However, because this was tolerated by the old casting logic, some
apps inadvertently rely on being able to cast such null references.
This change adds specific checks for null pointers in compatibility mode and
handles them similarly to the previous casting logic:
* Casting to Obj-C or CF type: casting a null pointer succeeds
* Casting to class-constrained existential: casting a null pointer succeeds
* Casting to a Swift class type: cast fails without crashing
* Bridging from Obj-C class to Swift struct type: cast fails without crashing
This also adds a guard to avoid trying to lookup the dynamic type of a null
class reference.
In non-compatibility mode, all of the above cause an immediate runtime crash.
The changes here are only intended to support existing binaries running against
new Swift runtimes.
Resolves rdar://72323929
This restores the earlier behavior of Optionals cast to
AnyHashable, so that [String?:Any] dictionaries cast
to [AnyHashable:Any] can be indexed by plain String
keys.
This is a little problematic because it's not consistent with the
compiler-optimized casts.
But the ability to index such dictionaries by plain String
keys seems important to preserve. SR-9047 will expand
Optional/AnyHashable interoperability so that such
indexing works without this special case.
An earlier proposal would link-check this, changing the behavior
depending on the caller. But it's not really workable to
change the behavior seen by intermediate frameworks depending
on the app they're being called by.
As part of making casting more consistent, the behavior of Optional -> AnyHashable casts
was changed in some cases. This PR provides a hook for re-enabling the old behavior
in certain contexts.
Background: Most of the time, casts from String? to AnyHashable get optimized
to just injects the String? into the AnyHashable, so the following
has long been true and remains true in Swift 5.4:
```
let s = "abc"
let o: String? = s
// Next test is true because s is promoted to Optional<String>
print(s == o)
// Next test is false: Optional<String> and String are different types
print(s as AnyHashable == o as AnyHashable)
```
But when casts ended up going through the runtime, Swift 5.3 behaved
differently, as you could see by casting a dictionary with `String?` keys (in
the generic array code, key and value casts always use the runtime logic). In
the following code, both print statements behave differently in Swift 5.4 than
before:
```
let a: [String?:String] = ["Foo":"Bar"]
let b = a as [AnyHashable:Any]
print(b["Foo"] == "Bar") // Works before Swift 5.4
print(b["Foo" as String?] == "Bar") // Works in Swift 5.4 and later
```
Old behavior: The `String?` keys would get unwrapped to `String` before being injected into AnyHashable. This allows the first to work but strangely breaks the second.
New behavior: The `String?` keys do not get unwrapped. This breaks the first but makes the second work.
TODO: The long-term goal, of course, is for `AnyHashable("Foo" as String?)` to
test equal to `AnyHashable("Foo")` (and hash the same, of course). In that
case, all of the tests above will succeed.
Resolves rdar://73301155
Background: We've noticed a lot of problems from Obj-C APIs that returned null
even though they were declared to never do so. These mismatches subvert Swift's
type system and can lead to hard-to-diagnose crashes much later in the program.
This fatal error was introduced into the primary casting function to help catch
such problems closer to the point where they occur so developers could more
easily identify and fix them.
However, there's been some concern about what this means for old binaries, so
we're considering a check here that would allow the old behavior in certain
cases yet to be determined. This PR adds the framework for such a check.
Resolves rdar://72323929
* Dynamic Casting: Properly unwrap existential metatype sources
Existential metatypes are really just existentials that hold metatypes. As
such, they should be handled in the general casting logic in much the same way
as regular existentials: They should generally be ignored by most casting logic,
and unwrapped as necessary at the top level.
In particular, the previous code would fail to correctly handle the following
cast from an existential metatype (`AnyObject.Type`) to an existential
(`AnyObject`):
```
class C {}
let a = C.self as AnyObject.Type
let b = a as! AnyObject
```
With the old code, `b` above would hold a reference to a `__SwiftValue` box
containing the type reference. The correct result would simply store the type
reference directly in `b`. These two are only really distinguishable in that
the correct form permits `a === b` to return `true`.
Fixes rdar://70582753
Note: This is not yet fully supported on Linux. Basically, metatypes on Linux are not currently
fully compatible with reference-counted class pointers, which prevents us from
fully supporting metatype operations on Linux that we support on macOS.
* Dynamic Cast Rework: Runtime
This is a completely refactored version of the core swift_dynamicCast
runtime method.
This fixes a number of bugs, especially in the handling of multiply-wrapped
types such as Optional within Any. The result should be much closer to the
behavior specified by `docs/DynamicCasting.md`.
Most of the type-specific logic is simply copied over from the
earlier implementation, but the overall structure has been changed
to be uniformly recursive. In particular, this provides uniform
handling of Optional, existentials, Any and other common "box"
types along all paths. The consistent structure should also be
easier to update in the future with new general types.
Benchmarking does not show any noticable performance implications.
**Temporarily**, the old implementation is still available. Setting the
environment variable `SWIFT_OLD_DYNAMIC_CAST_RUNTIME` before launching a program
will use the old runtime implementation. This is only to facilitate testing;
once the new implementation is stable, I expect to completely remove the old
implementation.