diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ee3d96a0f69bc..6c43a8aa8fd33 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8337,6 +8337,8 @@ ERROR(lifetime_dependence_duplicate_target, none, "invalid duplicate target lifetime dependencies on function", ()) ERROR(lifetime_parameter_requires_inout, none, "lifetime-dependent parameter '%0' must be 'inout'", (StringRef)) +ERROR(lifetime_target_requires_nonescapable, none, + "invalid lifetime dependence on an Escapable %0", (StringRef)) //------------------------------------------------------------------------------ // MARK: Lifetime Dependence Requirements diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 25ad46ef7b57b..41bcee8114f06 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -186,15 +186,18 @@ void LifetimeDependenceInfo::Profile(llvm::FoldingSetNodeID &ID) const { } } -// Warning: this is incorrect for Setter 'newValue' parameters. It should only -// be called for a Setter's 'self'. -static ValueOwnership getLoweredOwnership(AbstractFunctionDecl *afd) { +static ValueOwnership getLoweredOwnership(ParamDecl *param, + AbstractFunctionDecl *afd) { if (isa(afd)) { return ValueOwnership::Owned; } if (auto *ad = dyn_cast(afd)) { - if (ad->getAccessorKind() == AccessorKind::Set || - isYieldingMutableAccessor(ad->getAccessorKind())) { + if (ad->getAccessorKind() == AccessorKind::Set) { + return param->isSelfParameter() ? ValueOwnership::InOut + : ValueOwnership::Owned; + } + if (isYieldingMutableAccessor(ad->getAccessorKind())) { + assert(param->isSelfParameter()); return ValueOwnership::InOut; } } @@ -224,6 +227,13 @@ static bool isDiagnosedNonEscapable(Type type) { return !type->isEscapable(); } +static bool isDiagnosedEscapable(Type type) { + if (type->hasError()) { + return false; + } + return type->isEscapable(); +} + void LifetimeDependenceInfo::getConcatenatedData( SmallVectorImpl &concatenatedData) const { auto pushData = [&](IndexSubset *paramIndices) { @@ -565,17 +575,25 @@ class LifetimeDependenceChecker { } } - bool isCompatibleWithOwnership(ParsedLifetimeDependenceKind kind, Type type, - ValueOwnership loweredOwnership, + bool isCompatibleWithOwnership(ParsedLifetimeDependenceKind kind, + ParamDecl *param, bool isInterfaceFile = false) const { if (kind == ParsedLifetimeDependenceKind::Inherit) { return true; } + + auto *afd = cast(decl); + auto paramType = param->getTypeInContext(); + auto ownership = param->getValueOwnership(); + auto loweredOwnership = ownership != ValueOwnership::Default + ? ownership + : getLoweredOwnership(param, afd); + if (kind == ParsedLifetimeDependenceKind::Borrow) { // An owned/consumed BitwiseCopyable value can be effectively borrowed // because its lifetime can be indefinitely extended. - if (loweredOwnership == ValueOwnership::Owned - && isBitwiseCopyable(type, ctx)) { + if (loweredOwnership == ValueOwnership::Owned && + isBitwiseCopyable(paramType, ctx)) { return true; } if (isInterfaceFile) { @@ -588,21 +606,23 @@ class LifetimeDependenceChecker { return loweredOwnership == ValueOwnership::InOut; } - bool isCompatibleWithOwnership(LifetimeDependenceKind kind, Type type, - ValueOwnership ownership) const { - auto *afd = cast(decl); + bool isCompatibleWithOwnership(LifetimeDependenceKind kind, + ParamDecl *param) const { if (kind == LifetimeDependenceKind::Inherit) { return true; } + + auto *afd = cast(decl); + auto paramType = param->getTypeInContext(); + auto ownership = param->getValueOwnership(); + auto loweredOwnership = ownership != ValueOwnership::Default + ? ownership + : getLoweredOwnership(param, afd); // Lifetime dependence always propagates through temporary BitwiseCopyable // values, even if the dependence is scoped. - if (isBitwiseCopyable(type, ctx)) { + if (isBitwiseCopyable(paramType, ctx)) { return true; } - auto loweredOwnership = ownership != ValueOwnership::Default - ? ownership - : getLoweredOwnership(afd); - assert(kind == LifetimeDependenceKind::Scope); return loweredOwnership == ValueOwnership::Shared || loweredOwnership == ValueOwnership::InOut; @@ -690,7 +710,7 @@ class LifetimeDependenceChecker { auto ownership = paramDecl->getValueOwnership(); auto loweredOwnership = ownership != ValueOwnership::Default ? ownership - : getLoweredOwnership(afd); + : getLoweredOwnership(paramDecl, afd); switch (parsedLifetimeKind) { case ParsedLifetimeDependenceKind::Default: { @@ -717,9 +737,7 @@ class LifetimeDependenceChecker { case ParsedLifetimeDependenceKind::Inout: { // @lifetime(borrow x) is valid only for borrowing parameters. // @lifetime(&x) is valid only for inout parameters. - auto loweredOwnership = ownership != ValueOwnership::Default - ? ownership : getLoweredOwnership(afd); - if (isCompatibleWithOwnership(parsedLifetimeKind, type, loweredOwnership, + if (isCompatibleWithOwnership(parsedLifetimeKind, paramDecl, isInterfaceFile())) { return LifetimeDependenceKind::Scope; } @@ -882,8 +900,16 @@ class LifetimeDependenceChecker { diag::lifetime_parameter_requires_inout, targetDescriptor->getString()); } + if (isDiagnosedEscapable(targetDeclAndIndex->first->getTypeInContext())) { + diagnose(targetDescriptor->getLoc(), + diag::lifetime_target_requires_nonescapable, "target"); + } targetIndex = targetDeclAndIndex->second; } else { + if (isDiagnosedEscapable(getResultOrYield())) { + diagnose(entry->getLoc(), diag::lifetime_target_requires_nonescapable, + "result"); + } targetIndex = afd->hasImplicitSelfDecl() ? afd->getParameters()->size() + 1 : afd->getParameters()->size(); @@ -1039,8 +1065,7 @@ class LifetimeDependenceChecker { } // Infer based on ownership if possible for either explicit accessors or // methods as long as they pass preceding ambiguity checks. - auto kind = inferLifetimeDependenceKind( - selfTypeInContext, afd->getImplicitSelfDecl()->getValueOwnership()); + auto kind = inferLifetimeDependenceKind(afd->getImplicitSelfDecl()); if (!kind) { // Special diagnostic for an attempt to depend on a consuming parameter. diagnose(returnLoc, @@ -1054,19 +1079,21 @@ class LifetimeDependenceChecker { // Infer the kind of dependence that makes sense for reading or writing a // stored property (for getters or initializers). std::optional - inferLifetimeDependenceKind(Type sourceType, ValueOwnership ownership) { + inferLifetimeDependenceKind(ParamDecl *param) { auto *afd = cast(decl); - if (!sourceType->isEscapable()) { + Type paramType = param->getTypeInContext(); + ValueOwnership ownership = param->getValueOwnership(); + if (!paramType->isEscapable()) { return LifetimeDependenceKind::Inherit; } // Lifetime dependence always propagates through temporary BitwiseCopyable // values, even if the dependence is scoped. - if (isBitwiseCopyable(sourceType, ctx)) { + if (isBitwiseCopyable(paramType, ctx)) { return LifetimeDependenceKind::Scope; } auto loweredOwnership = ownership != ValueOwnership::Default ? ownership - : getLoweredOwnership(afd); + : getLoweredOwnership(param, afd); // It is impossible to depend on a consumed Escapable value (unless it is // BitwiseCopyable as checked above). if (loweredOwnership == ValueOwnership::Owned) { @@ -1114,8 +1141,7 @@ class LifetimeDependenceChecker { return; } // A single Escapable parameter must be borrowed. - auto kind = inferLifetimeDependenceKind(paramTypeInContext, - param->getValueOwnership()); + auto kind = inferLifetimeDependenceKind(param); if (!kind) { diagnose(returnLoc, diag::lifetime_dependence_cannot_infer_scope_ownership, @@ -1172,9 +1198,7 @@ class LifetimeDependenceChecker { return; } auto kind = LifetimeDependenceKind::Scope; - auto paramOwnership = param->getValueOwnership(); - if (!isCompatibleWithOwnership(kind, paramTypeInContext, paramOwnership)) - { + if (!isCompatibleWithOwnership(kind, param)) { diagnose(returnLoc, diag::lifetime_dependence_cannot_infer_scope_ownership, param->getParameterName().str(), diagnosticQualifier()); @@ -1207,8 +1231,7 @@ class LifetimeDependenceChecker { } } - candidateLifetimeKind = - inferLifetimeDependenceKind(paramTypeInContext, paramOwnership); + candidateLifetimeKind = inferLifetimeDependenceKind(param); if (!candidateLifetimeKind) { continue; } @@ -1383,11 +1406,9 @@ class LifetimeDependenceChecker { } } } - auto *afd = cast(decl); // Either a Get or Modify without any wrapped accessor. Handle these like a // read of the stored property. - return inferLifetimeDependenceKind( - selfTypeInContext, afd->getImplicitSelfDecl()->getValueOwnership()); + return inferLifetimeDependenceKind(accessor->getImplicitSelfDecl()); } // Infer 'inout' parameter dependency when the only parameter is diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index 60f1c29bf7f18..681abf91fecd1 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -754,7 +754,6 @@ public func expectNil( } } -@_lifetime(copy value) public func expectNil( _ value: borrowing T?, _ message: @autoclosure () -> String = "", diff --git a/test/SILOptimizer/lifetime_dependence/semantics.swift b/test/SILOptimizer/lifetime_dependence/semantics.swift index 8a1e5fd40461a..e81b716846888 100644 --- a/test/SILOptimizer/lifetime_dependence/semantics.swift +++ b/test/SILOptimizer/lifetime_dependence/semantics.swift @@ -627,7 +627,6 @@ func test(arg: inout AddressableInt) -> Span { } // unsafeAddress generates an addressable value with a local scope. -@_lifetime(borrow arg) func testBorrowedAddressableInt(arg: Holder) -> Int { let span = arg.addressableInt.span() return span[0] diff --git a/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift b/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift index 00bfef446bff0..9d4e2bbc12866 100644 --- a/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift +++ b/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift @@ -169,7 +169,7 @@ public struct NoncopyableImplicitAccessors : ~Copyable & ~Escapable { @_lifetime(borrow self) get { ne } - @_lifetime(&self) + @_lifetime(self: copy newValue) set { ne = newValue } @@ -315,14 +315,3 @@ func inoutToImmortal(_ s: inout RawSpan) { s = _overrideLifetime(tmp, borrowing: ()) } -// ============================================================================= -// Dependence on non-Copyable values -// ============================================================================= - -@_lifetime(immortal) -func dependOnNonCopyable() -> NCBuffer { - let buffer = NCBuffer() - let count = buffer.bytes.count - _ = count - return buffer // expected-error {{noncopyable 'buffer' cannot be consumed when captured by an escaping closure or borrowed by a non-Escapable type}} -} diff --git a/test/SILOptimizer/lifetime_dependence/verify_library_diagnostics.swift b/test/SILOptimizer/lifetime_dependence/verify_library_diagnostics.swift index 77d325290af2e..15d39a5f37633 100644 --- a/test/SILOptimizer/lifetime_dependence/verify_library_diagnostics.swift +++ b/test/SILOptimizer/lifetime_dependence/verify_library_diagnostics.swift @@ -50,7 +50,7 @@ public struct NoncopyableImplicitAccessors : ~Copyable & ~Escapable { @_lifetime(borrow self) get { ne } - @_lifetime(&self) + @_lifetime(self: copy newValue) set { ne = newValue } diff --git a/test/Sema/lifetime_attr.swift b/test/Sema/lifetime_attr.swift index ef8b16fffe087..f2b6d89b9021f 100644 --- a/test/Sema/lifetime_attr.swift +++ b/test/Sema/lifetime_attr.swift @@ -125,4 +125,50 @@ struct Wrapper : ~Escapable { nonmutating _modify {// expected-error{{lifetime-dependent parameter 'self' must be 'inout'}} } } + + var otherNE: NE { + @_lifetime(copy self) + get { + _ne + } + @_lifetime(self: borrow newValue) + set { + self._ne = newValue + } + @_lifetime(&self) + _modify { + yield &self._ne + } + } } + +@_lifetime(inValue) // expected-error{{invalid lifetime dependence on an Escapable result}} +func getInt(_ inValue: Int) -> Int { + return inValue +} + +@_lifetime(_outValue: borrow inValue) // expected-error{{invalid lifetime dependence on an Escapable target}} +func getInt(_outValue: inout Int, _ inValue: Int) { + _outValue = inValue +} + +@_lifetime(inValue) // expected-error{{invalid lifetime dependence on an Escapable result}} +func getGeneric(_ inValue: T) -> T { + return inValue +} + +@_lifetime(_outValue: borrow inValue) // expected-error{{invalid lifetime dependence on an Escapable target}} +func getGeneric(_outValue: inout T, _ inValue: T) { + _outValue = inValue +} + +@_lifetime(borrow inValue) +func getGeneric(_ inValue: T) -> T { + return inValue +} + +@_lifetime(_outValue: borrow inValue) +func getGeneric(_outValue: inout T, _ inValue: T) { + _outValue = inValue +} + diff --git a/test/Sema/lifetime_depend_infer.swift b/test/Sema/lifetime_depend_infer.swift index 0b08ced63ca8c..0b0b481a4a0b1 100644 --- a/test/Sema/lifetime_depend_infer.swift +++ b/test/Sema/lifetime_depend_infer.swift @@ -331,7 +331,6 @@ struct TrivialAccessors { NEImmortal() } - @_lifetime(&self) set { // OK (no dependency) } } @@ -385,7 +384,7 @@ struct NonescapableSelfAccessors: ~Escapable { ne } - @_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)'}} + @_lifetime(self: newValue) // expected-error{{cannot infer the lifetime dependence scope on a mutating method with a ~Escapable parameter, specify '@_lifetime(borrow newValue)' or '@_lifetime(copy newValue)'}} set { ne = newValue } @@ -409,7 +408,7 @@ struct NonescapableSelfAccessors: ~Escapable { ne } - @_lifetime(copy self) + @_lifetime(self: copy newValue) set { ne = newValue } @@ -433,7 +432,7 @@ struct NonescapableSelfAccessors: ~Escapable { ne } - @_lifetime(&self) + @_lifetime(self: borrow newValue) set { ne = newValue } @@ -482,7 +481,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { ne } - @_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)'}} + @_lifetime(self: newValue) // expected-error{{cannot infer the lifetime dependence scope on a mutating method with a ~Escapable parameter, specify '@_lifetime(borrow newValue)' or '@_lifetime(copy newValue)'}} set { ne = newValue } @@ -506,7 +505,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { ne } - @_lifetime(copy self) + @_lifetime(self: copy newValue) set { ne = newValue } @@ -530,7 +529,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { ne } - @_lifetime(&self) + @_lifetime(self: copy newValue) set { ne = newValue } @@ -574,10 +573,10 @@ struct NonEscapableMutableSelf: ~Escapable { @_lifetime(self: 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 mutatingMethodOneParamLifetime(_: NE) {} - @_lifetime(copy self) // OK + @_lifetime(self: copy self) // OK mutating func mutatingMethodOneParamCopy(_: NE) {} - @_lifetime(&self) + @_lifetime(self: copy self) // OK mutating func mutatingMethodOneParamBorrow(_: NE) {} } diff --git a/test/Sema/lifetime_depend_infer_lazy.swift b/test/Sema/lifetime_depend_infer_lazy.swift index 2f612c80863d2..0868a8aaa2951 100644 --- a/test/Sema/lifetime_depend_infer_lazy.swift +++ b/test/Sema/lifetime_depend_infer_lazy.swift @@ -266,7 +266,7 @@ struct NonescapableSelfAccessors: ~Escapable { ne } - @_lifetime(self) // OK + @_lifetime(self: newValue) // OK set { ne = newValue } @@ -290,7 +290,7 @@ struct NonescapableSelfAccessors: ~Escapable { ne } - @_lifetime(copy self) + @_lifetime(self: copy newValue) set { ne = newValue } @@ -314,7 +314,7 @@ struct NonescapableSelfAccessors: ~Escapable { ne } - @_lifetime(&self) + @_lifetime(self: borrow newValue) set { ne = newValue } @@ -363,7 +363,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { ne } - @_lifetime(self) // OK + @_lifetime(self: newValue) // OK set { ne = newValue } @@ -387,7 +387,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { ne } - @_lifetime(copy self) + @_lifetime(self: copy self) set { ne = newValue } @@ -411,7 +411,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { ne } - @_lifetime(&self) + @_lifetime(self: borrow newValue) set { ne = newValue } @@ -446,9 +446,9 @@ struct NonEscapableMutableSelf: ~Escapable { @_lifetime(self: self) // OK mutating func mutatingMethodOneParamLifetime(_: NE) {} - @_lifetime(copy self) // OK + @_lifetime(self: copy self) // OK mutating func mutatingMethodOneParamCopy(_: NE) {} - @_lifetime(&self) + @_lifetime(self: copy self) // OK mutating func mutatingMethodOneParamBorrow(_: NE) {} }