From 245915d795854e8547d61043e953969c4b4416fd Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 31 Mar 2025 12:23:18 -0700 Subject: [PATCH 1/8] [NFC] Replace calls of inferLifetimeDependenceKindFromType and isCompatibleOwnership to inferLifetimeDependenceKind --- lib/AST/LifetimeDependence.cpp | 59 ++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 8bdef9dc4b73d..6906e6119ce60 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -138,16 +138,6 @@ void LifetimeDependenceInfo::Profile(llvm::FoldingSetNodeID &ID) const { } } -// Infer the kind of dependence that would be implied by assigning into a stored -// property of 'sourceType'. -static LifetimeDependenceKind -inferLifetimeDependenceKindFromType(Type sourceType) { - if (sourceType->isEscapable()) { - return LifetimeDependenceKind::Scope; - } - return LifetimeDependenceKind::Inherit; -} - // Warning: this is incorrect for Setter 'newValue' parameters. It should only // be called for a Setter's 'self'. static ValueOwnership getLoweredOwnership(AbstractFunctionDecl *afd) { @@ -829,15 +819,35 @@ class LifetimeDependenceChecker { return; } } - auto kind = inferLifetimeDependenceKindFromType(selfTypeInContext); - auto selfOwnership = afd->getImplicitSelfDecl()->getValueOwnership(); - if (!isCompatibleWithOwnership(kind, selfTypeInContext, selfOwnership)) { + auto kind = inferLifetimeDependenceKind( + selfTypeInContext, afd->getImplicitSelfDecl()->getValueOwnership()); + if (!kind) { diagnose(returnLoc, diag::lifetime_dependence_cannot_infer_scope_ownership, "self", diagnosticQualifier()); return; } - pushDeps(createDeps(resultIndex).add(selfIndex, kind)); + pushDeps(createDeps(resultIndex).add(selfIndex, *kind)); + } + + std::optional + inferLifetimeDependenceKind(Type sourceType, ValueOwnership ownership) { + if (!sourceType->isEscapable()) { + return LifetimeDependenceKind::Inherit; + } + // Lifetime dependence always propagates through temporary BitwiseCopyable + // values, even if the dependence is scoped. + if (isBitwiseCopyable(sourceType, ctx)) { + return LifetimeDependenceKind::Scope; + } + auto loweredOwnership = ownership != ValueOwnership::Default + ? ownership + : getLoweredOwnership(afd); + if (loweredOwnership != ValueOwnership::Shared && + loweredOwnership != ValueOwnership::InOut) { + return std::nullopt; + } + return LifetimeDependenceKind::Scope; } // Infer implicit initialization. The dependence kind can be inferred, similar @@ -861,16 +871,15 @@ class LifetimeDependenceChecker { if (paramTypeInContext->hasError()) { continue; } - auto kind = inferLifetimeDependenceKindFromType(paramTypeInContext); - auto paramOwnership = param->getValueOwnership(); - if (!isCompatibleWithOwnership(kind, paramTypeInContext, paramOwnership)) - { + auto kind = inferLifetimeDependenceKind(paramTypeInContext, + param->getValueOwnership()); + if (!kind) { diagnose(returnLoc, diag::lifetime_dependence_cannot_infer_scope_ownership, param->getParameterName().str(), diagnosticQualifier()); continue; } - targetDeps = std::move(targetDeps).add(paramIndex, kind); + targetDeps = std::move(targetDeps).add(paramIndex, *kind); } pushDeps(std::move(targetDeps)); } @@ -954,9 +963,8 @@ class LifetimeDependenceChecker { } candidateLifetimeKind = - inferLifetimeDependenceKindFromType(paramTypeInContext); - if (!isCompatibleWithOwnership( - *candidateLifetimeKind, paramTypeInContext, paramOwnership)) { + inferLifetimeDependenceKind(paramTypeInContext, paramOwnership); + if (!candidateLifetimeKind) { continue; } if (candidateParamIndex) { @@ -1025,11 +1033,12 @@ class LifetimeDependenceChecker { if (paramTypeInContext->hasError()) { return; } - auto kind = inferLifetimeDependenceKindFromType(paramTypeInContext); + auto kind = inferLifetimeDependenceKind(paramTypeInContext, + param->getValueOwnership()); pushDeps(createDeps(selfIndex) - .add(selfIndex, LifetimeDependenceKind::Inherit) - .add(newValIdx, kind)); + .add(selfIndex, LifetimeDependenceKind::Inherit) + .add(newValIdx, *kind)); break; } default: From cfacd25df4152bdec48e877f3cee248fb8cf75c5 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 31 Mar 2025 12:49:50 -0700 Subject: [PATCH 2/8] Add support for @lifetime(&arg) --- include/swift/AST/ASTBridging.h | 1 + include/swift/AST/DiagnosticsSema.def | 3 + include/swift/AST/LifetimeDependence.h | 3 +- lib/AST/Bridging/DeclAttributeBridging.cpp | 2 + lib/AST/LifetimeDependence.cpp | 98 +++++++++++++++------- lib/ASTGen/Sources/ASTGen/DeclAttrs.swift | 3 + lib/Parse/ParseDecl.cpp | 5 ++ 7 files changed, 84 insertions(+), 31 deletions(-) diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 31cce3d1ff0c2..efc8ca7ef8f93 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -1088,6 +1088,7 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedParsedLifetimeDependenceKind { BridgedParsedLifetimeDependenceKindDefault, BridgedParsedLifetimeDependenceKindScope, BridgedParsedLifetimeDependenceKindInherit, + BridgedParsedLifetimeDependenceKindInout }; class BridgedLifetimeDescriptor { diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ca975820763c1..7eb521fafcae2 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8171,6 +8171,9 @@ ERROR(lifetime_dependence_invalid_inherit_escapable_type, none, ERROR(lifetime_dependence_cannot_use_parsed_borrow_consuming, none, "invalid use of borrow dependence with consuming ownership", ()) +ERROR(lifetime_dependence_cannot_use_default_escapable_consuming, none, + "invalid lifetime dependence on an Escapable value with %0 ownership", + (StringRef)) ERROR(lifetime_dependence_cannot_use_parsed_borrow_inout, none, "invalid use of borrow dependence on the same inout parameter", ()) diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index 0d8cd14029bbb..b0c622a545cd5 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -39,7 +39,8 @@ class SILResultInfo; enum class ParsedLifetimeDependenceKind : uint8_t { Default = 0, Scope, - Inherit // Only used with deserialized decls + Inherit, // Only used with deserialized decls + Inout }; enum class LifetimeDependenceKind : uint8_t { Inherit = 0, Scope }; diff --git a/lib/AST/Bridging/DeclAttributeBridging.cpp b/lib/AST/Bridging/DeclAttributeBridging.cpp index 774879aad0bda..5a7346ac7cf88 100644 --- a/lib/AST/Bridging/DeclAttributeBridging.cpp +++ b/lib/AST/Bridging/DeclAttributeBridging.cpp @@ -474,6 +474,8 @@ unbridged(BridgedParsedLifetimeDependenceKind kind) { return swift::ParsedLifetimeDependenceKind::Scope; case BridgedParsedLifetimeDependenceKindInherit: return swift::ParsedLifetimeDependenceKind::Inherit; + case BridgedParsedLifetimeDependenceKindInout: + return swift::ParsedLifetimeDependenceKind::Inout; } llvm_unreachable("unhandled enum value"); } diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 6906e6119ce60..09a6824dd6c47 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -74,6 +74,20 @@ filterEscapableLifetimeDependencies(GenericSignature sig, return didRemoveLifetimeDependencies; } +StringRef +getNameForParsedLifetimeDependenceKind(ParsedLifetimeDependenceKind kind) { + switch (kind) { + case ParsedLifetimeDependenceKind::Scope: + return "borrow"; + case ParsedLifetimeDependenceKind::Inherit: + return "copy"; + case ParsedLifetimeDependenceKind::Inout: + return "inout"; + default: + return ""; + } +} + std::string LifetimeDependenceInfo::getString() const { std::string lifetimeDependenceString = "@lifetime("; auto addressable = getAddressableIndices(); @@ -446,9 +460,9 @@ class LifetimeDependenceChecker { } } - bool isCompatibleWithOwnership(LifetimeDependenceKind kind, Type type, + bool isCompatibleWithOwnership(ParsedLifetimeDependenceKind kind, Type type, ValueOwnership ownership) const { - if (kind == LifetimeDependenceKind::Inherit) { + if (kind == ParsedLifetimeDependenceKind::Inherit) { return true; } // Lifetime dependence always propagates through temporary BitwiseCopyable @@ -456,16 +470,33 @@ class LifetimeDependenceChecker { if (isBitwiseCopyable(type, ctx)) { return true; } - assert(kind == LifetimeDependenceKind::Scope); auto loweredOwnership = ownership != ValueOwnership::Default ? ownership : getLoweredOwnership(afd); - if (loweredOwnership == ValueOwnership::InOut || - loweredOwnership == ValueOwnership::Shared) { + if (kind == ParsedLifetimeDependenceKind::Scope) { + return loweredOwnership == ValueOwnership::Shared; + } + assert(kind == ParsedLifetimeDependenceKind::Inout); + return loweredOwnership == ValueOwnership::InOut; + } + + bool isCompatibleWithOwnership(LifetimeDependenceKind kind, Type type, + ValueOwnership ownership) const { + if (kind == LifetimeDependenceKind::Inherit) { return true; } - assert(loweredOwnership == ValueOwnership::Owned); - return false; + // Lifetime dependence always propagates through temporary BitwiseCopyable + // values, even if the dependence is scoped. + if (isBitwiseCopyable(type, ctx)) { + return true; + } + auto loweredOwnership = ownership != ValueOwnership::Default + ? ownership + : getLoweredOwnership(afd); + + assert(kind == LifetimeDependenceKind::Scope); + return loweredOwnership == ValueOwnership::Shared || + loweredOwnership == ValueOwnership::InOut; } struct TargetDeps { @@ -541,44 +572,51 @@ class LifetimeDependenceChecker { std::optional getDependenceKindFromDescriptor(LifetimeDescriptor descriptor, ParamDecl *decl) { - auto loc = descriptor.getLoc(); - - auto ownership = decl->getValueOwnership(); + auto loc = decl->getLoc(); auto type = decl->getTypeInContext(); + auto parsedLifetimeKind = descriptor.getParsedLifetimeDependenceKind(); + auto ownership = decl->getValueOwnership(); + auto loweredOwnership = ownership != ValueOwnership::Default + ? ownership + : getLoweredOwnership(afd); - LifetimeDependenceKind kind; - switch (descriptor.getParsedLifetimeDependenceKind()) { - case ParsedLifetimeDependenceKind::Default: + if (parsedLifetimeKind == ParsedLifetimeDependenceKind::Default) { if (type->isEscapable()) { - kind = LifetimeDependenceKind::Scope; + if (loweredOwnership == ValueOwnership::Shared || + loweredOwnership == ValueOwnership::InOut) { + return LifetimeDependenceKind::Scope; + } else { + diagnose( + loc, + diag::lifetime_dependence_cannot_use_default_escapable_consuming, + getOwnershipSpelling(loweredOwnership)); + return std::nullopt; + } } else if (useLazyInference()) { - kind = LifetimeDependenceKind::Inherit; - } else { - diagnose(loc, diag::lifetime_dependence_cannot_infer_kind, - diagnosticQualifier(), descriptor.getString()); - return std::nullopt; + return LifetimeDependenceKind::Inherit; } - break; - case ParsedLifetimeDependenceKind::Scope: - kind = LifetimeDependenceKind::Scope; - break; - case ParsedLifetimeDependenceKind::Inherit: - kind = LifetimeDependenceKind::Inherit; - break; + diagnose(loc, diag::lifetime_dependence_cannot_infer_kind, + diagnosticQualifier(), descriptor.getString()); + return std::nullopt; } - // @lifetime(borrow x) is invalid for consuming parameters. - if (!isCompatibleWithOwnership(kind, type, ownership)) { + + // @lifetime(borrow x) is valid only for borrowing parameters. + // @lifetime(inout x) is valid only for inout parameters. + if (!isCompatibleWithOwnership(parsedLifetimeKind, type, ownership)) { diagnose(loc, diag::lifetime_dependence_cannot_use_parsed_borrow_consuming); return std::nullopt; } // @lifetime(copy x) is only invalid for Escapable types. - if (kind == LifetimeDependenceKind::Inherit && type->isEscapable()) { + if (parsedLifetimeKind == ParsedLifetimeDependenceKind::Inherit && + type->isEscapable()) { diagnose(loc, diag::lifetime_dependence_invalid_inherit_escapable_type, descriptor.getString()); return std::nullopt; } - return kind; + return parsedLifetimeKind == ParsedLifetimeDependenceKind::Inherit + ? LifetimeDependenceKind::Inherit + : LifetimeDependenceKind::Scope; } // Finds the ParamDecl* and its index from a LifetimeDescriptor diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 7d80b663340f0..d91d38b410600 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -1106,6 +1106,9 @@ extension ASTGenVisitor { } else if let borrowExpr = node.as(BorrowExprSyntax.self) { lifetimeDependenceKind = .scope descriptorExpr = borrowExpr.expression + } else if let inoutExpr = node.as(InOutExprSyntax.self) { + lifetimeDependenceKind = .inout + descriptorExpr = inoutExpr.expression } else { lifetimeDependenceKind = .default descriptorExpr = node diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b3a4319d904ba..962fba7d1575f 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5021,6 +5021,11 @@ ParserResult Parser::parseLifetimeEntry(SourceLoc loc) { tok::kw_self)) { return ParsedLifetimeDependenceKind::Scope; } + if (Tok.is(tok::amp_prefix) && + peekToken().isAny(tok::identifier, tok::integer_literal, + tok::kw_self)) { + return ParsedLifetimeDependenceKind::Inout; + } return std::nullopt; }; From d87a2b2ada8b1526b720ef21ab56d1dc9a291f50 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 31 Mar 2025 13:03:09 -0700 Subject: [PATCH 3/8] Rename ParsedLifetimeDependenceKind::Scope -> ParsedLifetimeDependenceKind::Borrow --- include/swift/AST/ASTBridging.h | 2 +- include/swift/AST/LifetimeDependence.h | 7 +++++-- lib/AST/Bridging/DeclAttributeBridging.cpp | 4 ++-- lib/AST/LifetimeDependence.cpp | 8 ++++---- lib/ASTGen/Sources/ASTGen/DeclAttrs.swift | 2 +- lib/Parse/ParseDecl.cpp | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index efc8ca7ef8f93..f3b7220761f56 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -1086,7 +1086,7 @@ BridgedInlineAttr BridgedInlineAttr_createParsed(BridgedASTContext cContext, enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedParsedLifetimeDependenceKind { BridgedParsedLifetimeDependenceKindDefault, - BridgedParsedLifetimeDependenceKindScope, + BridgedParsedLifetimeDependenceKindBorrow, BridgedParsedLifetimeDependenceKindInherit, BridgedParsedLifetimeDependenceKindInout }; diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index b0c622a545cd5..25cce41d3ba8e 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -38,7 +38,7 @@ class SILResultInfo; enum class ParsedLifetimeDependenceKind : uint8_t { Default = 0, - Scope, + Borrow, Inherit, // Only used with deserialized decls Inout }; @@ -208,12 +208,15 @@ class LifetimeEntry final result += ", "; } switch (source.getParsedLifetimeDependenceKind()) { - case ParsedLifetimeDependenceKind::Scope: + case ParsedLifetimeDependenceKind::Borrow: result += "borrow "; break; case ParsedLifetimeDependenceKind::Inherit: result += "copy "; break; + case ParsedLifetimeDependenceKind::InOut: + result += "inout "; + break; default: break; } diff --git a/lib/AST/Bridging/DeclAttributeBridging.cpp b/lib/AST/Bridging/DeclAttributeBridging.cpp index 5a7346ac7cf88..1cb1165bf470b 100644 --- a/lib/AST/Bridging/DeclAttributeBridging.cpp +++ b/lib/AST/Bridging/DeclAttributeBridging.cpp @@ -470,8 +470,8 @@ unbridged(BridgedParsedLifetimeDependenceKind kind) { switch (kind) { case BridgedParsedLifetimeDependenceKindDefault: return swift::ParsedLifetimeDependenceKind::Default; - case BridgedParsedLifetimeDependenceKindScope: - return swift::ParsedLifetimeDependenceKind::Scope; + case BridgedParsedLifetimeDependenceKindBorrow: + return swift::ParsedLifetimeDependenceKind::Borrow; case BridgedParsedLifetimeDependenceKindInherit: return swift::ParsedLifetimeDependenceKind::Inherit; case BridgedParsedLifetimeDependenceKindInout: diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 09a6824dd6c47..26ed52902e2ca 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -77,7 +77,7 @@ filterEscapableLifetimeDependencies(GenericSignature sig, StringRef getNameForParsedLifetimeDependenceKind(ParsedLifetimeDependenceKind kind) { switch (kind) { - case ParsedLifetimeDependenceKind::Scope: + case ParsedLifetimeDependenceKind::Borrow: return "borrow"; case ParsedLifetimeDependenceKind::Inherit: return "copy"; @@ -473,7 +473,7 @@ class LifetimeDependenceChecker { auto loweredOwnership = ownership != ValueOwnership::Default ? ownership : getLoweredOwnership(afd); - if (kind == ParsedLifetimeDependenceKind::Scope) { + if (kind == ParsedLifetimeDependenceKind::Borrow) { return loweredOwnership == ValueOwnership::Shared; } assert(kind == ParsedLifetimeDependenceKind::Inout); @@ -1166,7 +1166,7 @@ static std::optional checkSILTypeModifiers( auto loc = descriptor.getLoc(); auto kind = descriptor.getParsedLifetimeDependenceKind(); - if (kind == ParsedLifetimeDependenceKind::Scope && + if (kind == ParsedLifetimeDependenceKind::Borrow && isConsumedParameterInCallee(paramConvention)) { diags.diagnose(loc, diag::lifetime_dependence_cannot_use_kind, "_scope", getStringForParameterConvention(paramConvention)); @@ -1181,7 +1181,7 @@ static std::optional checkSILTypeModifiers( if (kind == ParsedLifetimeDependenceKind::Inherit) { inheritLifetimeParamIndices.set(paramIndexToSet); } else { - assert(kind == ParsedLifetimeDependenceKind::Scope); + assert(kind == ParsedLifetimeDependenceKind::Borrow); scopeLifetimeParamIndices.set(paramIndexToSet); } return false; diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index d91d38b410600..bc91febfee879 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -1104,7 +1104,7 @@ extension ASTGenVisitor { lifetimeDependenceKind = .inherit descriptorExpr = copyExpr.expression } else if let borrowExpr = node.as(BorrowExprSyntax.self) { - lifetimeDependenceKind = .scope + lifetimeDependenceKind = .borrow descriptorExpr = borrowExpr.expression } else if let inoutExpr = node.as(InOutExprSyntax.self) { lifetimeDependenceKind = .inout diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 962fba7d1575f..4429477f708ee 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5019,7 +5019,7 @@ ParserResult Parser::parseLifetimeEntry(SourceLoc loc) { if (Tok.isContextualKeyword("borrow") && peekToken().isAny(tok::identifier, tok::integer_literal, tok::kw_self)) { - return ParsedLifetimeDependenceKind::Scope; + return ParsedLifetimeDependenceKind::Borrow; } if (Tok.is(tok::amp_prefix) && peekToken().isAny(tok::identifier, tok::integer_literal, From 0d3d89ae22e2461a1cf6c0c0167821cb3c9fabf2 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 31 Mar 2025 14:15:12 -0700 Subject: [PATCH 4/8] [NFC] Update diagnostic msg --- include/swift/AST/DiagnosticsSema.def | 4 +-- include/swift/AST/LifetimeDependence.h | 36 +++-------------------- lib/AST/LifetimeDependence.cpp | 40 +++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 7eb521fafcae2..ccf07e88ad35a 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8169,8 +8169,8 @@ ERROR(lifetime_dependence_invalid_inherit_escapable_type, none, "'@lifetime(borrow %0)' instead", (StringRef)) ERROR(lifetime_dependence_cannot_use_parsed_borrow_consuming, none, - "invalid use of borrow dependence with consuming ownership", - ()) + "invalid use of %0 dependence with %1 ownership", + (StringRef, StringRef)) ERROR(lifetime_dependence_cannot_use_default_escapable_consuming, none, "invalid lifetime dependence on an Escapable value with %0 ownership", (StringRef)) diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index 25cce41d3ba8e..56681d4fc087d 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -183,6 +183,7 @@ class LifetimeEntry final ArrayRef sources, std::optional targetDescriptor = std::nullopt); + std::string getString() const; SourceLoc getLoc() const { return startLoc; } SourceLoc getStartLoc() const { return startLoc; } SourceLoc getEndLoc() const { return endLoc; } @@ -194,38 +195,6 @@ class LifetimeEntry final std::optional getTargetDescriptor() const { return targetDescriptor; } - - std::string getString() const { - std::string result = "@lifetime("; - if (targetDescriptor.has_value()) { - result += targetDescriptor->getString(); - result += ": "; - } - - bool firstElem = true; - for (auto source : getSources()) { - if (!firstElem) { - result += ", "; - } - switch (source.getParsedLifetimeDependenceKind()) { - case ParsedLifetimeDependenceKind::Borrow: - result += "borrow "; - break; - case ParsedLifetimeDependenceKind::Inherit: - result += "copy "; - break; - case ParsedLifetimeDependenceKind::InOut: - result += "inout "; - break; - default: - break; - } - result += source.getString(); - firstElem = false; - } - result += ")"; - return result; - } }; class LifetimeDependenceInfo { @@ -370,6 +339,9 @@ filterEscapableLifetimeDependencies(GenericSignature sig, SmallVectorImpl &outputs, llvm::function_ref getSubstTargetType); +StringRef +getNameForParsedLifetimeDependenceKind(ParsedLifetimeDependenceKind kind); + } // namespace swift #endif diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 26ed52902e2ca..9a2790293889a 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -37,6 +37,35 @@ LifetimeEntry::create(const ASTContext &ctx, SourceLoc startLoc, return new (mem) LifetimeEntry(startLoc, endLoc, sources, targetDescriptor); } +std::string LifetimeEntry::getString() const { + std::string result = "@lifetime("; + if (targetDescriptor.has_value()) { + result += targetDescriptor->getString(); + result += ": "; + } + + bool firstElem = true; + for (auto source : getSources()) { + if (!firstElem) { + result += ", "; + } + auto lifetimeKind = source.getParsedLifetimeDependenceKind(); + auto kindString = getNameForParsedLifetimeDependenceKind(lifetimeKind); + bool printSpace = (lifetimeKind == ParsedLifetimeDependenceKind::Borrow || + lifetimeKind == ParsedLifetimeDependenceKind::Inherit); + if (!kindString.empty()) { + result += kindString; + } + if (printSpace) { + result += " "; + } + result += source.getString(); + firstElem = false; + } + result += ")"; + return result; +} + std::optional getLifetimeDependenceFor(ArrayRef lifetimeDependencies, unsigned index) { @@ -82,7 +111,7 @@ getNameForParsedLifetimeDependenceKind(ParsedLifetimeDependenceKind kind) { case ParsedLifetimeDependenceKind::Inherit: return "copy"; case ParsedLifetimeDependenceKind::Inout: - return "inout"; + return "&"; default: return ""; } @@ -572,7 +601,7 @@ class LifetimeDependenceChecker { std::optional getDependenceKindFromDescriptor(LifetimeDescriptor descriptor, ParamDecl *decl) { - auto loc = decl->getLoc(); + auto loc = descriptor.getLoc(); auto type = decl->getTypeInContext(); auto parsedLifetimeKind = descriptor.getParsedLifetimeDependenceKind(); auto ownership = decl->getValueOwnership(); @@ -602,9 +631,12 @@ class LifetimeDependenceChecker { // @lifetime(borrow x) is valid only for borrowing parameters. // @lifetime(inout x) is valid only for inout parameters. - if (!isCompatibleWithOwnership(parsedLifetimeKind, type, ownership)) { + if (!isCompatibleWithOwnership(parsedLifetimeKind, type, + loweredOwnership)) { diagnose(loc, - diag::lifetime_dependence_cannot_use_parsed_borrow_consuming); + diag::lifetime_dependence_cannot_use_parsed_borrow_consuming, + getNameForParsedLifetimeDependenceKind(parsedLifetimeKind), + getOwnershipSpelling(loweredOwnership)); return std::nullopt; } // @lifetime(copy x) is only invalid for Escapable types. From 364338283b5a6ea9f2f35f753d6f346902ce5e2e Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 31 Mar 2025 14:15:22 -0700 Subject: [PATCH 5/8] [NFC] Update tests --- test/SILOptimizer/Inputs/SpanExtras.swift | 4 ++-- .../lifetime_dependence_diagnostics.swift | 2 +- .../lifetime_dependence_mutate.swift | 4 ++-- .../lifetime_dependence/semantics.swift | 18 +++++++-------- .../lifetime_depend_infer.swiftinterface | 22 +++++++++---------- test/Sema/lifetime_attr.swift | 6 +++++ test/Sema/lifetime_depend_infer.swift | 12 +++++----- test/Sema/lifetime_depend_infer_lazy.swift | 10 ++++----- .../def_explicit_lifetime_dependence.swift | 2 +- .../def_implicit_lifetime_dependence.swift | 2 +- 10 files changed, 44 insertions(+), 38 deletions(-) diff --git a/test/SILOptimizer/Inputs/SpanExtras.swift b/test/SILOptimizer/Inputs/SpanExtras.swift index 5775ccce3417d..881ac3d998b26 100644 --- a/test/SILOptimizer/Inputs/SpanExtras.swift +++ b/test/SILOptimizer/Inputs/SpanExtras.swift @@ -40,7 +40,7 @@ internal func _overrideLifetime< @_unsafeNonescapableResult @_alwaysEmitIntoClient @_transparent -@lifetime(borrow source) +@lifetime(&source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -310,7 +310,7 @@ extension MutableSpan where Element: ~Copyable { precondition(indices.contains(position), "index out of bounds") yield self[unchecked: position] } - @lifetime(borrow self) + @lifetime(&self) _modify { precondition(indices.contains(position), "index out of bounds") yield &self[unchecked: position] diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_diagnostics.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_diagnostics.swift index e161fa38707c7..eb8e31e76284c 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_diagnostics.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_diagnostics.swift @@ -39,7 +39,7 @@ public struct NEInt: ~Escapable { var iprop: NEInt { @lifetime(copy self) _read { yield self } - @lifetime(borrow self) + @lifetime(&self) _modify { yield &self } } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift index b1b28f35b416a..e880a0dac7d07 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift @@ -41,7 +41,7 @@ internal func _overrideLifetime< /// the `source` argument. @_unsafeNonescapableResult @_transparent -@lifetime(borrow source) +@lifetime(&source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -112,7 +112,7 @@ struct NC : ~Copyable { let c: Int // Requires a mutable borrow. - @lifetime(borrow self) + @lifetime(&self) mutating func getMutableSpan() -> MutableSpan { MutableSpan(p, c) } diff --git a/test/SILOptimizer/lifetime_dependence/semantics.swift b/test/SILOptimizer/lifetime_dependence/semantics.swift index 4dfcec2565417..7a2e012532932 100644 --- a/test/SILOptimizer/lifetime_dependence/semantics.swift +++ b/test/SILOptimizer/lifetime_dependence/semantics.swift @@ -49,7 +49,7 @@ internal func _overrideLifetime< /// the `source` argument. @_unsafeNonescapableResult @_transparent -@lifetime(borrow source) +@lifetime(&source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -110,7 +110,7 @@ struct MutableSpan: ~Escapable, ~Copyable { let base: UnsafeMutablePointer let count: Int - @lifetime(borrow base) + @lifetime(&base) init(base: UnsafeMutablePointer, count: Int) { self.base = base self.count = count @@ -128,7 +128,7 @@ extension Array { } extension Array { - @lifetime(borrow self) + @lifetime(&self) mutating func mutableSpan() -> MutableSpan { /* not the real implementation */ let p = self.withUnsafeMutableBufferPointer { $0.baseAddress! } @@ -263,7 +263,7 @@ struct MutableView: ~Escapable, ~Copyable { self.owner = copy owner // OK } - @lifetime(borrow mutableOwner) + @lifetime(&mutableOwner) init(mutableOwner: inout AnyObject) { // Initialization of a ~Escapable value is unenforced. So we can initialize // the `owner` property without locally borrowing `mutableOwner`. @@ -432,13 +432,13 @@ func testGlobal(local: InnerTrivial) -> Span { // Scoped dependence on mutable values // ============================================================================= -@lifetime(borrow a) +@lifetime(&a) func testInoutBorrow(a: inout [Int]) -> Span { a.span() // expected-error {{lifetime-dependent value escapes its scope}} // expected-note @-1{{it depends on this scoped access to variable 'a'}} } // expected-note {{this use causes the lifetime-dependent value to escape}} -@lifetime(borrow a) +@lifetime(&a) func testInoutMutableBorrow(a: inout [Int]) -> MutableSpan { a.mutableSpan() } @@ -460,7 +460,7 @@ extension Container { View(owner: self.owner) // OK } - @lifetime(borrow self) + @lifetime(&self) mutating func mutableView() -> MutableView { // Reading 'self.owner' creates a local borrow scope. This new MutableView // depends on a the local borrow scope for 'self.owner', so it cannot be @@ -469,7 +469,7 @@ extension Container { // expected-note @-1{{it depends on this scoped access to variable 'self'}} } // expected-note {{this use causes the lifetime-dependent value to escape}} - @lifetime(borrow self) + @lifetime(&self) mutating func mutableViewModifiesOwner() -> MutableView { // Passing '&self.owner' inout creates a nested exclusive access that must extend to all uses of the new // MutableView. This is allowed because the nested access is logically extended to the end of the function (without @@ -477,7 +477,7 @@ extension Container { MutableView(mutableOwner: &self.owner) } - @lifetime(borrow self) + @lifetime(&self) mutating func mutableSpan() -> MutableSpan { // Reading 'self.pointer' creates a local borrow scope. The local borrow // scope is ignored because 'pointer' is a trivial value. Instead, the new diff --git a/test/Sema/Inputs/lifetime_depend_infer.swiftinterface b/test/Sema/Inputs/lifetime_depend_infer.swiftinterface index d4776fcf1106e..f7982508c5773 100644 --- a/test/Sema/Inputs/lifetime_depend_infer.swiftinterface +++ b/test/Sema/Inputs/lifetime_depend_infer.swiftinterface @@ -40,7 +40,7 @@ public struct NonEscapableSelf : ~Swift.Escapable { public mutating func mutatingMethodNoParamCopy() -> lifetime_depend_infer.NonEscapableSelf #endif #if $LifetimeDependence - @lifetime(borrow self) + @lifetime(&self) public mutating func mutatingMethodNoParamBorrow() -> lifetime_depend_infer.NonEscapableSelf #endif #if $LifetimeDependence @@ -67,7 +67,7 @@ public struct NonEscapableSelf : ~Swift.Escapable { public mutating func mutatingMethodOneParamCopy(_: Swift.Int) -> lifetime_depend_infer.NonEscapableSelf #endif #if $LifetimeDependence - @lifetime(borrow self) + @lifetime(&self) public mutating func mutatingMethodOneParamBorrow(_: Swift.Int) -> lifetime_depend_infer.NonEscapableSelf #endif } @@ -85,7 +85,7 @@ public struct EscapableTrivialSelf { public mutating func mutatingMethodNoParamLifetime() -> lifetime_depend_infer.NEImmortal #endif #if compiler(>=5.3) && $NonescapableTypes && $LifetimeDependence - @lifetime(borrow self) + @lifetime(&self) public mutating func mutatingMethodNoParamBorrow() -> lifetime_depend_infer.NEImmortal #endif #if compiler(>=5.3) && $NonescapableTypes && $LifetimeDependence @@ -101,7 +101,7 @@ public struct EscapableTrivialSelf { public mutating func mutatingMethodOneParamLifetime(_: Swift.Int) -> lifetime_depend_infer.NEImmortal #endif #if compiler(>=5.3) && $NonescapableTypes && $LifetimeDependence - @lifetime(borrow self) + @lifetime(&self) public mutating func mutatingMethodOneParamBorrow(_: Swift.Int) -> lifetime_depend_infer.NEImmortal #endif } @@ -126,7 +126,7 @@ public struct EscapableNonTrivialSelf { public mutating func mutatingMethodNoParamLifetime() -> lifetime_depend_infer.NEImmortal #endif #if compiler(>=5.3) && $NonescapableTypes && $LifetimeDependence - @lifetime(borrow self) + @lifetime(&self) public mutating func mutatingMethodNoParamBorrow() -> lifetime_depend_infer.NEImmortal #endif #if compiler(>=5.3) && $NonescapableTypes && $LifetimeDependence @@ -148,7 +148,7 @@ public struct EscapableNonTrivialSelf { public mutating func mutatingMethodOneParamLifetime(_: Swift.Int) -> lifetime_depend_infer.NEImmortal #endif #if compiler(>=5.3) && $NonescapableTypes && $LifetimeDependence - @lifetime(borrow self) + @lifetime(&self) public mutating func mutatingMethodOneParamBorrow(_: Swift.Int) -> lifetime_depend_infer.NEImmortal #endif } @@ -256,7 +256,7 @@ public struct NonescapableSelfAccessors : ~Swift.Escapable { #if $LifetimeDependence public var neYielded: lifetime_depend_infer.NE { _read - @lifetime(borrow self) + @lifetime(&self) _modify } #endif @@ -272,7 +272,7 @@ public struct NoncopyableSelfAccessors : ~Copyable & ~Escapable { #if $LifetimeDependence public var neYielded: lifetime_depend_infer.NE { _read - @lifetime(borrow self) + @lifetime(&self) _modify } #endif @@ -312,7 +312,7 @@ public struct NoncopyableSelfAccessors : ~Copyable & ~Escapable { public var neComputedBorrow: lifetime_depend_infer.NE { @lifetime(borrow self) get - @lifetime(borrow self) + @lifetime(&self) set } #endif @@ -320,7 +320,7 @@ public struct NoncopyableSelfAccessors : ~Copyable & ~Escapable { public var neYieldedBorrow: lifetime_depend_infer.NE { @lifetime(borrow self) _read - @lifetime(borrow self) + @lifetime(&self) _modify } #endif @@ -346,7 +346,7 @@ public struct NonEscapableMutableSelf : ~Swift.Escapable { public mutating func mutatingMethodOneParamCopy(_: lifetime_depend_infer.NE) #endif #if $LifetimeDependence - @lifetime(borrow self) + @lifetime(&self) public mutating func mutatingMethodOneParamBorrow(_: lifetime_depend_infer.NE) #endif } diff --git a/test/Sema/lifetime_attr.swift b/test/Sema/lifetime_attr.swift index 4c31c7f6ec784..51bb6e59f30ad 100644 --- a/test/Sema/lifetime_attr.swift +++ b/test/Sema/lifetime_attr.swift @@ -63,3 +63,9 @@ do { // rdar://146401190 ([nonescapable] implement non-inout parameter dependencies) @lifetime(span: borrow holder) func testParameterDep(holder: AnyObject, span: Span) {} // expected-error{{lifetime-dependent parameter must be 'inout'}} + +@lifetime(&ne) +func inoutLifetimeDependence(_ ne: inout NE) -> NE { + ne +} + diff --git a/test/Sema/lifetime_depend_infer.swift b/test/Sema/lifetime_depend_infer.swift index 631242a0685e7..ede6a1a2ca0a9 100644 --- a/test/Sema/lifetime_depend_infer.swift +++ b/test/Sema/lifetime_depend_infer.swift @@ -137,7 +137,7 @@ struct EscapableNonTrivialSelf { @lifetime(copy self) // expected-error{{cannot copy the lifetime of an Escapable type, use '@lifetime(borrow self)' instead}} mutating func mutatingMethodNoParamCopy() -> NEImmortal { NEImmortal() } - @lifetime(borrow self) + @lifetime(&self) mutating func mutatingMethodNoParamBorrow() -> NEImmortal { NEImmortal() } func methodOneParam(_: Int) -> NEImmortal { NEImmortal() } // expected-error{{a method with a ~Escapable result requires '@lifetime(...)'}} @@ -159,7 +159,7 @@ struct EscapableNonTrivialSelf { @lifetime(copy self) // expected-error{{cannot copy the lifetime of an Escapable type, use '@lifetime(borrow self)' instead}} mutating func mutatingMethodOneParamCopy(_: Int) -> NEImmortal { NEImmortal() } - @lifetime(borrow self) + @lifetime(&self) mutating func mutatingMethodOneParamBorrow(_: Int) -> NEImmortal { NEImmortal() } } @@ -224,7 +224,7 @@ func oneParamLifetime(c: C) -> NEImmortal { NEImmortal() } func oneParamConsume(c: consuming C) -> NEImmortal { NEImmortal() } // expected-error{{cannot borrow the lifetime of 'c', which has consuming ownership on a function}} -@lifetime(c) // expected-error{{invalid use of borrow dependence with consuming ownership}} +@lifetime(c) // expected-error{{invalid lifetime dependence on an Escapable value with consuming ownership}} func oneParamConsumeLifetime(c: consuming C) -> NEImmortal { NEImmortal() } func oneParamBorrow(c: borrowing C) -> NEImmortal { NEImmortal() } // OK @@ -424,7 +424,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { yield ne } - @lifetime(borrow self) + @lifetime(&self) _modify { yield &ne } @@ -484,7 +484,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { ne } - @lifetime(borrow self) + @lifetime(&self) set { ne = newValue } @@ -496,7 +496,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { yield ne } - @lifetime(borrow self) + @lifetime(&self) _modify { yield &ne } diff --git a/test/Sema/lifetime_depend_infer_lazy.swift b/test/Sema/lifetime_depend_infer_lazy.swift index 12b92d69f19c2..d717e754515ec 100644 --- a/test/Sema/lifetime_depend_infer_lazy.swift +++ b/test/Sema/lifetime_depend_infer_lazy.swift @@ -106,7 +106,7 @@ struct EscapableNonTrivialSelf { @lifetime(self) mutating func mutatingMethodNoParamLifetime() -> NEImmortal { NEImmortal() } - @lifetime(borrow self) + @lifetime(&self) mutating func mutatingMethodNoParamBorrow() -> NEImmortal { NEImmortal() } func methodOneParam(_: Int) -> NEImmortal { NEImmortal() } // OK @@ -122,7 +122,7 @@ struct EscapableNonTrivialSelf { @lifetime(self) // OK mutating func mutatingMethodOneParamLifetime(_: Int) -> NEImmortal { NEImmortal() } - @lifetime(borrow self) // OK + @lifetime(&self) // OK mutating func mutatingMethodOneParamBorrow(_: Int) -> NEImmortal { NEImmortal() } } @@ -351,7 +351,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { yield ne } - @lifetime(borrow self) + @lifetime(&self) _modify { yield &ne } @@ -411,7 +411,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { ne } - @lifetime(borrow self) + @lifetime(&self) set { ne = newValue } @@ -423,7 +423,7 @@ struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { yield ne } - @lifetime(borrow self) + @lifetime(&self) _modify { yield &ne } diff --git a/test/Serialization/Inputs/def_explicit_lifetime_dependence.swift b/test/Serialization/Inputs/def_explicit_lifetime_dependence.swift index 05094ef6e9689..7987999b93b53 100644 --- a/test/Serialization/Inputs/def_explicit_lifetime_dependence.swift +++ b/test/Serialization/Inputs/def_explicit_lifetime_dependence.swift @@ -72,7 +72,7 @@ public struct Wrapper : ~Escapable { _read { yield _view } - @lifetime(borrow self) + @lifetime(&self) _modify { yield &_view } diff --git a/test/Serialization/Inputs/def_implicit_lifetime_dependence.swift b/test/Serialization/Inputs/def_implicit_lifetime_dependence.swift index 8a327ce4fdbdf..fe8577c255d07 100644 --- a/test/Serialization/Inputs/def_implicit_lifetime_dependence.swift +++ b/test/Serialization/Inputs/def_implicit_lifetime_dependence.swift @@ -62,7 +62,7 @@ public struct Wrapper : ~Escapable { _read { yield _view } - @lifetime(borrow self) + @lifetime(&self) _modify { yield &_view } From f4a980d62d4b172fb796276eaca52744a67237ba Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 2 Apr 2025 15:27:23 -0700 Subject: [PATCH 6/8] Update stdlib --- stdlib/public/core/Array.swift | 2 +- stdlib/public/core/ArraySlice.swift | 2 +- stdlib/public/core/ContiguousArray.swift | 2 +- stdlib/public/core/LifetimeManager.swift | 2 +- stdlib/public/core/Span/MutableRawSpan.swift | 20 ++++++++++---------- stdlib/public/core/Span/MutableSpan.swift | 18 +++++++++--------- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/stdlib/public/core/Array.swift b/stdlib/public/core/Array.swift index ccec5d1c45a10..e0798e8597782 100644 --- a/stdlib/public/core/Array.swift +++ b/stdlib/public/core/Array.swift @@ -1732,7 +1732,7 @@ extension Array { @available(SwiftStdlib 6.2, *) public var mutableSpan: MutableSpan { - @lifetime(/*inout*/borrow self) + @lifetime(&self) @_alwaysEmitIntoClient mutating get { _makeMutableAndUnique() diff --git a/stdlib/public/core/ArraySlice.swift b/stdlib/public/core/ArraySlice.swift index 886869b1ec796..282830234f997 100644 --- a/stdlib/public/core/ArraySlice.swift +++ b/stdlib/public/core/ArraySlice.swift @@ -1302,7 +1302,7 @@ extension ArraySlice { @available(SwiftStdlib 6.2, *) public var mutableSpan: MutableSpan { - @lifetime(/*inout*/borrow self) + @lifetime(/*inout*/&self) @_alwaysEmitIntoClient mutating get { _makeMutableAndUnique() diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index 13b50082e7556..6537d818ab188 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -1244,7 +1244,7 @@ extension ContiguousArray { @available(SwiftStdlib 6.2, *) public var mutableSpan: MutableSpan { - @lifetime(/*inout*/borrow self) + @lifetime(&self) @_alwaysEmitIntoClient mutating get { _makeMutableAndUnique() diff --git a/stdlib/public/core/LifetimeManager.swift b/stdlib/public/core/LifetimeManager.swift index 34c8aefbb7712..eeaeba32b2484 100644 --- a/stdlib/public/core/LifetimeManager.swift +++ b/stdlib/public/core/LifetimeManager.swift @@ -325,7 +325,7 @@ internal func _overrideLifetime< @_unsafeNonescapableResult @_alwaysEmitIntoClient @_transparent -@lifetime(borrow source) +@lifetime(&source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( diff --git a/stdlib/public/core/Span/MutableRawSpan.swift b/stdlib/public/core/Span/MutableRawSpan.swift index 056a5f9f912e8..2d1aa6776064d 100644 --- a/stdlib/public/core/Span/MutableRawSpan.swift +++ b/stdlib/public/core/Span/MutableRawSpan.swift @@ -190,7 +190,7 @@ extension MutableRawSpan { @unsafe @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) public mutating func _unsafeMutableView( as type: T.Type ) -> MutableSpan { @@ -448,7 +448,7 @@ extension MutableRawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(_ bounds: Range) -> Self { _precondition( UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && @@ -475,7 +475,7 @@ extension MutableRawSpan { /// - Complexity: O(1) @unsafe @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(unchecked bounds: Range) -> Self { let newStart = unsafe _pointer?.advanced(by: bounds.lowerBound) let newSpan = unsafe Self(_unchecked: newStart, byteCount: bounds.count) @@ -496,7 +496,7 @@ extension MutableRawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting( _ bounds: some RangeExpression ) -> Self { @@ -520,7 +520,7 @@ extension MutableRawSpan { /// - Complexity: O(1) @unsafe @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(unchecked bounds: ClosedRange) -> Self { let range = unsafe Range( _uncheckedBounds: (bounds.lowerBound, bounds.upperBound+1) @@ -538,7 +538,7 @@ extension MutableRawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(_: UnboundedRange) -> Self { let newSpan = unsafe Self(_unchecked: _start(), byteCount: _count) return unsafe _overrideLifetime(newSpan, mutating: &self) @@ -565,7 +565,7 @@ extension MutableRawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(first maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, byteCount) @@ -588,7 +588,7 @@ extension MutableRawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(droppingLast k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, byteCount) @@ -613,7 +613,7 @@ extension MutableRawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(last maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, byteCount) @@ -637,7 +637,7 @@ extension MutableRawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(droppingFirst k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of bytes") let droppedCount = min(k, byteCount) diff --git a/stdlib/public/core/Span/MutableSpan.swift b/stdlib/public/core/Span/MutableSpan.swift index c9f444c434f1f..b778a9b6fe2e0 100644 --- a/stdlib/public/core/Span/MutableSpan.swift +++ b/stdlib/public/core/Span/MutableSpan.swift @@ -666,7 +666,7 @@ extension MutableSpan where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(_ bounds: Range) -> Self { _precondition( UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && @@ -693,7 +693,7 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @unsafe @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(unchecked bounds: Range) -> Self { let delta = bounds.lowerBound &* MemoryLayout.stride let newStart = unsafe _pointer?.advanced(by: delta) @@ -715,7 +715,7 @@ extension MutableSpan where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting( _ bounds: some RangeExpression ) -> Self { @@ -739,7 +739,7 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @unsafe @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting( unchecked bounds: ClosedRange ) -> Self { @@ -759,7 +759,7 @@ extension MutableSpan where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(_: UnboundedRange) -> Self { let newSpan = unsafe Self(_unchecked: _start(), count: _count) return unsafe _overrideLifetime(newSpan, mutating: &self) @@ -786,7 +786,7 @@ extension MutableSpan where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(first maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, count) @@ -809,7 +809,7 @@ extension MutableSpan where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(droppingLast k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) @@ -834,7 +834,7 @@ extension MutableSpan where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(last maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, count) @@ -859,7 +859,7 @@ extension MutableSpan where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(borrow self) + @lifetime(&self) mutating public func extracting(droppingFirst k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) From 493f0b782e114a79de410d726b7ea3e319ba993f Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 2 Apr 2025 15:27:29 -0700 Subject: [PATCH 7/8] Introduce InoutLifetimeDependence feature --- include/swift/Basic/Features.def | 3 +++ lib/AST/FeatureSet.cpp | 12 ++++++++++++ stdlib/cmake/modules/SwiftSource.cmake | 1 + 3 files changed, 16 insertions(+) diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 2f889e2fefc7b..42aee05e6109a 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -416,6 +416,9 @@ EXPERIMENTAL_FEATURE(StructLetDestructuring, true) /// Enable returning non-escapable types from functions. EXPERIMENTAL_FEATURE(LifetimeDependence, true) +/// Enable inout lifetime dependence - @lifetime(&arg) +EXPERIMENTAL_FEATURE(InoutLifetimeDependence, true) + /// Enable the `@_staticExclusiveOnly` attribute. EXPERIMENTAL_FEATURE(StaticExclusiveOnly, true) diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 2197edead58e8..f876293661a9d 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -271,6 +271,18 @@ static bool usesFeatureLifetimeDependence(Decl *decl) { return false; } +static bool usesFeatureInoutLifetimeDependence(Decl *decl) { + for (auto attr : decl->getAttrs().getAttributes()) { + for (auto source : attr->getLifetimeEntry()->getSources()) { + if (source.getParsedLifetimeDependenceKind() == + ParsedLifetimeDependenceKind::Inout) { + return true; + } + } + } + return false; +} + UNINTERESTING_FEATURE(DynamicActorIsolation) UNINTERESTING_FEATURE(NonfrozenEnumExhaustivity) UNINTERESTING_FEATURE(ClosureIsolation) diff --git a/stdlib/cmake/modules/SwiftSource.cmake b/stdlib/cmake/modules/SwiftSource.cmake index 6f85621f62a91..a67ce5d14d200 100644 --- a/stdlib/cmake/modules/SwiftSource.cmake +++ b/stdlib/cmake/modules/SwiftSource.cmake @@ -631,6 +631,7 @@ function(_compile_swift_files list(APPEND swift_flags "-enable-experimental-feature" "NonescapableTypes") list(APPEND swift_flags "-enable-experimental-feature" "LifetimeDependence") + list(APPEND swift_flags "-enable-experimental-feature" "InoutLifetimeDependence") list(APPEND swift_flags "-enable-upcoming-feature" "MemberImportVisibility") From 88ce18eb8bd5a3858dd892e59c703f7fff5f038e Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Thu, 3 Apr 2025 17:14:14 -0700 Subject: [PATCH 8/8] [NFC] Reorganize getDependenceKindFromDescriptor --- lib/AST/LifetimeDependence.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 9a2790293889a..d64e3d2576046 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -609,19 +609,20 @@ class LifetimeDependenceChecker { ? ownership : getLoweredOwnership(afd); - if (parsedLifetimeKind == ParsedLifetimeDependenceKind::Default) { + switch (parsedLifetimeKind) { + case ParsedLifetimeDependenceKind::Default: { if (type->isEscapable()) { if (loweredOwnership == ValueOwnership::Shared || loweredOwnership == ValueOwnership::InOut) { return LifetimeDependenceKind::Scope; - } else { - diagnose( - loc, - diag::lifetime_dependence_cannot_use_default_escapable_consuming, - getOwnershipSpelling(loweredOwnership)); - return std::nullopt; } - } else if (useLazyInference()) { + diagnose( + loc, + diag::lifetime_dependence_cannot_use_default_escapable_consuming, + getOwnershipSpelling(loweredOwnership)); + return std::nullopt; + } + if (useLazyInference()) { return LifetimeDependenceKind::Inherit; } diagnose(loc, diag::lifetime_dependence_cannot_infer_kind, @@ -629,6 +630,8 @@ class LifetimeDependenceChecker { return std::nullopt; } + case ParsedLifetimeDependenceKind::Borrow: LLVM_FALLTHROUGH; + case ParsedLifetimeDependenceKind::Inout: { // @lifetime(borrow x) is valid only for borrowing parameters. // @lifetime(inout x) is valid only for inout parameters. if (!isCompatibleWithOwnership(parsedLifetimeKind, type, @@ -639,16 +642,17 @@ class LifetimeDependenceChecker { getOwnershipSpelling(loweredOwnership)); return std::nullopt; } + return LifetimeDependenceKind::Scope; + } + case ParsedLifetimeDependenceKind::Inherit: // @lifetime(copy x) is only invalid for Escapable types. - if (parsedLifetimeKind == ParsedLifetimeDependenceKind::Inherit && - type->isEscapable()) { + if (type->isEscapable()) { diagnose(loc, diag::lifetime_dependence_invalid_inherit_escapable_type, descriptor.getString()); return std::nullopt; } - return parsedLifetimeKind == ParsedLifetimeDependenceKind::Inherit - ? LifetimeDependenceKind::Inherit - : LifetimeDependenceKind::Scope; + return LifetimeDependenceKind::Inherit; + } } // Finds the ParamDecl* and its index from a LifetimeDescriptor