diff --git a/docs/ReferenceGuides/LifetimeAnnotation.md b/docs/ReferenceGuides/LifetimeAnnotation.md index 5e399d268b8e3..7ac7b6d8952a2 100644 --- a/docs/ReferenceGuides/LifetimeAnnotation.md +++ b/docs/ReferenceGuides/LifetimeAnnotation.md @@ -18,58 +18,73 @@ The `@lifetime` annotation is enforced both in the body of the function and at e ## Default lifetimes -The Swift 6.2 compiler provided default `@_lifetime` behavior whenever it can do so without ambiguity. Often, despite ambiguity, an "obvious" default exists, but we wanted to introduce defaults slowly after developers have enough experience to inform discussion about them. This document tracks the current state of the implementation as it progresses from the original 6.2 implementation. Corresponding tests are in `test/Sema/lifetime_depend_infer.swift`; searching for "DEFAULT:" highlights the rules defined below... +The Swift 6.2 compiler provided default `@_lifetime` behavior whenever it can do so without ambiguity. Often, despite ambiguity, an obvious default exists, but we wanted to introduce defaults slowly after developers have enough experience to inform discussion about them. This document tracks the current state of the implementation as it progresses from the original 6.2 implementation. Corresponding tests are in `test/Sema/lifetime_depend_infer.swift`; searching for "DEFAULT:" highlights the rules defined below... -### Single parameter default rule +### Same-type default lifetime -Given a function or method that returns a non-Escapable result: +Given a function declaration: -- Default to `@_lifetime( a)` for a `~Escapable` result on functions with a single parameter `a`. +`func foo(..., a: A, ...) -> R { ... }` -- Default to `@_lifetime( self)` for a `~Escapable` result on methods with no parameters. +Where `R: ~Escapable`, `A == R`, and `a` is not an `inout` parameter, default to `@_lifetime(copy a)`. +For non-mutating methods, the same rule applies to implicit `Self` parameter. -| Type of parameter | default | -| (`a` or `self`) | lifetime dependency | -| ----------------- | ------------------------------ | -| `Escapable` | `@_lifetime(borrow param)`[^1] | -| `inout Escapable` | `@_lifetime(¶m)`[^1] | -| `~Escapable` | none[^2] | +This handles the obvious cases in which both the parameter and result are `~Escapable`. For example: -[^1]: When the parameter is `BitwiseCopyable`, such as an integer or unsafe pointer, the single parameter default rule applies to function parameters but not to the implicit `self` parameter. Depending on a `BitwiseCopyable` value is a convenience for APIs that construct span-like values from an `UnsafePointer` passed as an argument. This creates a dependency on a local copy of the pointer variable with subtle semantics. User-defined `BitwiseCopyable` structs should generally avoid such subtle lifetime dependencies. If needed, the author of the data type should explicitly opt into them. +```swift +extension Span { + /* DEFAULT: @_lifetime(copy self) */ + func extracting(droppingLast k: Int) -> Self { ... } +} +``` -[^2]: When the single parameter is also `~Escapable`, the result must depend on it, but the dependency may either be scoped (`borrow` or `&`) or it may be copied (`copy`). `copy` is the obvious choice when the parameter and result are the same type, but it is not always correct. Furthermore, a lifetime dependency can only be copied from a generic type when result as the same generic type. This case is therefore handled by same-type default lifetime (discussed below) rather than as a default `@_lifetime` rule. +#### Generic same-type default lifetime -Examples: +The same-type default lifetime rule described above is convenient for nominal types but essential for generic type parameters. -```swift -struct A: Escapable { - let obj: AnyObject // ~BitwiseCopyable -} -struct NE: ~Escapable {...} +Given a generic function declaration: -/* DEFAULT: @_lifetime(borrow a) */ -func oneParam_NEResult(a: A) -> NE +`func foo(..., a: A, ...) -> R { ... }` -/* DEFAULT: @_lifetime(&a) */ -func oneInoutParam_NEResult(a: inout A) -> NE +The same-type default lifetime rule applies to the types in the function declaration's generic context just as it did for nominal types in the previous example. So, again, if type resolution determines `R: ~Escapable` and `A == R`, then `@_lifetime(copy a)` will be default. -extension A /* Self: Escapable */ { - /* DEFAULT: @_lifetime(borrow self) */ - func noParam_NEResult() -> NE +Unlike nominal types, the programmer is not allowed to explicitly declare a lifetime dependency, `@_lifetime(copy +a)`, unless the argument and result types are equivalent (`A == R`). Copying a lifetime dependency from one value to another requires that both values are non-Escapable. Generic types are conditionally non-Escapable (their lifetime dependencies are type-erased), so type equivalence is the only way to ensure that both values are non-Escapable under the same conditions. - /* DEFAULT: @_lifetime(&self) */ - mutating func mutating_noParam_NEResult() -> NE +Here we see how same-type lifetime requirement applies to type substitution and associated types: + +```swift +protocol P { + associatedtype T: ~Escapable +} + +protocol Q { + associatedtype U: ~Escapable +} + +struct S { + /* OK: @_lifetime(copy a) is valid and default */ + func foo(a: A.T) -> B.U where A.T == B.U } ``` -### Implicit initializer and setter defaults +Note that lifetime dependencies are resolved at function declaration time, which determines the function's type. The generic context at the point of function invocation is not considered. For example, the following declaration of `foo` is invalid, because it's argument and results types don't match at the point of declaration, even though the argument and result do have the same type when invoked inside `bar`: -An implicit setter of a `~Escapable` stored property defaults to `@_lifetime(self: copy self, copy newValue)`. This is always correct because the setter simply assigns the stored property to the newValue. Assigning a `~Escapable` variable copies the lifetime dependency. +```swift +struct S { + static func foo(a: T) -> U // ERROR: missing lifetime dependency +} -Similarly, an implicit initializer of a non-Escapable struct defaults to `@_lifetime(self: copy arg)` if all of the initializer arguments are `~Escapable`. This is equivalent to assigning each `~Escapable` stored property. If, however, any initializer arguments are `Escapable`, then no default lifetime is provided unless it is the sole argument, in which case the single parameter rule applies. +/* OK: @_lifetime(copy a) is valid and default */ +func bar(a: T) -> T { + S.foo(a: a) // The same-type rule is satisfied in this context, but 'foo's declaration is invalid. +} +``` ### `inout` parameter default rule +The following `inout` parameter default rule directly follows from the general same-type default rule above. We descrive it as a separate rule here only because the behavior is important and would otherwise be nonobvious: + - Default to `@_lifetime(a: copy a)` for all `inout` parameters where `a` is `~Escapable`. - Default to `@_lifetime(self: copy self)` on `mutating` methods where `self` is `~Escapable`. @@ -122,63 +137,50 @@ extension NE /* Self: ~Escapable */ { } ``` -## Same-type default lifetime (unimplemented) - -Given a function declaration: - -`func foo(..., a: A, ...) -> R { ... }` - -Where `R: ~Escapable` and `A == R`, default to `@_lifetime(copy a)`. -For methods, the same rule applies to implicit `Self` parameter. - -This handles the obvious cases in which both the parameter and result are `~Escapable`. For example: - -```swift -extension Span { - /* DEFAULT: @_lifetime(copy self) */ - func extracting(droppingLast k: Int) -> Self { ... } -} -``` +### Single parameter default rule -### Generic same-type default lifetime (unimplemented) +Given a function or method that returns a non-Escapable result, if that result's dependency does not have a same-type default: -The same-type default lifetime rule described above is convenient for nominal types but essential for generic type parameters. +- Default to `@_lifetime( a)` for a `~Escapable` result on functions with a single parameter `a`. -Given a generic function declaration: +- Default to `@_lifetime( self)` for a `~Escapable` result on methods with no parameters. -`func foo(..., a: A, ...) -> R { ... }` +| Type of parameter | default | +| (`a` or `self`) | lifetime dependency | +| ----------------- | ------------------------------ | +| `Escapable` | `@_lifetime(borrow param)`[^1] | +| `inout Escapable` | `@_lifetime(¶m)`[^1] | +| `~Escapable` | none[^2] | -The same-type default lifetime rule applies to the types in the function declaration's generic context just as it did for nominal types in the previous example. So, again, if type resolution determines `R: ~Escapable` and `A == R`, then `@_lifetime(copy a)` will be default. +[^1]: When the parameter is `BitwiseCopyable`, such as an integer or unsafe pointer, the single parameter default rule applies to function parameters but not to the implicit `self` parameter. Depending on a `BitwiseCopyable` value is a convenience for APIs that construct span-like values from an `UnsafePointer` passed as an argument. This creates a dependency on a local copy of the pointer variable with subtle semantics. User-defined `BitwiseCopyable` structs should generally avoid such subtle lifetime dependencies. If needed, the author of the data type should explicitly opt into them. -Unlike nominal types, the programmer is not allowed to explicitly declare a lifetime dependency, `@_lifetime(copy -a)`, unless the argument and result types are equivalent (`A == R`). Copying a lifetime dependency from one value to another requires that both values are non-Escapable. Generic types are conditionally non-Escapable (their lifetime dependencies are type-erased), so type equivalence is the only way to ensure that both values are non-Escapable under the same conditions. +[^2]: When the single parameter is also `~Escapable`, the result must depend on it, but the dependency may either be scoped (`borrow` or `&`) or it may be copied (`copy`). `copy` is the obvious choice when the parameter and result are the same type, but it is not always correct. Furthermore, a lifetime dependency can only be copied from a generic type when result as the same generic type. This case is therefore handled by same-type default lifetime (discussed below) rather than as a default `@_lifetime` rule. -Here we see how same-type lifetime requirement applies to type substitution and associated types: +Examples: ```swift -protocol P { - associatedtype T: ~Escapable +struct A: Escapable { + let obj: AnyObject // ~BitwiseCopyable } +struct NE: ~Escapable {...} -protocol Q { - associatedtype U: ~Escapable -} +/* DEFAULT: @_lifetime(borrow a) */ +func oneParam_NEResult(a: A) -> NE -struct S { - /* OK: @_lifetime(copy a) is valid and default */ - func foo(a: A.T) -> B.U where A.T == B.U +/* DEFAULT: @_lifetime(&a) */ +func oneInoutParam_NEResult(a: inout A) -> NE + +extension A /* Self: Escapable */ { + /* DEFAULT: @_lifetime(borrow self) */ + func noParam_NEResult() -> NE + + /* DEFAULT: @_lifetime(&self) */ + mutating func mutating_noParam_NEResult() -> NE } ``` -Note that lifetime dependencies are resolved at function declaration time, which determines the function's type. The generic context at the point of function invocation is not considered. For example, the following declaration of `foo` is invalid, because it's argument and results types don't match at the point of declaraion, even though the argument and result do have the same type when invoked inside `bar`: +### Implicit initializer and setter defaults -```swift -struct S { - static func foo(a: T) -> U // ERROR: missing lifetime dependency -} +An implicit setter of a `~Escapable` stored property defaults to `@_lifetime(self: copy self, copy newValue)`. This is always correct because the setter simply assigns the stored property to the newValue. Assigning a `~Escapable` variable copies the lifetime dependency. -/* OK: @_lifetime(copy a) is valid and default */ -func bar(a: T) -> T { - S.foo(a: a) // The same-type rule is satisfied in this context, but 'foo's declaration is invalid. -} -``` +Similarly, an implicit initializer of a non-Escapable struct defaults to `@_lifetime(self: copy arg)` if all of the initializer arguments are `~Escapable`. This is equivalent to assigning each `~Escapable` stored property. If, however, any initializer arguments are `Escapable`, then no default lifetime is provided unless it is the sole argument, in which case the single parameter rule applies. diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index b76d437aa3090..cd20fd6f9d882 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -1084,6 +1084,8 @@ class LifetimeDependenceChecker { // Infer non-Escapable results. if (isDiagnosedNonEscapable(getResultOrYield())) { + inferNonEscapableResultOnSameTypeParam(); + if (isInit() && isImplicitOrSIL()) { inferImplicitInit(); } else if (hasImplicitSelfParam()) { @@ -1105,6 +1107,45 @@ class LifetimeDependenceChecker { inferInoutParams(); } + // Infer a dependency to the ~Escapable result from all parameters of the same + // type. + void inferNonEscapableResultOnSameTypeParam() { + // The function declaration's substituted result type. + CanType resultTy = getResultOrYield()->getCanonicalType(); + + TargetDeps *targetDeps = depBuilder.getInferredTargetDeps(resultIndex); + if (!targetDeps) + return; + + // Ignore mutating self. An 'inout' modifier effectively makes the parameter + // a different type for lifetime inference. + if (hasImplicitSelfParam() && !afd->getImplicitSelfDecl()->isInOut()) { + Type selfTy = afd->mapTypeIntoEnvironment(dc->getSelfInterfaceType()); + if (selfTy->getCanonicalType() == resultTy) { + targetDeps->inheritIndices.set(selfIndex); + } + } + + unsigned paramIndex = 0; + for (auto *param : *afd->getParameters()) { + SWIFT_DEFER { paramIndex++; }; + + // Ignore 'inout' parameters. An 'inout' modifier effectively makes the + // parameter a different type for lifetime inference. An 'inout' parameter + // defaults to being the source and target of a self-dependency, as + // covered by the 'inout' rule. + if (param->isInOut()) + continue; + + CanType paramTy = afd->mapTypeIntoEnvironment( + param->getInterfaceType())->getCanonicalType(); + if (paramTy != resultTy) + continue; + + targetDeps->inheritIndices.set(paramIndex); + } + } + // Infer dependence for an accessor whose non-escapable result depends on // self. This includes _read and _modify. // @@ -1117,7 +1158,8 @@ class LifetimeDependenceChecker { bool nonEscapableSelf = isDiagnosedNonEscapable(dc->getSelfTypeInContext()); if (nonEscapableSelf && accessor->getImplicitSelfDecl()->isInOut()) { // First, infer the dependency of the inout non-Escapable 'self'. This may - // result in two inferred dependencies for accessors. + // result in two inferred dependencies for accessors (one targetting + // selfIndex here, and one targetting resultIndex below). inferMutatingAccessor(accessor); } // Handle synthesized wrappers... @@ -1255,6 +1297,9 @@ class LifetimeDependenceChecker { if (!resultDeps) return; // .sil implicit initializers may have been annotated. + if (!resultDeps->empty()) + return; // same-type inferrence applied; don't issue diagnostics. + unsigned paramIndex = 0; for (auto *param : *afd->getParameters()) { SWIFT_DEFER { paramIndex++; }; @@ -1293,6 +1338,9 @@ class LifetimeDependenceChecker { if (!resultDeps) return; + if (!resultDeps->empty()) + return; // same-type inferrence applied; don't issue diagnostics. + bool nonEscapableSelf = isDiagnosedNonEscapable(dc->getSelfTypeInContext()); // Do not infer the result's dependence when the method is mutating and // 'self' is non-Escapable. Independently, a missing dependence on inout @@ -1359,6 +1407,9 @@ class LifetimeDependenceChecker { if (!resultDeps) return; + if (!resultDeps->empty()) + return; // same-type inferrence applied; don't issue diagnostics. + // Strict inference only handles a single escapable parameter, // which is an unambiguous borrow dependence. if (afd->getParameters()->size() == 0) { diff --git a/test/Parse/lifetime_attr.swift b/test/Parse/lifetime_attr.swift index d679b0c4b6a32..218dc8b5bbe70 100644 --- a/test/Parse/lifetime_attr.swift +++ b/test/Parse/lifetime_attr.swift @@ -20,12 +20,12 @@ func derive(_ ne1: NE, _ ne2: NE) -> NE { } @_lifetime // expected-error{{expected '(' after lifetime dependence specifier}} -func testMissingLParenError(_ ne: NE) -> NE { // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} +func testMissingLParenError(_ ne: NE) -> NE { ne } @_lifetime() // expected-error{{expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier}} -func testMissingDependence(_ ne: NE) -> NE { // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} +func testMissingDependence(_ ne: NE) -> NE { ne } diff --git a/test/Sema/lifetime_depend_infer.swift b/test/Sema/lifetime_depend_infer.swift index 5b0865bbd2c9b..964afc129c929 100644 --- a/test/Sema/lifetime_depend_infer.swift +++ b/test/Sema/lifetime_depend_infer.swift @@ -1,7 +1,9 @@ // RUN: %target-typecheck-verify-swift \ -// RUN: -enable-experimental-feature Lifetimes +// RUN: -enable-experimental-feature Lifetimes \ +// RUN: -enable-experimental-feature SuppressedAssociatedTypes // REQUIRES: swift_feature_Lifetimes +// REQUIRES: swift_feature_SuppressedAssociatedTypes // Coverage testing for LifetimeDependence inferrence logic. The tests are sorted and grouped according to // docs/ReferenceGuides/LifetimeAnnotation.md. To find the cases that cover the default lifetime @@ -21,6 +23,129 @@ struct NEImmortal: ~Escapable { struct MutNE: ~Copyable & ~Escapable {} +// ============================================================================= +// Same-type default rule +// ============================================================================= + +func sameTypeParam(ne: NE) -> NE { ne } + +func sameTypeConsumingParam(ne: consuming NE) -> NE { ne } + +func sameTypeBorrowingParam(ne: borrowing NE) -> NE { ne } + +func sameTypeInoutParam(ne: inout NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} + +func sameTypeParam_sameTypeParam(ne1: NE, ne2: NE) -> NE { ne1 } + +func sameTypeParam_otherTypeParam(ne: NE, c: C) -> NE { ne } + +func sameTypeParam_sameTypeInoutParam(ne: NE, mutNE: inout NE) -> NE { ne } + +struct NonEscapable: ~Escapable { + @_lifetime(immortal) + init() {} + + init(ne: Self) {} + + init(c: C) {} // expected-error{{cannot borrow the lifetime of 'c', which has consuming ownership on an initializer}} + + init(ne: Self, c: C) {} + + init(ne: Self, u: U) {} + + init(neNominal: NonEscapable, u: U) {} + + init(other: NonEscapable) {} // expected-error{{cannot infer the lifetime dependence scope on an initializer with a ~Escapable parameter, specify '@_lifetime(borrow other)' or '@_lifetime(copy other)'}} + + func sameTypeSelf_noParam() -> Self { self } + + consuming func sameTypeConsumingSelf_noParam() -> Self { self } + + borrowing func sameTypeBorrowingSelf_noParam() -> Self { self } + + mutating func sameTypeMutatingSelf() -> Self { self } // expected-error{{a mutating method with a ~Escapable result requires '@_lifetime(...)'}} + + func sameTypeSelf_sameTypeParam(ne: Self) -> Self { self } + + func sameTypeSelf_otherTypeParam(c: C) -> Self { self } + + func sameTypeSelf_sameTypeInoutParam(mutNE: inout Self) -> Self { self } + + func sameArchetypeSelf(u: U) -> Self { self } + + func sameNominalTypeSelf_genericParam(u: U) -> NonEscapable { self } + + func otherNominalTypeSelf_genericParam(u: U) -> NonEscapable { NonEscapable() } // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)'}} +} + +protocol NonEscapableProtocol: ~Escapable { + @_lifetime(immortal) + static func create() -> Self + + init(ne: Self) + + init(ne: Self, u: U) + + init(ne: some NonEscapableProtocol, c: C) // expected-error{{an initializer with a ~Escapable result requires '@_lifetime(...)'}} + + init(neProtocol: any NonEscapableProtocol, c: C) // expected-error{{an initializer with a ~Escapable result requires '@_lifetime(...)'}} + + func sameArchetypeSelf() -> Self + + mutating func sameArchetypeMutatingSelf() -> Self // expected-error{{a mutating method with a ~Escapable result requires '@_lifetime(...)'}} +} + +extension NonEscapableProtocol where Self: ~Escapable { + func sameArchetypeSelf() -> Self { + Self.create() + } +} + +func sameGenericTypeParam(ne: T) -> T { + ne +} + +func otherGenericTypeParam( + ne: T, utype: U.Type) -> U { // expected-error{{a function with a ~Escapable result requires '@_lifetime(...)'}} + U.create() +} + +protocol P_NE { + associatedtype T: ~Escapable +} + +protocol Q_NE { + associatedtype U: NonEscapableProtocol & ~Escapable +} + +struct AssociatedNE { + func otherAssociatedTypeParam(a: P.T, b: P.T) -> Q.U { // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)'}} + Q.U.create() + } +} + +extension AssociatedNE where P.T == Q.U { + /* OK: @_lifetime(copy a) is valid and default */ + func sameAssociatedTypeParam(a: P.T) -> Q.U { + // Note: the same-type rule is satisfied in this context, but the declaration of 'otherAssociatedTypeParam' + // is itself invalid. + otherAssociatedTypeParam(a: a, b: a) + } +} + +protocol QQ_NE { + associatedtype U: NonEscapableProtocol & ~Escapable +} + +struct AssociatedQ {} + +extension AssociatedQ where Q == QQ { + static func sameAssociatedTypeParam(a: Q.U) -> QQ.U { + QQ.U.create() + } +} + // ============================================================================= // Single parameter default rule for functions // ============================================================================= @@ -71,22 +196,22 @@ func twoParamsBorrow_NEResult(c: borrowing C, _: Int) -> NEImmortal { NEImmortal func twoParamsInout_NEResult(c: inout C, _: Int) -> NEImmortal { NEImmortal() } // expected-error{{a function with a ~Escapable result requires '@_lifetime(...)'}} -func neParam_NEResult(ne: NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} +func neParam_NEResult(ne: NE) -> NE { ne } @_lifetime(ne) // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} func neParamLifetime_NEResult(ne: NE) -> NE { ne } -func neParamBorrow_NEResult(ne: borrowing NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} +func neParamBorrow_NEResult(ne: borrowing NE) -> NE { ne } @_lifetime(ne) // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} func neParamBorrowLifetime_NEResult(ne: borrowing NE) -> NE { ne } -func neParamConsume_NEResult(ne: consuming NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} +func neParamConsume_NEResult(ne: consuming NE) -> NE { ne } @_lifetime(ne) // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} func neParamConsumeLifetime_NEResult(ne: consuming NE) -> NE { ne } -func neParam_IntParam_NEResult(ne: NE, _:Int) -> NE { ne } // expected-error{{a function with a ~Escapable result requires '@_lifetime(...)'}} +func neParam_IntParam_NEResult(ne: NE, _:Int) -> NE { ne } func inoutParam_inoutParam_NEResult(a: inout C, b: inout C) -> NEImmortal { NEImmortal() } // expected-error@-1{{a function with a ~Escapable result requires '@_lifetime(...)'}} @@ -201,50 +326,50 @@ struct EscapableTrivialSelf { mutating func mutating_oneParamBorrow_NEResult(_: Int) -> NEImmortal { NEImmortal() } } -struct NonEscapableSelf: ~Escapable { - func noParam_NEResult() -> NonEscapableSelf { self } // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@_lifetime(borrow self)' or '@_lifetime(copy self)'}} +extension NonEscapable /* where Self: ~Escapable */ { + func noParam_NEResult() -> NonEscapable { self } @_lifetime(self) // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@_lifetime(borrow self)' or '@_lifetime(copy self)'}} - func noParamLifetime_NEResult() -> NonEscapableSelf { self } + func noParamLifetime_NEResult() -> NonEscapable { self } @_lifetime(copy self) // OK - func noParamCopy_NEResult_NEResult() -> NonEscapableSelf { self } + func noParamCopy_NEResult_NEResult() -> NonEscapable { self } @_lifetime(borrow self) // OK - func noParamBorrow_NEResult() -> NonEscapableSelf { self } + func noParamBorrow_NEResult() -> NonEscapable { self } - mutating func mutating_noParam_NEResult() -> NonEscapableSelf { self } // expected-error{{a mutating method with a ~Escapable result requires '@_lifetime(...)'}} + mutating func mutating_noParam_NEResult() -> NonEscapable { self } // expected-error{{a mutating method with a ~Escapable result requires '@_lifetime(...)'}} @_lifetime(self) // expected-error{{cannot infer the lifetime dependence scope on a mutating method with a ~Escapable parameter, specify '@_lifetime(borrow self)' or '@_lifetime(copy self)'}} - mutating func mutating_noParamLifetime_NEResult() -> NonEscapableSelf { self } + mutating func mutating_noParamLifetime_NEResult() -> NonEscapable { self } @_lifetime(copy self) // OK - mutating func mutating_noParamCopy_NEResult() -> NonEscapableSelf { self } + mutating func mutating_noParamCopy_NEResult() -> NonEscapable { self } @_lifetime(&self) // OK - mutating func mutating_noParamBorrow_NEResult() -> NonEscapableSelf { self } + mutating func mutating_noParamBorrow_NEResult() -> NonEscapable { self } - func oneParam_NEResult(_: Int) -> NonEscapableSelf { self } // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)'}} + func oneParam_NEResult(_: Int) -> NonEscapable { self } @_lifetime(self) // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@_lifetime(borrow self)' or '@_lifetime(copy self)'}} - func oneParamLifetime_NEResult(_: Int) -> NonEscapableSelf { self } + func oneParamLifetime_NEResult(_: Int) -> NonEscapable { self } @_lifetime(copy self) // OK - func oneParamCopy_NEResult(_: Int) -> NonEscapableSelf { self } + func oneParamCopy_NEResult(_: Int) -> NonEscapable { self } @_lifetime(borrow self) // OK - func oneParamBorrow_NEResult(_: Int) -> NonEscapableSelf { self } + func oneParamBorrow_NEResult(_: Int) -> NonEscapable { self } - mutating func mutating_oneParam_NEResult(_: Int) -> NonEscapableSelf { self } // expected-error{{a mutating method with a ~Escapable result requires '@_lifetime(...)'}} + mutating func mutating_oneParam_NEResult(_: Int) -> NonEscapable { self } // expected-error{{a mutating method with a ~Escapable result requires '@_lifetime(...)'}} @_lifetime(self) // expected-error{{cannot infer the lifetime dependence scope on a mutating method with a ~Escapable parameter, specify '@_lifetime(borrow self)' or '@_lifetime(copy self)'}} - mutating func mutating_oneParamLifetime_NEResult(_: Int) -> NonEscapableSelf { self } + mutating func mutating_oneParamLifetime_NEResult(_: Int) -> NonEscapable { self } @_lifetime(copy self) // OK - mutating func mutating_oneParamCopy_NEResult(_: Int) -> NonEscapableSelf { self } + mutating func mutating_oneParamCopy_NEResult(_: Int) -> NonEscapable { self } @_lifetime(&self) // OK - mutating func mutating_oneParamBorrow_NEResult(_: Int) -> NonEscapableSelf { self } + mutating func mutating_oneParamBorrow_NEResult(_: Int) -> NonEscapable { self } mutating func mutating_inoutParam_NEResult(a: inout NE) -> NEImmortal { NEImmortal() } // expected-error{{a mutating method with a ~Escapable result requires '@_lifetime(...)'}} } @@ -263,6 +388,7 @@ func inoutNEParam_NEParam_void(_: inout NE, _: NE) {} // OK /* DEFAULT: @_lifetime(1: copy 1) */ func inoutParam_inoutNEParam_void(_: inout NE, _: inout NE) {} // OK + func inoutNEParam_NEResult(ne: inout NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}} /* DEFAULT: @_lifetime(ne: copy ne) */ diff --git a/test/Sema/lifetime_depend_infer_defaults.swift b/test/Sema/lifetime_depend_infer_defaults.swift index e69e351c2debd..ac168761f73f5 100644 --- a/test/Sema/lifetime_depend_infer_defaults.swift +++ b/test/Sema/lifetime_depend_infer_defaults.swift @@ -1,8 +1,12 @@ -// RUN: %target-swift-emit-silgen -enable-experimental-feature Lifetimes -primary-file %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -primary-file %s \ +// RUN: -enable-experimental-feature Lifetimes \ +// RUN: -enable-experimental-feature SuppressedAssociatedTypes \ +// RUN: | %FileCheck %s // // -primary-file is required to synthesize accessors. // REQUIRES: swift_feature_Lifetimes +// REQUIRES: swift_feature_SuppressedAssociatedTypes // Check that all the DEFAULT cases in Sema/lifetime_depend_infer_defaults.swift generate the correct function // signature. Checking the SILGen output seems like the easiest way. @@ -18,6 +22,144 @@ struct NEImmortal: ~Escapable { struct MutNE: ~Copyable & ~Escapable {} +// ============================================================================= +// Same-type default rule +// ============================================================================= + +/* DEFAULT: @_lifetime(copy ne) */ +// CHECK: @$s30lifetime_depend_infer_defaults13sameTypeParam2neAA2NEVAE_tF : $@convention(thin) (@guaranteed NE) -> @lifetime(copy 0) @owned NE +func sameTypeParam(ne: NE) -> NE { ne } + +/* DEFAULT: @_lifetime(copy ne) */ +// CHECK: @$s30lifetime_depend_infer_defaults22sameTypeConsumingParam2neAA2NEVAEn_tF : $@convention(thin) (@owned NE) -> @lifetime(copy 0) @owned NE +func sameTypeConsumingParam(ne: consuming NE) -> NE { ne } + +/* DEFAULT: @_lifetime(copy ne) */ +// CHECK: @$s30lifetime_depend_infer_defaults22sameTypeBorrowingParam2neAA2NEVAE_tF : $@convention(thin) (@guaranteed NE) -> @lifetime(copy 0) @owned NE +func sameTypeBorrowingParam(ne: borrowing NE) -> NE { ne } + +/* DEFAULT: @_lifetime(copy ne, copy ne2) */ +// CHECK: @$s30lifetime_depend_infer_defaults014sameTypeParam_efG03ne13ne2AA2NEVAF_AFtF : $@convention(thin) (@guaranteed NE, @guaranteed NE) -> @lifetime(copy 0, copy 1) @owned NE +func sameTypeParam_sameTypeParam(ne1: NE, ne2: NE) -> NE { ne1 } + +/* DEFAULT: @_lifetime(copy ne) */ +// CHECK: @$s30lifetime_depend_infer_defaults019sameTypeParam_otherfG02ne1cAA2NEVAF_AA1CCtF : $@convention(thin) (@guaranteed NE, @guaranteed C) -> @lifetime(copy 0) @owned NE +func sameTypeParam_otherTypeParam(ne: NE, c: C) -> NE { ne } + +/* DEFAULT: @_lifetime(copy ne) */ +// CHECK: @$s30lifetime_depend_infer_defaults014sameTypeParam_ef5InoutG02ne5mutNEAA0K0VAF_AFztF : $@convention(thin) (@guaranteed NE, @lifetime(copy 1) @inout NE) -> @lifetime(copy 0) @owned NE +func sameTypeParam_sameTypeInoutParam(ne: NE, mutNE: inout NE) -> NE { ne } + +struct NonEscapable: ~Escapable { + @_lifetime(immortal) + init() {} + + /* DEFAULT: @_lifetime(copy ne) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV2neACyxGAE_tcfC : $@convention(method) (@owned NonEscapable, @thin NonEscapable.Type) -> @lifetime(copy 0) @owned NonEscapable + init(ne: Self) {} + + /* DEFAULT: @_lifetime(copy ne) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV2ne1cACyxGAF_AA1CCtcfC : $@convention(method) (@owned NonEscapable, @owned C, @thin NonEscapable.Type) -> @lifetime(copy 0) @owned NonEscapable + init(ne: Self, c: C) {} + + /* DEFAULT: @_lifetime(copy ne) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV2ne1uACyxGAF_qd__tclufC : $@convention(method) (@owned NonEscapable, @in U, @thin NonEscapable.Type) -> @lifetime(copy 0) @owned NonEscapable + init(ne: Self, u: U) {} + + /* DEFAULT: @_lifetime(copy neNominal) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV9neNominal1uACyxGAF_qd__tclufC : $@convention(method) (@owned NonEscapable, @in U, @thin NonEscapable.Type) -> @lifetime(copy 0) @owned NonEscapable + init(neNominal: NonEscapable, u: U) {} + + /* DEFAULT: @_lifetime(copy self) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV20sameTypeSelf_noParamACyxGyF : $@convention(method) (@guaranteed NonEscapable) -> @lifetime(copy 0) @owned NonEscapable + func sameTypeSelf_noParam() -> Self { self } + + /* DEFAULT: @_lifetime(copy self) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV29sameTypeConsumingSelf_noParamACyxGyF : $@convention(method) (@owned NonEscapable) -> @lifetime(copy 0) @owned NonEscapable + consuming func sameTypeConsumingSelf_noParam() -> Self { self } + + /* DEFAULT: @_lifetime(copy self) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV29sameTypeBorrowingSelf_noParamACyxGyF : $@convention(method) (@guaranteed NonEscapable) -> @lifetime(copy 0) @owned NonEscapable + borrowing func sameTypeBorrowingSelf_noParam() -> Self { self } + + /* DEFAULT: @_lifetime(copy self, copy ne) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV013sameTypeSelf_gH5Param2neACyxGAF_tF : $@convention(method) (@guaranteed NonEscapable, @guaranteed NonEscapable) -> @lifetime(copy 0, copy 1) @owned NonEscapable + func sameTypeSelf_sameTypeParam(ne: Self) -> Self { self } + + /* DEFAULT: @_lifetime(copy self) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV018sameTypeSelf_otherH5Param1cACyxGAA1CC_tF : $@convention(method) (@guaranteed C, @guaranteed NonEscapable) -> @lifetime(copy 1) @owned NonEscapable + func sameTypeSelf_otherTypeParam(c: C) -> Self { self } + + /* DEFAULT: @_lifetime(copy self) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV013sameTypeSelf_gH10InoutParam5mutNEACyxGAFz_tF : $@convention(method) (@lifetime(copy 0) @inout NonEscapable, @guaranteed NonEscapable) -> @lifetime(copy 1) @owned NonEscapable + func sameTypeSelf_sameTypeInoutParam(mutNE: inout Self) -> Self { self } + + /* DEFAULT: @_lifetime(copy self) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV17sameArchetypeSelf1uACyxGqd___tlF : $@convention(method) (@in_guaranteed U, @guaranteed NonEscapable) -> @lifetime(copy 1) @owned NonEscapable + func sameArchetypeSelf(u: U) -> Self { self } + + /* DEFAULT: @_lifetime(copy self) */ + // CHECK: @$s30lifetime_depend_infer_defaults12NonEscapableV32sameNominalTypeSelf_genericParam1uACyxGqd___tlF : $@convention(method) (@in_guaranteed U, @guaranteed NonEscapable) -> @lifetime(copy 1) @owned NonEscapable + func sameNominalTypeSelf_genericParam(u: U) -> NonEscapable { self } +} + +protocol NonEscapableProtocol: ~Escapable { + @_lifetime(immortal) + static func create() -> Self + + init(ne: Self) + + init(ne: Self, u: U) + + func sameArchetypeSelf() -> Self +} + +extension NonEscapableProtocol where Self: ~Escapable { + /* DEFAULT: @_lifetime(copy self) */ + // CHECK: @$s30lifetime_depend_infer_defaults20NonEscapableProtocolPAARi0_zrlE17sameArchetypeSelfxyF : $@convention(method) (@in_guaranteed Self) -> @lifetime(copy 0) @out Self + func sameArchetypeSelf() -> Self { + Self.create() + } +} + +/* DEFAULT: @_lifetime(copy ne) */ +// CHECK: @$s30lifetime_depend_infer_defaults20sameGenericTypeParam2nexx_tRi0_zlF : $@convention(thin) (@in_guaranteed T) -> @lifetime(copy 0) @out T +func sameGenericTypeParam(ne: T) -> T { + ne +} + +protocol P_NE { + associatedtype T: ~Escapable +} + +protocol Q_NE { + associatedtype U: NonEscapableProtocol & ~Escapable +} + +struct AssociatedNE {} + +extension AssociatedNE where P.T == Q.U { + /* DEFAULT: @_lifetime(copy a) */ + // CHECK: @$s30lifetime_depend_infer_defaults12AssociatedNEVAA1UQy_1TRtzrlE04sameE9TypeParam1aA2G_tF : $@convention(method) (@in_guaranteed P.T, AssociatedNE) -> @lifetime(copy 0) @out P.T + func sameAssociatedTypeParam(a: P.T) -> Q.U { + Q.U.create() + } +} + +protocol QQ_NE { + associatedtype U: NonEscapableProtocol & ~Escapable +} + +struct AssociatedQ {} + +extension AssociatedQ where Q == QQ { + /* DEFAULT: @_lifetime(copy a) */ + // CHECK: @$s30lifetime_depend_infer_defaults11AssociatedQVA2A5QQ_NERzq_RszrlE04sameE9TypeParam1a1UAaDPQzAI_tFZ : $@convention(method) (@in_guaranteed Q.U, @thin AssociatedQ.Type) -> @lifetime(copy 0) @out Q.U + static func sameAssociatedTypeParam(a: Q.U) -> QQ.U { + QQ.U.create() + } +} + // ============================================================================= // Single parameter default rule for functions // ============================================================================= @@ -112,7 +254,8 @@ struct EscapableTrivialSelf { func inoutNEParam_void(ne: inout NE) {} // OK /* DEFAULT: @_lifetime(0: copy 0) */ -// CHECK: @$s30lifetime_depend_infer_defaults013inoutNEParam_F5_voidyyAA2NEVz_ADtF : $@convention(thin) (@lifetime(copy 0) @inout NE, @guaranteed NE) -> () +// CHECK: @$s30lifetime_depend_infer_defaults013inoutNEParam_F5_voidyyAA2NEVz_ADtF : $@convention(thin) (@lifetime(copy +// 0, copy 1) @inout NE, @guaranteed NE) -> () func inoutNEParam_NEParam_void(_: inout NE, _: NE) {} // OK /* DEFAULT: @_lifetime(0: copy 0) */ diff --git a/test/Sema/lifetime_depend_noattr.swift b/test/Sema/lifetime_depend_noattr.swift index c7e6f9de05a68..da3788883e6b4 100644 --- a/test/Sema/lifetime_depend_noattr.swift +++ b/test/Sema/lifetime_depend_noattr.swift @@ -13,18 +13,18 @@ struct EmptyNonEscapable: ~Escapable {} // OK - no dependence // Don't allow non-Escapable return values. -func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow span)' or '@_lifetime(copy span)'}} +func neReturn(span: RawSpan) -> RawSpan { span } func neInout(span: inout RawSpan) {} // OK - inferred struct S { - func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)}} + func neReturn(span: RawSpan) -> RawSpan { span } func neInout(span: inout RawSpan) {} // OK - inferred } class C { - func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)'}} + func neReturn(span: RawSpan) -> RawSpan { span } func neInout(span: inout RawSpan) {} // OK - inferred }