[ASTPrinter] Escape @_lifetime arguments when needed
Printing a LifetimeDescriptor would never wrap it in backticks (even if originally wrapped in backticks). This would result in the output not being able to be parsed
rdar://159992995
Restructure the inference logic to allow the same function declaration to
independently annotate or infer different lifetime targets.
For example:
@_lifetime(borrow a)
/* DEFAULT: @_lifetime(b: copy b)
func f(a: A, b: inout B) -> NE
The fact that we prevented this was somewhat accidental and surprising.
This restructuring simplifies the code but also gives us much more control over
the inference logic.
Fixes: rdar://159288750 ("A function cannot have a ~Escapable 'inout' parameter
in addition to other ~Escapable parameters" is not actionable)
Clang complains that paramIndicesLength is unused. This block is already
guarded by CONDITIONAL_ASSERT_enabled(), so promoting assert() to
ASSERT() should not affect the behavior of assertions-disabled builds.
While deserializing AST function types FunctionType and GenericFunctionType which include
lifetime dependencies and an implicit self parameter, we don't correctly populate
ASTExtInfoBuilder.lifetimeDependencies. We end up reading one dependency less due to
incorrect index calculation.
Unlike SILFunctionType, AST function types FunctionType and GenericFunctionType
do not include implicit self in their param list. They represent methods with
implicit self as like: `(Self) -> (Args...) -> Result` and don't have any information
to indicate they may have implicit self. Since we use number of parameters while
deserializing lifetime dependencies, we go wrong for such function types.
Serialize the length of parameter indices, so that lifetime dependencies can be
deserialized to that length.
rdar://151768216
`scopeLifetimeParamIndices` sometimes ends up non-null but empty, which causes crashes in module deserialization, blocking cross-module work on nonescapable types.
rdar://147765187
When a generic function has potentially Escapable outputs, those outputs
declare lifetime dependencies, which have no effect when substitution
leads to those types becoming `Escapable` in a concrete context.
This means that type substitution should canonically eliminate lifetime
dependencies targeting Escapable parameters or returns, and that
type checking should allow a function value with potentially-Escapable
lifetime dependencies to bind to a function type without those dependencies
when the target of the dependencies is Escapable.
Fixes rdar://147533059.
Preserve conditionallyAddressableParamIndices independent of any
addressableParamIndices. The conditional dependencies are subject to change
based on type substitution.
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.
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).
@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.
The lifetimebound annotations are now imported as lifetime dependencies.
This works for basic cases but there are still some parts missing:
* Support lifeitmebound annotations on constructors
* A way to represent immortal/static lifetimes on the C++ side
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.
Pitch - https://github.com/apple/swift-evolution/pull/2305
Changes highlights:
dependsOn(paramName) and dependsOn(scoped argName) syntax
dependsOn(paramName) -> copy lifetime dependence for all parameters/self except
when we have Escapable parameters/self, we assign scope
lifetime dependence.
Allow lifetime dependence on parameters without ownership modifier.
Always infer copy lifetime dependence except when we have
Escapable parameters/self, we infer scope lifetime dependence.
Allow lifetime dependence inference on parameters without ownership modifier.
Provide APIs needed by lifetime dependence diagnostics, namely LifetimeDependenceConvention.
Reorganize the APIs so it's easy to find related functionality which
API is responsible for which functionality.
Remove the originalFunctionConvention complexity. It is no longer
needed for lifetime dependence inference, and generally should be
avoided in SIL.
Add some placeholder FIXMEs because this not a good PR in which to
change existing functionality.