This was fix was accidentally not include in the previous commit,
which breaks older .swiftinterface files without it:
commit 75ba7a845c
Merge: befc15e6dfd41c4d4cc9
Author: Andrew Trick <atrick@apple.com>
Date: Wed Mar 19 18:22:35 2025
Merge pull request #80064 from atrick/lifetime-inference
LifetimeDependence: implement strict type checking
Rework the type checker to support completely checking lifetime dependence
requirements. Don't let anything through without the feature being enabled and
complete annotation or inference.
First, prevent lifetime dependencies from sneaking into source that does not
enable LifetimeDependence. This is essential for controlling the scope of the
feature.
Fixing this is disruptive because, ever since `~Escapable` was introduced we
have been declaring empty non-Escapable types without enabling
LifetimeDependence. Such as:
struct Implicit_Init_Nonescapable : ~Escapable {}
Fixes: rdar://145979187 ([nonescapable] diagnose implicit non-Escapable
initializers as an error unless LifetimeDependence is enabled)
Various forms of unsupported 'inout' dependencies are now also caught by the
type checker.
Second, disable lifetime dependency inferrence except in unambiguous cases and
some implicitly generated cases.
Fixes: rdar://131176898 ([nonescapable] missing diagnostic for incorrectly inferred inherited dependence)
This is important to avoid source compatibility problems as inference rules
change. They will change as the proposal goes through review.
This fixes various latent missing dependency bugs.
Disable experimental lifetime dependence inference. Unambiguous lifetime
dependency candidates will still be inferred by default, without any frontend
options. Ambiguous candidates will, however, no longer be inferred unless
-Xfrontend -enable_experimental_lifetime_dependence_inference is enabled.
This all has to be done without breaking existing .swiftinterface files. So
backward compatibility logic is maintained.
Examples of inference rules that are no longer enabled by default:
1. do not infer a dependency on non-Escapable 'self' for methods with more than
zero parameters:
extension NE: ~Escapable {
/*@lifetime(self)*/ // ERROR: 'self' not inferred
func method<Arg>(arg: Arg) -> NE { ... }
}
2. Never infer a 'copy' dependency kind for explicit functions
extension NE: ~Escapable {
@lifetime(self) // ERROR: 'copy' not inferred
func method() -> NE { ... }
@lifetime(self) // ERROR: 'copy' not inferred
var property : NE { /*@lifetime(self: newValue)*/ set { ... } }
}
Parameters of generic type need to be treated as potentially
addressable-for-dependencies, but we don't want callers using the generic
function with concrete types that are known not to be addressable-for-
dependencies to be overconstrained. In SILFunctionType lowering, lower
these dependencies distinctly as conditionally addressable, meaning that
the dependency on an argument depends on whether the concrete type of
that argument is (potentially) addressable-for-dependencies or not.
We use experimental features to let people know that the construct is
subject to change and users should not rely on this unless they are
willing to rewrite the uses of this feature later. However, in compiler
generated code everything should be fair game, we will update the
compiler when these features change. This is a requirement to be able to
turn safe wrapper generation on by default.
Map the lifetime dependencies described in terms of the formal AST-level parameters
to the correct parameter(s) in the lowered SIL function type. There can be 0, 1,
or many SIL parameters per formal parameter because of tuple exploding. Also,
record which dependencies are on addressable parameters (meaning that the dependency
includes not only the value of the parameter, but its specific memory location).
Disable inference diagnostics because the AST output makes implicit initializers
explicit.
Enable parsing the @lifetime declaration syntax to handle explicit annotations
on declarations.
Lifetime dependencies in SIL tests continue to be represented as a type modifier on the target.
As before, they are represented as a LifetimeDependentTypeRepr in the AST.
@lifetime(target: source1, source2...) where target can be any
parameter or 'self'. We cannot have @lifetime attributes with duplicate targets.
Also, update the internal data structures. Previously LifetimeEntry stored
pairwise (target, source) dependencies. Now, LifetimeEntry will store an optional
target descriptor and an array of source descriptors.
Lifetime dependencies will now be represented with @lifetime attribute in the language.
dependsOn is a type modifier and was represented as a LifetimeDependentTypeRepr in the AST.
I am deleting dependsOn syntax parsing support and retaining LifetimeDependentTypeRepr support.
We may want to represent lifetime dependencies in a function type with a type attribute in the future.
If we use a decl attribute instead, then support for LifetimeDependentTypeRepr can be deleted.
We need this at least until we have 'dependsOn(self)' syntax.
When 'self' is nonescapable and the result is 'void', assume that 'self' depends
on a single nonescapable argument.
Create two versions of the following functions:
isConsumedParameter
isGuaranteedParameter
SILParameterInfo::isConsumed
SILParameterInfo::isGuaranteed
SILArgumentConvention::isOwnedConvention
SILArgumentConvention::isGuaranteedConvention
These changes will be needed when we add a new convention for
non-trivial C++ types as the functions will return different answers
depending on whether they are called for the caller or the callee. This
commit doesn't change any functionality.
Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
Allow lifetime depenendence on types that are BitwiseCopyable & Escapable.
This is unsafe in the sense that the compiler will not diagnose any use of the
dependent value outside of the lexcial scope of the source value. But, in
practice, dependence on an UnsafePointer is often needed. In that case, the
programmer should have already taken responsibility for ensuring the lifetime of the
pointer over all dependent uses. Typically, an unsafe pointer is valid for the
duration of a closure. Lifetime dependence prevents the dependent value from
being returned by the closure, so common usage is safe by default.
Typical example:
func decode(_ bufferRef: Span<Int>) { /*...*/ }
extension UnsafeBufferPointer {
// The client must ensure the lifetime of the buffer across the invocation of `body`.
// The client must ensure that no code modifies the buffer during the invocation of `body`.
func withUnsafeSpan<Result>(_ body: (Span<Element>) throws -> Result) rethrows -> Result {
// Construct Span using its internal, unsafe API.
try body(Span(unsafePointer: baseAddress!, count: count))
}
}
func decodeArrayAsUBP(array: [Int]) {
array.withUnsafeBufferPointer { buffer in
buffer.withUnsafeSpan {
decode($0)
}
}
}
In the future, we may add SILGen support for tracking the lexical scope of
BitwiseCopyable values. That would allow them to have the same dependence
behavior as other source values.