diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift index 13db38c7f8088..9c56a55d0bfa3 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift @@ -272,6 +272,9 @@ private func insertParameterDependencies(apply: LifetimeDependentApply, target: sources.initializeBases(context) + assert(target.value.type.isAddress, + "lifetime-dependent parameter must be 'inout'") + Builder.insert(after: apply.applySite, context) { insertMarkDependencies(value: target.value, initializer: nil, bases: sources.bases, builder: $0, context) } @@ -285,14 +288,14 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?, let markDep = builder.createMarkDependence( value: currentValue, base: base, kind: .Unresolved) - // Address dependencies cannot be represented as SSA values, so it does not make sense to replace any uses of the - // dependent address. - // - // TODO: either (1) insert a separate mark_dependence_addr instruction with no return value, or (2) perform data - // flow to replace all reachable address uses, and if any aren't dominated by base, then insert an extra - // escaping mark_dependence at this apply site that directly uses the mark_dependence [nonescaping] to force - // diagnostics to fail. - if !value.type.isAddress { + if value.type.isAddress { + // Address dependencies cannot be represented as SSA values, so it does not make sense to replace any uses of the + // dependent address. + // + // TODO: insert a separate mark_dependence_addr instruction with no return value and do not update currentValue. + } else { + // TODO: implement non-inout parameter dependencies. This assumes that currentValue is the apply immediately + // preceeding the mark_dependence. let uses = currentValue.uses.lazy.filter { if $0.isScopeEndingUse { return false diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index c2ba72687496e..59002ebc84d55 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8089,7 +8089,7 @@ ERROR(pack_iteration_where_clause_not_supported, none, //------------------------------------------------------------------------------ -// MARK: Lifetime Dependence Diagnostics +// MARK: Lifetime Dependence Syntax //------------------------------------------------------------------------------ ERROR(lifetime_dependence_invalid_param_name, none, @@ -8108,34 +8108,15 @@ ERROR(lifetime_dependence_cannot_use_kind, none, ERROR(lifetime_dependence_cannot_use_parsed_scoped_consuming, none, "invalid use of scoped lifetime dependence with consuming ownership", ()) -ERROR(lifetime_dependence_cannot_use_inferred_scoped_consuming, none, - "invalid use of lifetime dependence on an Escapable parameter with " - "consuming ownership", - ()) -ERROR(lifetime_dependence_invalid_self_ownership, none, - "invalid scoped lifetime dependence on an Escapable self with consuming " - "ownership", - ()) ERROR(lifetime_dependence_only_on_function_method_init_result, none, "lifetime dependence specifiers may only be used on result of " "functions, methods, initializers", ()) -ERROR(lifetime_dependence_invalid_type, none, - "lifetime dependence can only be specified on ~Escapable types", ()) -ERROR(lifetime_dependence_cannot_infer_ambiguous_candidate, none, - "cannot infer lifetime dependence %0, multiple parameters qualifiy as a candidate", (StringRef)) - ERROR(lifetime_dependence_cannot_infer_no_candidates, none, - "cannot infer lifetime dependence%0, no parameters found that are either " - "~Escapable or Escapable with a borrowing ownership", (StringRef)) - ERROR(lifetime_dependence_ctor_non_self_or_nil_return, none, - "expected nil or self as return values in an initializer with " - "lifetime dependent specifiers", - ()) - ERROR(lifetime_dependence_cannot_be_applied_to_tuple_elt, none, - "lifetime dependence specifiers cannot be applied to tuple elements", ()) - ERROR(lifetime_dependence_method_escapable_bitwisecopyable_self, none, - "cannot infer lifetime dependence on a self which is BitwiseCopyable & " - "Escapable", +ERROR(lifetime_dependence_ctor_non_self_or_nil_return, none, + "expected 'nil' or 'self' as return values in an initializer with " + "lifetime dependent specifiers", ()) +ERROR(lifetime_dependence_cannot_be_applied_to_tuple_elt, none, + "lifetime dependence specifiers cannot be applied to tuple elements", ()) ERROR(lifetime_dependence_immortal_conflict_name, none, "conflict between the parameter name and 'immortal' contextual keyword", ()) ERROR(lifetime_dependence_function_type, none, @@ -8144,18 +8125,75 @@ ERROR(lifetime_dependence_function_type, none, ERROR(lifetime_dependence_immortal_alone, none, "cannot specify any other dependence source along with immortal", ()) ERROR(lifetime_dependence_invalid_inherit_escapable_type, none, - "invalid lifetime dependence on a source of Escapable type, use borrow " - "dependence instead", - ()) + "cannot copy the lifetime of an Escapable type, use " + "'@lifetime(borrow %0)' instead", + (StringRef)) ERROR(lifetime_dependence_cannot_use_parsed_borrow_consuming, none, "invalid use of borrow dependence with consuming ownership", ()) +ERROR(lifetime_dependence_cannot_use_parsed_borrow_inout, none, + "invalid use of borrow dependence on the same inout parameter", + ()) ERROR(lifetime_dependence_duplicate_target, none, "invalid duplicate target lifetime dependencies on function", ()) +ERROR(lifetime_parameter_requires_inout, none, + "lifetime-dependent parameter must be 'inout'", (Identifier)) + +//------------------------------------------------------------------------------ +// MARK: Lifetime Dependence Requirements +//------------------------------------------------------------------------------ + +ERROR(lifetime_dependence_feature_required_return, none, + "%0 with a ~Escapable result requires " + "'-enable-experimental-feature LifetimeDependence'", (StringRef)) +ERROR(lifetime_dependence_feature_required_mutating, none, + "%0 with ~Escapable 'self' requires " + "'-enable-experimental-feature LifetimeDependence'", (StringRef)) +ERROR(lifetime_dependence_feature_required_inout, none, + "%0 with a ~Escapable 'inout' parameter requires " + "'-enable-experimental-feature LifetimeDependence'", + // arg list is interchangable with lifetime_dependence_cannot_infer_inout + (StringRef, Identifier)) + +ERROR(lifetime_dependence_cannot_infer_return, none, + "%0 with a ~Escapable result requires '@lifetime(...)'", (StringRef)) +ERROR(lifetime_dependence_cannot_infer_mutating, none, + "%0 with a ~Escapable 'self' requires '@lifetime(self: ...)'", (StringRef)) +ERROR(lifetime_dependence_cannot_infer_inout, none, + "%0 with a ~Escapable 'inout' parameter requires '@lifetime(%1: ...)'", + (StringRef, Identifier)) + +//------------------------------------------------------------------------------ +// MARK: Lifetime Dependence Inference - refinements to the requirements above +//------------------------------------------------------------------------------ -ERROR(lifetime_dependence_feature_required, none, - "returning ~Escapable type requires '-enable-experimental-feature " - "LifetimeDependence'", ()) +ERROR(lifetime_dependence_cannot_infer_return_no_param, none, + "%0 with a ~Escapable result needs a parameter to depend on", + (StringRef)) +NOTE(lifetime_dependence_cannot_infer_return_immortal, none, + "'@lifetime(immortal)' can be used to indicate that values produced by " + "this initializer have no lifetime dependencies", ()) +ERROR(lifetime_dependence_cannot_infer_bitwisecopyable, none, + "cannot infer lifetime dependence on %0 because '%1' is BitwiseCopyable, " + "specify '@lifetime(borrow self)'", + (StringRef, StringRef)) +ERROR(lifetime_dependence_cannot_infer_kind, none, + "cannot infer the lifetime dependence scope on %0 with a ~Escapable " + "parameter, specify '@lifetime(borrow %1)' or '@lifetime(copy %1)'", + (StringRef, StringRef)) +ERROR(lifetime_dependence_cannot_infer_scope_ownership, none, + "cannot borrow the lifetime of '%0', which has consuming ownership on %1", + (StringRef, StringRef)) + +//------------------------------------------------------------------------------ +// MARK: Lifetime Dependence Experimental Inference +//------------------------------------------------------------------------------ + +ERROR(lifetime_dependence_cannot_infer_no_candidates, none, + "cannot infer lifetime dependence%0, no parameters found that are either " + "~Escapable or Escapable with a borrowing ownership", (StringRef)) +ERROR(lifetime_dependence_cannot_infer_ambiguous_candidate, none, + "cannot infer lifetime dependence%0, multiple parameters qualify as a candidate", (StringRef)) //===----------------------------------------------------------------------===// // MARK: Sending diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index 63ba7a5cdd906..d768c16327710 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -206,9 +206,15 @@ class LifetimeEntry final if (!firstElem) { result += ", "; } - if (source.getParsedLifetimeDependenceKind() == - ParsedLifetimeDependenceKind::Scope) { + switch (source.getParsedLifetimeDependenceKind()) { + case ParsedLifetimeDependenceKind::Scope: result += "borrow "; + break; + case ParsedLifetimeDependenceKind::Inherit: + result += "copy "; + break; + default: + break; } result += source.getString(); firstElem = false; @@ -227,32 +233,6 @@ class LifetimeDependenceInfo { unsigned targetIndex; - static LifetimeDependenceInfo getForIndex(AbstractFunctionDecl *afd, - unsigned targetIndex, - unsigned sourceIndex, - LifetimeDependenceKind kind); - - /// Builds LifetimeDependenceInfo from @lifetime attribute - static std::optional> - fromLifetimeAttribute(AbstractFunctionDecl *afd); - - /// Infer LifetimeDependenceInfo on result - static std::optional infer(AbstractFunctionDecl *afd); - - /// Infer LifetimeDependenceInfo on setter - static std::optional - inferSetter(AbstractFunctionDecl *afd); - - /// Infer LifetimeDependenceInfo on mutating self - static std::optional - inferMutatingSelf(AbstractFunctionDecl *afd); - - /// Builds LifetimeDependenceInfo from SIL function type - static std::optional - fromDependsOn(LifetimeDependentTypeRepr *lifetimeDependentRepr, - unsigned targetIndex, ArrayRef params, - DeclContext *dc); - public: LifetimeDependenceInfo(IndexSubset *inheritLifetimeParamIndices, IndexSubset *scopeLifetimeParamIndices, @@ -350,8 +330,8 @@ class LifetimeDependenceInfo { /// Builds LifetimeDependenceInfo from SIL static std::optional> - get(FunctionTypeRepr *funcRepr, ArrayRef params, - ArrayRef results, DeclContext *dc); + getFromSIL(FunctionTypeRepr *funcRepr, ArrayRef params, + ArrayRef results, DeclContext *dc); bool operator==(const LifetimeDependenceInfo &other) const { return this->isImmortal() == other.isImmortal() && diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index e19ddbb993b53..f7f143db3df37 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -5574,6 +5574,8 @@ class SILFunctionType final return NumLifetimeDependencies != 0; } + // Return lowered lifetime dependencies, which has remapped parameter indices + // relative to the original FunctionType. ArrayRef getLifetimeDependencies() const { if (!hasLifetimeDependencies()) return std::nullopt; diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 54f65fc462e44..0ab9297ad68f6 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -598,7 +598,7 @@ namespace swift { bool EnableRequirementMachineOpaqueArchetypes = false; /// Enable implicit lifetime dependence for ~Escapable return types. - bool EnableExperimentalLifetimeDependenceInference = true; + bool EnableExperimentalLifetimeDependenceInference = false; /// Skips decls that cannot be referenced externally. bool SkipNonExportableDecls = false; diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 505d39d2b9fd3..2f8bb0d1372f6 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -12,6 +12,7 @@ #include "swift/AST/LifetimeDependence.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/Builtins.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsSema.h" @@ -113,8 +114,10 @@ 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 -getLifetimeDependenceKindFromType(Type sourceType) { +inferLifetimeDependenceKindFromType(Type sourceType) { if (sourceType->isEscapable()) { return LifetimeDependenceKind::Scope; } @@ -145,52 +148,18 @@ static bool isBitwiseCopyable(Type type, ASTContext &ctx) { return (bool)checkConformance(type, bitwiseCopyableProtocol); } -static bool -isLifetimeDependenceCompatibleWithOwnership(LifetimeDependenceKind kind, - Type type, ValueOwnership ownership, - AbstractFunctionDecl *afd) { - auto &ctx = afd->getASTContext(); - if (kind == LifetimeDependenceKind::Inherit) { - return true; - } - // Lifetime dependence always propagates through temporary BitwiseCopyable - // values, even if the dependence is scoped. - 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) { - return true; - } - assert(loweredOwnership == ValueOwnership::Owned); - return false; -} - -LifetimeDependenceInfo -LifetimeDependenceInfo::getForIndex(AbstractFunctionDecl *afd, - unsigned targetIndex, unsigned sourceIndex, - LifetimeDependenceKind kind) { - auto *dc = afd->getDeclContext(); - auto &ctx = dc->getASTContext(); - unsigned capacity = afd->hasImplicitSelfDecl() - ? (afd->getParameters()->size() + 1) - : afd->getParameters()->size(); - auto indexSubset = IndexSubset::get(ctx, capacity, {sourceIndex}); - if (kind == LifetimeDependenceKind::Scope) { - return LifetimeDependenceInfo{/*inheritLifetimeParamIndices*/ nullptr, - /*scopeLifetimeParamIndices*/ indexSubset, - targetIndex, - /*isImmortal*/ false}; - } - return LifetimeDependenceInfo{/*inheritLifetimeParamIndices*/ indexSubset, - /*scopeLifetimeParamIndices*/ nullptr, - targetIndex, - /*isImmortal*/ false}; +static bool isDiagnosedNonEscapable(Type type) { + if (type->hasError()) { + return false; + } + // FIXME: This check is temporary until rdar://139976667 is fixed. + // ModuleType created with ModuleType::get methods are ~Copyable and + // ~Escapable because the Copyable and Escapable conformance is not added to + // them by default. + if (type->is()) { + return false; + } + return !type->isEscapable(); } void LifetimeDependenceInfo::getConcatenatedData( @@ -220,545 +189,999 @@ void LifetimeDependenceInfo::getConcatenatedData( } } -static Type getResultOrYield(AbstractFunctionDecl *afd) { - if (auto *accessor = dyn_cast(afd)) { - if (accessor->isCoroutine()) { - auto yieldTyInContext = accessor->mapTypeIntoContext( - accessor->getStorage()->getValueInterfaceType()); - return yieldTyInContext; - } - } - Type resultType; - if (auto fn = dyn_cast(afd)) { - resultType = fn->getResultInterfaceType(); - } else { - auto ctor = cast(afd); - resultType = ctor->getResultInterfaceType(); - } - return afd->mapTypeIntoContext(resultType); -} +class LifetimeDependenceChecker { + AbstractFunctionDecl *afd; -static bool hasEscapableResultOrYield(AbstractFunctionDecl *afd) { - auto resultType = getResultOrYield(afd); - // FIXME: This check is temporary until rdar://139976667 is fixed. - // ModuleType created with ModuleType::get methods are ~Copyable and - // ~Escapable because the Copyable and Escapable conformance is not added to - // them by default. - if (resultType->is()) { - return true; + DeclContext *dc; + ASTContext &ctx; + + SourceLoc returnLoc; + + // Only initialized when hasImplicitSelfDecl() is true. + unsigned selfIndex = ~0; + + // 'resultIndex' is a pseudo-parameter-index used by LifetimeDependenceInfo to + // represent the function result. + unsigned resultIndex = ~0; + + SmallVector lifetimeDependencies; + + // True if lifetime diganostics have already been performed. Avoids redundant + // diagnostics, and allows bypassing diagnostics for special cases. + bool performedDiagnostics = false; + +public: + LifetimeDependenceChecker(AbstractFunctionDecl *afd): + afd(afd), dc(afd->getDeclContext()), ctx(dc->getASTContext()) + { + auto resultTypeRepr = afd->getResultTypeRepr(); + returnLoc = resultTypeRepr ? resultTypeRepr->getLoc() : afd->getLoc(); + + if (afd->hasImplicitSelfDecl()) { + selfIndex = afd->getParameters()->size(); + resultIndex = selfIndex + 1; + } else { + resultIndex = afd->getParameters()->size(); + } } - return resultType->isEscapable(); -} -static std::optional -getLifetimeDependenceKind(LifetimeDescriptor descriptor, - AbstractFunctionDecl *afd, ParamDecl *decl) { - auto &ctx = afd->getASTContext(); - auto &diags = ctx.Diags; - auto loc = descriptor.getLoc(); - - auto ownership = decl->getValueOwnership(); - auto type = decl->getTypeInContext(); - - // For @lifetime attribute, we check if we had a "borrow" modifier, if not - // we infer inherit dependence. - auto parsedLifetimeKind = descriptor.getParsedLifetimeDependenceKind(); - if (parsedLifetimeKind == ParsedLifetimeDependenceKind::Scope) { - bool isCompatible = isLifetimeDependenceCompatibleWithOwnership( - LifetimeDependenceKind::Scope, type, ownership, afd); - if (!isCompatible) { - diags.diagnose( - loc, diag::lifetime_dependence_cannot_use_parsed_borrow_consuming); + std::optional> + currentDependencies() const { + if (lifetimeDependencies.empty()) { return std::nullopt; } - return LifetimeDependenceKind::Scope; - } - if (type->isEscapable()) { - diags.diagnose(loc, - diag::lifetime_dependence_invalid_inherit_escapable_type); - return std::nullopt; + return afd->getASTContext().AllocateCopy(lifetimeDependencies); } - return LifetimeDependenceKind::Inherit; -} -// Finds the ParamDecl* and its index from a LifetimeDescriptor -static std::optional> -getParamDeclFromDescriptor(AbstractFunctionDecl *afd, - LifetimeDescriptor descriptor) { - auto *dc = afd->getDeclContext(); - auto &ctx = dc->getASTContext(); - auto &diags = ctx.Diags; - switch (descriptor.getDescriptorKind()) { - case LifetimeDescriptor::DescriptorKind::Named: { - unsigned paramIndex = 0; - ParamDecl *candidateParam = nullptr; - for (auto *param : *afd->getParameters()) { - if (param->getParameterName() == descriptor.getName()) { - candidateParam = param; - break; - } - paramIndex++; + std::optional> checkFuncDecl() { + assert(isa(afd) || isa(afd)); + assert(lifetimeDependencies.empty()); + + // Handle Builtins first because, even though Builtins require + // LifetimeDependence, we don't force Feature::LifetimeDependence + // to be enabled when importing the Builtin module. + if (afd->isImplicit() && afd->getModuleContext()->isBuiltinModule()) { + inferBuiltin(); + return currentDependencies(); } - if (!candidateParam) { - diags.diagnose(descriptor.getLoc(), - diag::lifetime_dependence_invalid_param_name, - descriptor.getName()); + + if (!ctx.LangOpts.hasFeature(Feature::LifetimeDependence) + && !ctx.SourceMgr.isImportMacroGeneratedLoc(returnLoc)) { + diagnoseMissingResultDependencies( + diag::lifetime_dependence_feature_required_return.ID); + diagnoseMissingSelfDependencies( + diag::lifetime_dependence_feature_required_mutating.ID); + diagnoseMissingInoutDependencies( + diag::lifetime_dependence_feature_required_inout.ID); return std::nullopt; } - return std::make_pair(candidateParam, paramIndex); - } - case LifetimeDescriptor::DescriptorKind::Ordered: { - auto paramIndex = descriptor.getIndex(); - if (paramIndex >= afd->getParameters()->size()) { - diags.diagnose(descriptor.getLoc(), - diag::lifetime_dependence_invalid_param_index, paramIndex); - return std::nullopt; + + if (afd->getAttrs().hasAttribute()) { + return checkAttribute(); } - auto candidateParam = afd->getParameters()->get(paramIndex); - return std::make_pair(candidateParam, paramIndex); - } - case LifetimeDescriptor::DescriptorKind::Self: { - if (!afd->hasImplicitSelfDecl()) { - diags.diagnose(descriptor.getLoc(), - diag::lifetime_dependence_invalid_self_in_static); + // Methods or functions with @_unsafeNonescapableResult do not require + // lifetime annotation and do not infer any lifetime dependency. + if (afd->getAttrs().hasAttribute()) { return std::nullopt; } - if (isa(afd)) { - diags.diagnose(descriptor.getLoc(), - diag::lifetime_dependence_invalid_self_in_init); - return std::nullopt; + + inferOrDiagnose(); + + // If precise diagnostics were already issued, bypass + // diagnoseMissingDependencies to avoid redundant diagnostics. + if (!performedDiagnostics) { + diagnoseMissingResultDependencies( + diag::lifetime_dependence_cannot_infer_return.ID); + diagnoseMissingSelfDependencies( + diag::lifetime_dependence_cannot_infer_mutating.ID); + diagnoseMissingInoutDependencies( + diag::lifetime_dependence_cannot_infer_inout.ID); } - auto *selfDecl = afd->getImplicitSelfDecl(); - return std::make_pair(selfDecl, afd->getParameters()->size()); + return currentDependencies(); } + +protected: + template + InFlightDiagnostic diagnose( + SourceLoc Loc, Diag ID, + typename detail::PassArgument::type... Args) { + performedDiagnostics = true; + return ctx.Diags.diagnose(Loc, ID, std::move(Args)...); } -} -static std::optional -populateLifetimeDependence(AbstractFunctionDecl *afd, LifetimeEntry *entry) { - auto *dc = afd->getDeclContext(); - auto &ctx = dc->getASTContext(); - auto &diags = ctx.Diags; - auto capacity = afd->hasImplicitSelfDecl() - ? (afd->getParameters()->size() + 1) - : afd->getParameters()->size(); - - SmallBitVector inheritIndices(capacity); - SmallBitVector scopeIndices(capacity); - - auto updateLifetimeIndices = [&](LifetimeDescriptor descriptor, - unsigned paramIndexToSet, - LifetimeDependenceKind lifetimeKind) { - if (inheritIndices.test(paramIndexToSet) || - scopeIndices.test(paramIndexToSet)) { - diags.diagnose(descriptor.getLoc(), - diag::lifetime_dependence_duplicate_param_id); + template + InFlightDiagnostic + diagnose(const Decl *decl, Diag id, + typename detail::PassArgument::type... args) { + return ctx.Diags.diagnose(decl, Diagnostic(id, std::move(args)...)); + } + + bool isInit() const { + return isa(afd); + } + + // For initializers, the implicit self parameter is ignored and instead shows + // up as the result type. + // + // Note: Do not use this to reserve the self parameter index. + // LifetimeDependenceInfo always reserves an extra formal parameter + // index for hasImplicitSelfDecl(), even for initializers. During function + // type lowering, it is mapped to the metatype parameter. Without reserving + // the extra formal self parameter, a dependency targeting the formal result + // index would incorrectly target the SIL metatype parameter. + bool hasImplicitSelfParam() const { + return !isInit() && afd->hasImplicitSelfDecl(); + } + + // In SIL, implicit initializers and accessors become explicit. + bool isImplicitOrSIL() const { + if (afd->isImplicit()) { return true; } - if (lifetimeKind == LifetimeDependenceKind::Inherit) { - inheritIndices.set(paramIndexToSet); - } else { - assert(lifetimeKind == LifetimeDependenceKind::Scope); - scopeIndices.set(paramIndexToSet); + // TODO: remove this check once SIL prints @lifetime. + if (auto *sf = afd->getParentSourceFile()) { + // The AST printer makes implicit initializers explicit, but does not + // print the @lifetime annotations. Until that is fixed, avoid + // diagnosing this as an error. + if (sf->Kind == SourceFileKind::SIL) { + return true; + } } return false; - }; + } - auto targetDescriptor = entry->getTargetDescriptor(); - unsigned targetIndex; - if (targetDescriptor.has_value()) { - auto targetDeclAndIndex = - getParamDeclFromDescriptor(afd, *targetDescriptor); - if (!targetDeclAndIndex.has_value()) { - return std::nullopt; + bool isInterfaceFile() const { + // TODO: remove this check once all compilers that are rev-locked to the + // stdlib print the 'copy' dependence kind in the interface (Aug '25) + if (auto *sf = afd->getParentSourceFile()) { + if (sf->Kind == SourceFileKind::SIL) { + return true; + } } - targetIndex = targetDeclAndIndex->second; - } else { - targetIndex = afd->hasImplicitSelfDecl() ? afd->getParameters()->size() + 1 - : afd->getParameters()->size(); + return false; } - for (auto source : entry->getSources()) { - if (source.isImmortal()) { - auto immortalParam = - std::find_if(afd->getParameters()->begin(), - afd->getParameters()->end(), [](ParamDecl *param) { - return strcmp(param->getName().get(), "immortal") == 0; - }); - if (immortalParam != afd->getParameters()->end()) { - diags.diagnose(*immortalParam, - diag::lifetime_dependence_immortal_conflict_name); - return std::nullopt; + bool useLazyInference() const { + return isInterfaceFile() + || ctx.LangOpts.EnableExperimentalLifetimeDependenceInference; + } + + std::string diagnosticQualifier() const { + if (afd->isImplicit()) { + if (isInit()) { + return "an implicit initializer"; + } + if (auto *ad = dyn_cast(afd)) { + std::string qualifier = "the '"; + qualifier += accessorKindName(ad->getAccessorKind()); + qualifier += "' accessor"; + return qualifier; } - return LifetimeDependenceInfo(nullptr, nullptr, targetIndex, - /*isImmortal*/ true); } - - auto paramDeclAndIndex = getParamDeclFromDescriptor(afd, source); - if (!paramDeclAndIndex.has_value()) { - return std::nullopt; + if (afd->hasImplicitSelfDecl()) { + if (isInit()) { + return "an initializer"; + } + if (afd->getImplicitSelfDecl()->isInOut()) { + return "a mutating method"; + } + return "a method"; } - auto lifetimeKind = - getLifetimeDependenceKind(source, afd, paramDeclAndIndex->first); - if (!lifetimeKind.has_value()) { - return std::nullopt; + return "a function"; + } + + // Ensure that dependencies exist for any return value or inout parameter that + // needs one. Always runs before the checker completes if no other diagnostics + // were issued. + void diagnoseMissingResultDependencies(DiagID diagID) { + if (!isDiagnosedNonEscapable(getResultOrYield())) { + return; } - bool hasError = - updateLifetimeIndices(source, paramDeclAndIndex->second, *lifetimeKind); - if (hasError) { - return std::nullopt; + if (llvm::none_of(lifetimeDependencies, + [&](LifetimeDependenceInfo dep) { + return dep.getTargetIndex() == resultIndex; + })) { + ctx.Diags.diagnose(returnLoc, diagID, + {StringRef(diagnosticQualifier())}); } } - return LifetimeDependenceInfo( - inheritIndices.any() ? IndexSubset::get(ctx, inheritIndices) : nullptr, - scopeIndices.any() ? IndexSubset::get(ctx, scopeIndices) : nullptr, - targetIndex, /*isImmortal*/ false); -} - -std::optional> -LifetimeDependenceInfo::fromLifetimeAttribute(AbstractFunctionDecl *afd) { - auto *dc = afd->getDeclContext(); - auto &ctx = dc->getASTContext(); - auto &diags = ctx.Diags; + // Ensure that dependencies exist for any mutating self value. Always runs + // before the checker completes if no other diagnostics were issued. For + // initializers, the inout self parameter is actually considered the result + // type so is not handled here. + void diagnoseMissingSelfDependencies(DiagID diagID) { + if (!hasImplicitSelfParam()) { + return; + } + auto *selfDecl = afd->getImplicitSelfDecl(); + if (!selfDecl->isInOut()) { + return; + } + if (!isDiagnosedNonEscapable(dc->getSelfTypeInContext())) { + return; + } + if (llvm::none_of(lifetimeDependencies, + [&](LifetimeDependenceInfo dep) { + return dep.getTargetIndex() == selfIndex; + })) { + ctx.Diags.diagnose(selfDecl->getLoc(), diagID, + {StringRef(diagnosticQualifier())}); + } + } - SmallVector lifetimeDependencies; - llvm::SmallSet lifetimeDependentTargets; - auto lifetimeAttrs = afd->getAttrs().getAttributes(); - for (auto attr : lifetimeAttrs) { - auto lifetimeDependenceInfo = - populateLifetimeDependence(afd, attr->getLifetimeEntry()); - if (!lifetimeDependenceInfo.has_value()) { - return std::nullopt; + void diagnoseMissingInoutDependencies(DiagID diagID) { + unsigned paramIndex = 0; + for (auto *param : *afd->getParameters()) { + SWIFT_DEFER { paramIndex++; }; + if (!param->isInOut()) { + continue; + } + if (!isDiagnosedNonEscapable( + afd->mapTypeIntoContext(param->getInterfaceType()))) { + continue; + } + if (llvm::none_of(lifetimeDependencies, + [&](LifetimeDependenceInfo dep) { + return dep.getTargetIndex() == paramIndex; + })) { + ctx.Diags.diagnose(param->getLoc(), diagID, + {StringRef(diagnosticQualifier()), + param->getName()}); + } + } + } + + bool isCompatibleWithOwnership(LifetimeDependenceKind kind, Type type, + ValueOwnership ownership) const { + if (kind == LifetimeDependenceKind::Inherit) { + return true; + } + // Lifetime dependence always propagates through temporary BitwiseCopyable + // values, even if the dependence is scoped. + if (isBitwiseCopyable(type, ctx)) { + return true; } - auto targetIndex = lifetimeDependenceInfo->getTargetIndex(); - if (lifetimeDependentTargets.contains(targetIndex)) { - // TODO: Diagnose at the source location of the @lifetime attribute with - // duplicate target. - diags.diagnose(afd->getLoc(), diag::lifetime_dependence_duplicate_target); + assert(kind == LifetimeDependenceKind::Scope); + auto loweredOwnership = ownership != ValueOwnership::Default + ? ownership : getLoweredOwnership(afd); + + if (loweredOwnership == ValueOwnership::InOut || + loweredOwnership == ValueOwnership::Shared) { + return true; } - lifetimeDependentTargets.insert(targetIndex); - lifetimeDependencies.push_back(*lifetimeDependenceInfo); + assert(loweredOwnership == ValueOwnership::Owned); + return false; } - return afd->getASTContext().AllocateCopy(lifetimeDependencies); -} + struct TargetDeps { + unsigned targetIndex; + SmallBitVector inheritIndices; + SmallBitVector scopeIndices; -// This utility is similar to its overloaded version that builds the -// LifetimeDependenceInfo from the swift decl. Reason for duplicated code is -// the apis on type and ownership is different in SIL compared to Sema. -std::optional LifetimeDependenceInfo::fromDependsOn( - LifetimeDependentTypeRepr *lifetimeDependentRepr, unsigned targetIndex, - ArrayRef params, DeclContext *dc) { - auto &ctx = dc->getASTContext(); - auto &diags = ctx.Diags; - auto capacity = params.size(); // SIL parameters include self + TargetDeps(unsigned targetIndex, unsigned capacity) + : targetIndex(targetIndex), inheritIndices(capacity), + scopeIndices(capacity) {} - SmallBitVector inheritLifetimeParamIndices(capacity); - SmallBitVector scopeLifetimeParamIndices(capacity); - SmallBitVector addressableLifetimeParamIndices(capacity); - SmallBitVector conditionallyAddressableLifetimeParamIndices(capacity); + TargetDeps &&add(unsigned sourceIndex, LifetimeDependenceKind kind) && { + switch (kind) { + case LifetimeDependenceKind::Inherit: + inheritIndices.set(sourceIndex); + break; + case LifetimeDependenceKind::Scope: + scopeIndices.set(sourceIndex); + break; + } + return std::move(*this); + } + }; - auto updateLifetimeDependenceInfo = [&](LifetimeDescriptor descriptor, - unsigned paramIndexToSet, - ParameterConvention paramConvention) { - auto loc = descriptor.getLoc(); - auto kind = descriptor.getParsedLifetimeDependenceKind(); + TargetDeps createDeps(unsigned targetIndex) { + unsigned capacity = afd->hasImplicitSelfDecl() + ? (afd->getParameters()->size() + 1) + : afd->getParameters()->size(); + return TargetDeps(targetIndex, capacity); + } - if (kind == ParsedLifetimeDependenceKind::Scope && - isConsumedParameterInCallee(paramConvention)) { - diags.diagnose(loc, diag::lifetime_dependence_cannot_use_kind, "_scope", - getStringForParameterConvention(paramConvention)); - return true; + // Allocate LifetimeDependenceInfo in the ASTContext and push it onto + // lifetimeDependencies. + void pushDeps(const TargetDeps &&deps) { + assert(llvm::none_of(lifetimeDependencies, + [&](LifetimeDependenceInfo dep) { + return dep.getTargetIndex() == deps.targetIndex; + })); + IndexSubset *inheritIndices = nullptr; + if (deps.inheritIndices.any()) { + inheritIndices = IndexSubset::get(ctx, deps.inheritIndices); + } + IndexSubset *scopeIndices = nullptr; + if (deps.scopeIndices.any()) { + scopeIndices = IndexSubset::get(ctx, deps.scopeIndices); } + lifetimeDependencies.push_back( + LifetimeDependenceInfo{ + /*inheritLifetimeParamIndices*/ inheritIndices, + /*scopeLifetimeParamIndices*/ scopeIndices, + deps.targetIndex, + /*isImmortal*/ false}); + } - if (inheritLifetimeParamIndices.test(paramIndexToSet) || - scopeLifetimeParamIndices.test(paramIndexToSet)) { - diags.diagnose(loc, diag::lifetime_dependence_duplicate_param_id); - return true; + Type getResultOrYield() const { + if (auto *accessor = dyn_cast(afd)) { + if (accessor->isCoroutine()) { + auto yieldTyInContext = accessor->mapTypeIntoContext( + accessor->getStorage()->getValueInterfaceType()); + return yieldTyInContext; + } } - if (kind == ParsedLifetimeDependenceKind::Inherit) { - inheritLifetimeParamIndices.set(paramIndexToSet); + Type resultType; + if (auto fn = dyn_cast(afd)) { + resultType = fn->getResultInterfaceType(); } else { - assert(kind == ParsedLifetimeDependenceKind::Scope); - scopeLifetimeParamIndices.set(paramIndexToSet); + auto ctor = cast(afd); + resultType = ctor->getResultInterfaceType(); } - return false; - }; + return afd->mapTypeIntoContext(resultType); + } - for (auto source : lifetimeDependentRepr->getLifetimeEntry()->getSources()) { - switch (source.getDescriptorKind()) { - case LifetimeDescriptor::DescriptorKind::Ordered: { - auto index = source.getIndex(); - if (index > capacity) { - diags.diagnose(source.getLoc(), - diag::lifetime_dependence_invalid_param_index, index); + std::optional + getDependenceKindFromDescriptor(LifetimeDescriptor descriptor, + ParamDecl *decl) { + auto loc = descriptor.getLoc(); + + auto ownership = decl->getValueOwnership(); + auto type = decl->getTypeInContext(); + + LifetimeDependenceKind kind; + switch (descriptor.getParsedLifetimeDependenceKind()) { + case ParsedLifetimeDependenceKind::Default: + if (type->isEscapable()) { + kind = LifetimeDependenceKind::Scope; + } else if (useLazyInference()) { + kind = LifetimeDependenceKind::Inherit; + } else { + diagnose(loc, diag::lifetime_dependence_cannot_infer_kind, + diagnosticQualifier(), descriptor.getString()); return std::nullopt; } - auto param = params[index]; - auto paramConvention = param.getConvention(); - if (updateLifetimeDependenceInfo(source, index, paramConvention)) { + break; + case ParsedLifetimeDependenceKind::Scope: + kind = LifetimeDependenceKind::Scope; + break; + case ParsedLifetimeDependenceKind::Inherit: + kind = LifetimeDependenceKind::Inherit; + break; + } + // @lifetime(borrow x) is invalid for consuming parameters. + if (!isCompatibleWithOwnership(kind, 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()) { + diagnose(loc, diag::lifetime_dependence_invalid_inherit_escapable_type, + descriptor.getString()); + return std::nullopt; + } + return kind; + } + + // Finds the ParamDecl* and its index from a LifetimeDescriptor + std::optional> + getParamDeclFromDescriptor(LifetimeDescriptor descriptor) { + switch (descriptor.getDescriptorKind()) { + case LifetimeDescriptor::DescriptorKind::Named: { + unsigned paramIndex = 0; + ParamDecl *candidateParam = nullptr; + for (auto *param : *afd->getParameters()) { + if (param->getParameterName() == descriptor.getName()) { + candidateParam = param; + break; + } + paramIndex++; + } + if (!candidateParam) { + diagnose(descriptor.getLoc(), + diag::lifetime_dependence_invalid_param_name, + descriptor.getName()); return std::nullopt; } - switch (source.isAddressable()) { - case LifetimeDescriptor::IsNotAddressable: - break; - case LifetimeDescriptor::IsConditionallyAddressable: - conditionallyAddressableLifetimeParamIndices.set(index); - addressableLifetimeParamIndices.set(index); - break; - case LifetimeDescriptor::IsAddressable: - addressableLifetimeParamIndices.set(index); - break; + return std::make_pair(candidateParam, paramIndex); + } + case LifetimeDescriptor::DescriptorKind::Ordered: { + auto paramIndex = descriptor.getIndex(); + if (paramIndex >= afd->getParameters()->size()) { + diagnose(descriptor.getLoc(), + diag::lifetime_dependence_invalid_param_index, + paramIndex); + return std::nullopt; } - break; + auto candidateParam = afd->getParameters()->get(paramIndex); + return std::make_pair(candidateParam, paramIndex); } - case LifetimeDescriptor::DescriptorKind::Named: { - assert(source.isImmortal()); - return LifetimeDependenceInfo(/*inheritLifetimeParamIndices*/ nullptr, - /*scopeLifetimeParamIndices*/ nullptr, - targetIndex, - /*isImmortal*/ true); + case LifetimeDescriptor::DescriptorKind::Self: { + if (!hasImplicitSelfParam()) { + diagnose(descriptor.getLoc(), + diag::lifetime_dependence_invalid_self_in_static); + return std::nullopt; + } + if (isa(afd)) { + diagnose(descriptor.getLoc(), + diag::lifetime_dependence_invalid_self_in_init); + return std::nullopt; + } + auto *selfDecl = afd->getImplicitSelfDecl(); + return std::make_pair(selfDecl, afd->getParameters()->size()); } - default: - llvm_unreachable("SIL can only have ordered or immortal lifetime " - "dependence specifier kind"); } } - return LifetimeDependenceInfo( - inheritLifetimeParamIndices.any() - ? IndexSubset::get(ctx, inheritLifetimeParamIndices) - : nullptr, - scopeLifetimeParamIndices.any() - ? IndexSubset::get(ctx, scopeLifetimeParamIndices) - : nullptr, - targetIndex, - /*isImmortal*/ false, - addressableLifetimeParamIndices.any() - ? IndexSubset::get(ctx, addressableLifetimeParamIndices) - : nullptr, - conditionallyAddressableLifetimeParamIndices.any() - ? IndexSubset::get(ctx, conditionallyAddressableLifetimeParamIndices) - : nullptr); -} - -std::optional -LifetimeDependenceInfo::infer(AbstractFunctionDecl *afd) { - auto *dc = afd->getDeclContext(); - auto &ctx = dc->getASTContext(); + std::optional> checkAttribute() { + SmallVector lifetimeDependencies; + llvm::SmallSet lifetimeDependentTargets; + auto lifetimeAttrs = afd->getAttrs().getAttributes(); + for (auto attr : lifetimeAttrs) { + auto lifetimeDependenceInfo = + checkAttributeEntry(attr->getLifetimeEntry()); + if (!lifetimeDependenceInfo.has_value()) { + return std::nullopt; + } + auto targetIndex = lifetimeDependenceInfo->getTargetIndex(); + if (lifetimeDependentTargets.contains(targetIndex)) { + // TODO: Diagnose at the source location of the @lifetime attribute with + // duplicate target. + diagnose(afd->getLoc(), diag::lifetime_dependence_duplicate_target); + } + lifetimeDependentTargets.insert(targetIndex); + lifetimeDependencies.push_back(*lifetimeDependenceInfo); + } - // Disable inference if requested. - if (!ctx.LangOpts.EnableExperimentalLifetimeDependenceInference) { - return std::nullopt; + return afd->getASTContext().AllocateCopy(lifetimeDependencies); } - if (getResultOrYield(afd)->hasError()) { - return std::nullopt; - } + std::optional + checkAttributeEntry(LifetimeEntry *entry) { + auto capacity = afd->hasImplicitSelfDecl() + ? (afd->getParameters()->size() + 1) + : afd->getParameters()->size(); + + SmallBitVector inheritIndices(capacity); + SmallBitVector scopeIndices(capacity); + + auto updateLifetimeIndices = [&](LifetimeDescriptor descriptor, + unsigned paramIndexToSet, + LifetimeDependenceKind lifetimeKind) { + if (inheritIndices.test(paramIndexToSet) || + scopeIndices.test(paramIndexToSet)) { + diagnose(descriptor.getLoc(), + diag::lifetime_dependence_duplicate_param_id); + return true; + } + if (lifetimeKind == LifetimeDependenceKind::Inherit) { + inheritIndices.set(paramIndexToSet); + } else { + assert(lifetimeKind == LifetimeDependenceKind::Scope); + scopeIndices.set(paramIndexToSet); + } + return false; + }; + + auto targetDescriptor = entry->getTargetDescriptor(); + unsigned targetIndex; + if (targetDescriptor.has_value()) { + auto targetDeclAndIndex = getParamDeclFromDescriptor(*targetDescriptor); + if (!targetDeclAndIndex.has_value()) { + return std::nullopt; + } + // TODO: support dependencies on non-inout parameters. + if (!targetDeclAndIndex->first->isInOut()) { + diagnose(targetDeclAndIndex->first, + diag::lifetime_parameter_requires_inout, + targetDescriptor->getName()); + } + targetIndex = targetDeclAndIndex->second; + } else { + targetIndex = afd->hasImplicitSelfDecl() + ? afd->getParameters()->size() + 1 + : afd->getParameters()->size(); + } - if (afd->getAttrs().hasAttribute()) { - return std::nullopt; - } + for (auto source : entry->getSources()) { + if (source.isImmortal()) { + auto immortalParam = + std::find_if(afd->getParameters()->begin(), + afd->getParameters()->end(), [](ParamDecl *param) { + return strcmp(param->getName().get(), "immortal") == 0; + }); + if (immortalParam != afd->getParameters()->end()) { + diagnose(*immortalParam, + diag::lifetime_dependence_immortal_conflict_name); + return std::nullopt; + } + return LifetimeDependenceInfo(nullptr, nullptr, targetIndex, + /*isImmortal*/ true); + } - // Setters infer 'self' dependence on 'newValue'. - if (auto accessor = dyn_cast(afd)) { - if (accessor->getAccessorKind() == AccessorKind::Set) { - return inferSetter(accessor); + auto paramDeclAndIndex = getParamDeclFromDescriptor(source); + if (!paramDeclAndIndex.has_value()) { + return std::nullopt; + } + auto lifetimeKind = + getDependenceKindFromDescriptor(source, paramDeclAndIndex->first); + if (!lifetimeKind.has_value()) { + return std::nullopt; + } + unsigned sourceIndex = paramDeclAndIndex->second; + if (lifetimeKind == LifetimeDependenceKind::Scope + && sourceIndex == targetIndex) { + diagnose(source.getLoc(), + diag::lifetime_dependence_cannot_use_parsed_borrow_inout); + return std::nullopt; + } + bool hasError = + updateLifetimeIndices(source, sourceIndex, *lifetimeKind); + if (hasError) { + return std::nullopt; + } } - } - if (hasEscapableResultOrYield(afd)) { - return std::nullopt; + return LifetimeDependenceInfo( + inheritIndices.any() ? IndexSubset::get(ctx, inheritIndices) : nullptr, + scopeIndices.any() ? IndexSubset::get(ctx, scopeIndices) : nullptr, + targetIndex, /*isImmortal*/ false); } - auto &diags = ctx.Diags; - auto returnTypeRepr = afd->getResultTypeRepr(); - auto returnLoc = returnTypeRepr ? returnTypeRepr->getLoc() : afd->getLoc(); - unsigned resultIndex = afd->hasImplicitSelfDecl() - ? afd->getParameters()->size() + 1 - : afd->getParameters()->size(); - - auto *cd = dyn_cast(afd); - if (cd && cd->getParameters()->size() == 0) { - if (cd->isImplicit()) { - return std::nullopt; - } - if (auto *sf = afd->getParentSourceFile()) { - // The AST printer makes implicit initializers explicit, but does not - // print the @lifetime annotations. Until that is fixed, avoid diagnosing - // this as an error. - if (sf->Kind == SourceFileKind::SIL) { - return std::nullopt; + // On returning, 'lifetimeDependencies' contains any inferred dependencies and + // 'performedDiagnostics' indicates whether any specific diagnostics were + // issued. + void inferOrDiagnose() { + // Infer non-Escapable results. + if (isDiagnosedNonEscapable(getResultOrYield())) { + if (hasImplicitSelfParam()) { + // Methods and accessors that return or yield a non-Escapable value. + inferNonEscapableResultOnSelf(); + return; + } + if (isInit() && isImplicitOrSIL()) { + inferImplicitInit(); + return; } + // Regular functions and initializers that return a non-Escapable value. + inferNonEscapableResultOnParam(); + return; } - } - if (!ctx.LangOpts.hasFeature(Feature::LifetimeDependence) && - !ctx.SourceMgr.isImportMacroGeneratedLoc(returnLoc)) { - diags.diagnose(returnLoc, diag::lifetime_dependence_feature_required); - return std::nullopt; + // Infer mutating methods. + if (hasImplicitSelfParam()) { + if (isDiagnosedNonEscapable(dc->getSelfTypeInContext())) { + assert(!isInit() && "class initializers have Escapable self"); + auto *selfDecl = afd->getImplicitSelfDecl(); + if (selfDecl->isInOut()) { + // Mutating methods (excluding initializers) + inferMutatingSelf(selfDecl); + return; + } + } + } + // Infer inout parameters. + inferInoutParams(); } - if (!cd && afd->hasImplicitSelfDecl()) { + // Infer method dependence: result depends on self. This includes _modify. + void inferNonEscapableResultOnSelf() { Type selfTypeInContext = dc->getSelfTypeInContext(); - if (selfTypeInContext->isEscapable()) { - if (isBitwiseCopyable(selfTypeInContext, ctx)) { - diags.diagnose( - returnLoc, - diag::lifetime_dependence_method_escapable_bitwisecopyable_self); - return std::nullopt; + if (selfTypeInContext->hasError()) { + return; + } + bool nonEscapableSelf = isDiagnosedNonEscapable(selfTypeInContext); + + // Avoid diagnosing inference on mutating methods when 'self' is + // non-Escapable. The inout 'self' also needs an inferred dependence on + // itself. This will be diagnosed when checking for missing dependencies. + if (nonEscapableSelf && afd->getImplicitSelfDecl()->isInOut()) { + if (auto accessor = dyn_cast(afd)) { + inferMutatingAccessor(accessor); } + return; + } + + // Methods with parameters only apply to lazy inference. + if (!useLazyInference() && afd->getParameters()->size() > 0) { + return; + } + if (!nonEscapableSelf && isBitwiseCopyable(selfTypeInContext, ctx)) { + diagnose(returnLoc, + diag::lifetime_dependence_cannot_infer_bitwisecopyable, + diagnosticQualifier(), "self"); + return; } - auto kind = getLifetimeDependenceKindFromType(selfTypeInContext); + if (!useLazyInference()) { + // Do not infer LifetimeDependenceKind::Inherit unless this is an implicit + // getter, which simply returns a stored property. + if (nonEscapableSelf && !isImplicitOrSIL()) { + diagnose(returnLoc, diag::lifetime_dependence_cannot_infer_kind, + diagnosticQualifier(), "self"); + return; + } + } + auto kind = inferLifetimeDependenceKindFromType(selfTypeInContext); auto selfOwnership = afd->getImplicitSelfDecl()->getValueOwnership(); - if (!isLifetimeDependenceCompatibleWithOwnership(kind, selfTypeInContext, - selfOwnership, afd)) { - diags.diagnose(returnLoc, - diag::lifetime_dependence_invalid_self_ownership); - return std::nullopt; + if (!isCompatibleWithOwnership(kind, selfTypeInContext, selfOwnership)) { + diagnose(returnLoc, + diag::lifetime_dependence_cannot_infer_scope_ownership, + "self", diagnosticQualifier()); + return; } - - // Infer method dependence: result depends on self. - // - // This includes _modify. A _modify's yielded value depends on self. The - // caller of the _modify ensures that the 'self' depends on any value stored - // to the yielded address. - return LifetimeDependenceInfo::getForIndex( - afd, resultIndex, /*selfIndex */ afd->getParameters()->size(), kind); + pushDeps(createDeps(resultIndex).add(selfIndex, kind)); } - std::optional candidateParamIndex; - std::optional candidateLifetimeKind; - unsigned paramIndex = 0; - bool hasParamError = false; - for (auto *param : *afd->getParameters()) { - SWIFT_DEFER { paramIndex++; }; - Type paramTypeInContext = - afd->mapTypeIntoContext(param->getInterfaceType()); - if (paramTypeInContext->hasError()) { - hasParamError = true; - continue; + // Infer implicit initialization. The dependence kind can be inferred, similar + // to an implicit setter, because the implementation is simply an assignment + // to stored property. + void inferImplicitInit() { + if (afd->getParameters()->size() == 0) { + // Empty ~Escapable types can be implicitly initialized without any + // dependencies. In SIL, implicit initializers become explicit. Set + // performedDiagnostics here to bypass normal dependence checking without + // raising an error. + performedDiagnostics = true; + return; } - auto paramOwnership = param->getValueOwnership(); - if (paramTypeInContext->isEscapable()) { - if (isBitwiseCopyable(paramTypeInContext, ctx)) { + auto targetDeps = createDeps(resultIndex); + unsigned paramIndex = 0; + for (auto *param : *afd->getParameters()) { + SWIFT_DEFER { paramIndex++; }; + Type paramTypeInContext = + afd->mapTypeIntoContext(param->getInterfaceType()); + if (paramTypeInContext->hasError()) { continue; } - if (paramOwnership == ValueOwnership::Default) { + auto kind = inferLifetimeDependenceKindFromType(paramTypeInContext); + auto paramOwnership = param->getValueOwnership(); + if (!isCompatibleWithOwnership(kind, paramTypeInContext, paramOwnership)) + { + diagnose(returnLoc, + diag::lifetime_dependence_cannot_infer_scope_ownership, + param->getParameterName().str(), diagnosticQualifier()); continue; } + targetDeps = std::move(targetDeps).add(paramIndex, kind); } + pushDeps(std::move(targetDeps)); + } - candidateLifetimeKind = - getLifetimeDependenceKindFromType(paramTypeInContext); - if (!isLifetimeDependenceCompatibleWithOwnership( - *candidateLifetimeKind, paramTypeInContext, paramOwnership, afd)) { - continue; + // Infer result dependence on a function or intitializer parameter. + // + // Note: for implicit initializers with parameters, consider inferring + // Inherit dependency for each non-Escapable parameter. This would be + // consistent with implicit stored property setters. This isn't done yet + // because we also need to consider any Escapable parameters: either skip + // inference if any exist, infer scoped dependency, or infer no + // dependency. Implicit setters for Escapable properties are not inferred. + void inferNonEscapableResultOnParam() { + // This is only called when there is no 'self' argument that can be the + // source of a dependence. + assert(!hasImplicitSelfParam()); + + if (useLazyInference()) { + return lazillyInferNonEscapableResultOnParam(); } - if (candidateParamIndex) { - if (cd && afd->isImplicit()) { - diags.diagnose( - returnLoc, - diag::lifetime_dependence_cannot_infer_ambiguous_candidate, - "on implicit initializer"); - return std::nullopt; - } - diags.diagnose(returnLoc, - diag::lifetime_dependence_cannot_infer_ambiguous_candidate, - ""); - return std::nullopt; + + // Strict inference only handles a single escapable parameter, + // which is an unambiguous borrow dependence. + if (afd->getParameters()->size() == 0) { + diagnose(returnLoc, + diag::lifetime_dependence_cannot_infer_return_no_param, + diagnosticQualifier()); + diagnose(returnLoc, + diag::lifetime_dependence_cannot_infer_return_immortal); + return; + } + if (afd->getParameters()->size() > 1) { + // The usual diagnostic check is sufficient. + return; + } + // Do not infer non-escapable dependence kind -- it is ambiguous. + auto *param = afd->getParameters()->get(0); + Type paramTypeInContext = + afd->mapTypeIntoContext(param->getInterfaceType()); + if (paramTypeInContext->hasError()) { + return; + } + if (!paramTypeInContext->isEscapable()) { + diagnose(returnLoc, diag::lifetime_dependence_cannot_infer_kind, + diagnosticQualifier(), param->getParameterName().str()); + return; + } + auto kind = LifetimeDependenceKind::Scope; + auto paramOwnership = param->getValueOwnership(); + if (!isCompatibleWithOwnership(kind, paramTypeInContext, paramOwnership)) + { + diagnose(returnLoc, + diag::lifetime_dependence_cannot_infer_scope_ownership, + param->getParameterName().str(), diagnosticQualifier()); + return; } - candidateParamIndex = paramIndex; + pushDeps(createDeps(resultIndex).add(/*paramIndex*/ 0, kind)); } - if (!candidateParamIndex && !hasParamError) { - if (cd && afd->isImplicit()) { - diags.diagnose(returnLoc, - diag::lifetime_dependence_cannot_infer_no_candidates, - " on implicit initializer"); - return std::nullopt; + // Lazy inference for .swiftinterface backward compatibility and + // experimentation. Inference cases can be added but not removed. + void lazillyInferNonEscapableResultOnParam() { + std::optional candidateParamIndex; + std::optional candidateLifetimeKind; + unsigned paramIndex = 0; + for (auto *param : *afd->getParameters()) { + SWIFT_DEFER { paramIndex++; }; + Type paramTypeInContext = + afd->mapTypeIntoContext(param->getInterfaceType()); + if (paramTypeInContext->hasError()) { + return; + } + auto paramOwnership = param->getValueOwnership(); + if (paramTypeInContext->isEscapable()) { + if (isBitwiseCopyable(paramTypeInContext, ctx)) { + continue; + } + if (paramOwnership == ValueOwnership::Default) { + continue; + } + } + + candidateLifetimeKind = + inferLifetimeDependenceKindFromType(paramTypeInContext); + if (!isCompatibleWithOwnership( + *candidateLifetimeKind, paramTypeInContext, paramOwnership)) { + continue; + } + if (candidateParamIndex) { + diagnose(returnLoc, + diag::lifetime_dependence_cannot_infer_ambiguous_candidate, + diagnosticQualifier()); + return; + } + candidateParamIndex = paramIndex; } - diags.diagnose(returnLoc, - diag::lifetime_dependence_cannot_infer_no_candidates, ""); - return std::nullopt; + if (!candidateParamIndex) { + diagnose(returnLoc, + diag::lifetime_dependence_cannot_infer_no_candidates, + diagnosticQualifier()); + return; + } + pushDeps(createDeps(resultIndex).add(*candidateParamIndex, + *candidateLifetimeKind)); + } + + // Infer a mutating 'self' dependency when 'self' is non-Escapable and the + // result is 'void'. + void inferMutatingSelf(ParamDecl *selfDecl) { + // Handle implicit setters before diagnosing mutating methods. This + // does not include global accessors, which have no implicit 'self'. + if (auto accessor = dyn_cast(afd)) { + inferMutatingAccessor(accessor); + return; + } + if (afd->getParameters()->size() > 0) { + return; + } + pushDeps(createDeps(selfIndex).add(selfIndex, + LifetimeDependenceKind::Inherit)); } - return LifetimeDependenceInfo::getForIndex( - afd, resultIndex, *candidateParamIndex, *candidateLifetimeKind); -} - -/// Infer LifetimeDependence on a setter where 'self' is nonescapable. -std::optional LifetimeDependenceInfo::inferSetter( - AbstractFunctionDecl *afd) { + // Infer a mutating accessor's non-Escapable 'self' dependencies. + void inferMutatingAccessor(AccessorDecl *accessor) { + if (!isImplicitOrSIL()) { + // Explicit setters require explicit lifetime dependencies. + return; + } + switch (accessor->getAccessorKind()) { + case AccessorKind::Read: + // An implicit _read accessor is generated when a mutating getter is + // declared. Emit the same lifetime dependencies as an implicit _modify. + case AccessorKind::Modify: + case AccessorKind::Modify2: + // A _modify's yielded value depends on self. The _modify dependency in + // the opposite direction (self depends on the modified value) is not + // recorded. The caller of _modify ensures that the modified 'self', + // passed as 'inout', depends on any value stored to the yielded address. + // + // This is required for stored properties because the AST generates a + // _modify for them even though it won't be emitted. + pushDeps(createDeps(selfIndex).add(selfIndex, + LifetimeDependenceKind::Inherit)); + pushDeps(createDeps(resultIndex).add(selfIndex, + LifetimeDependenceKind::Scope)); + break; + case AccessorKind::Set: { + const unsigned newValIdx = 0; + auto *param = afd->getParameters()->get(newValIdx); + Type paramTypeInContext = + afd->mapTypeIntoContext(param->getInterfaceType()); + if (paramTypeInContext->hasError()) { + return; + } + auto kind = inferLifetimeDependenceKindFromType(paramTypeInContext); - auto *param = afd->getParameters()->get(0); - Type paramTypeInContext = - afd->mapTypeIntoContext(param->getInterfaceType()); - if (paramTypeInContext->hasError()) { - return std::nullopt; - } - if (paramTypeInContext->isEscapable()) { - return std::nullopt; + pushDeps(createDeps(selfIndex) + .add(selfIndex, LifetimeDependenceKind::Inherit) + .add(newValIdx, kind)); + break; + } + default: + // Unknown mutating accessor. + break; + } } - auto kind = getLifetimeDependenceKindFromType(paramTypeInContext); - return LifetimeDependenceInfo::getForIndex( - afd, /*selfIndex */ afd->getParameters()->size(), 0, kind); -} -/// Infer LifetimeDependenceInfo on a mutating method where 'self' is -/// nonescapable and the result is 'void'. For now, we'll assume that 'self' -/// depends on a single nonescapable argument. -std::optional LifetimeDependenceInfo::inferMutatingSelf( - AbstractFunctionDecl *afd) { - std::optional dep; - for (unsigned paramIndex : range(afd->getParameters()->size())) { + // Infer 'inout' parameter dependency when the only parameter is + // non-Escapable. + // + // This is needed for most generic Builtin functions. + void inferInoutParams() { + if (afd->getParameters()->size() != 1) { + return; + } + const unsigned paramIndex = 0; auto *param = afd->getParameters()->get(paramIndex); - Type paramTypeInContext = - afd->mapTypeIntoContext(param->getInterfaceType()); - if (paramTypeInContext->hasError()) { - continue; + if (!param->isInOut()) { + return; } - if (paramTypeInContext->isEscapable()) { - continue; + if (!isDiagnosedNonEscapable( + afd->mapTypeIntoContext(param->getInterfaceType()))) { + return; } - if (dep) { - // Don't infer dependence on multiple nonescapable parameters. We may want - // to do this in the future if dependsOn(self: arg1, arg2) syntax is too - // cumbersome. - return std::nullopt; + pushDeps(createDeps(paramIndex).add(paramIndex, + LifetimeDependenceKind::Inherit)); + } + + void inferBuiltin() { + // Normal inout parameter inference works for most generic Builtins. + inferInoutParams(); + if (!lifetimeDependencies.empty()) { + return; + } + const DeclName &name = afd->getName(); + if (name.isSpecial()) { + return; + } + // TODO: declare lifetime dependencies in Builtins.def. Until then, filter + // the few that are not covered by general inference rules here. This is + // safer than using a broader rule for implicit declarations. New Builtins + // need to be considered as they are defined. + auto id = name.getBaseIdentifier(); + if (id == + ctx.getIdentifier(getBuiltinName(BuiltinValueKind::InjectEnumTag))) { + // ignore the tag parameter + const unsigned inoutIdx = 0; + pushDeps(createDeps(inoutIdx).add(inoutIdx, + LifetimeDependenceKind::Inherit)); + } else if (id == + ctx.getIdentifier( + getBuiltinName(BuiltinValueKind::ConvertUnownedUnsafeToGuaranteed))) { + const unsigned baseIdx = 0; + const unsigned inoutIdx = 1; + pushDeps(createDeps(inoutIdx) + .add(inoutIdx, LifetimeDependenceKind::Inherit) + .add(baseIdx, LifetimeDependenceKind::Scope)); } - int selfIndex = afd->getParameters()->size(); - dep = LifetimeDependenceInfo::getForIndex( - afd, selfIndex, paramIndex, LifetimeDependenceKind::Inherit); } - return dep; -} +}; std::optional> LifetimeDependenceInfo::get(AbstractFunctionDecl *afd) { - assert(isa(afd) || isa(afd)); + return LifetimeDependenceChecker(afd).checkFuncDecl(); +} - if (afd->getAttrs().hasAttribute()) { - return LifetimeDependenceInfo::fromLifetimeAttribute(afd); - } +// This implements the logic for SIL type descriptors similar to source-level +// logic in LifetimeDependenceChecker::checkAttributeEntry(). The SIL context is +// substantially different from Sema. +static std::optional checkSILTypeModifiers( + LifetimeDependentTypeRepr *lifetimeDependentRepr, unsigned targetIndex, + ArrayRef params, DeclContext *dc) { + auto &ctx = dc->getASTContext(); + auto &diags = ctx.Diags; + auto capacity = params.size(); // SIL parameters include self - SmallVector lifetimeDependencies; - auto resultDependence = LifetimeDependenceInfo::infer(afd); - if (!resultDependence.has_value()) { - return std::nullopt; + SmallBitVector inheritLifetimeParamIndices(capacity); + SmallBitVector scopeLifetimeParamIndices(capacity); + SmallBitVector addressableLifetimeParamIndices(capacity); + SmallBitVector conditionallyAddressableLifetimeParamIndices(capacity); + + auto updateLifetimeDependenceInfo = + [&](LifetimeDescriptor descriptor, + unsigned paramIndexToSet, + ParameterConvention paramConvention) { + auto loc = descriptor.getLoc(); + auto kind = descriptor.getParsedLifetimeDependenceKind(); + + if (kind == ParsedLifetimeDependenceKind::Scope && + isConsumedParameterInCallee(paramConvention)) { + diags.diagnose(loc, diag::lifetime_dependence_cannot_use_kind, "_scope", + getStringForParameterConvention(paramConvention)); + return true; + } + + if (inheritLifetimeParamIndices.test(paramIndexToSet) || + scopeLifetimeParamIndices.test(paramIndexToSet)) { + diags.diagnose(loc, diag::lifetime_dependence_duplicate_param_id); + return true; + } + if (kind == ParsedLifetimeDependenceKind::Inherit) { + inheritLifetimeParamIndices.set(paramIndexToSet); + } else { + assert(kind == ParsedLifetimeDependenceKind::Scope); + scopeLifetimeParamIndices.set(paramIndexToSet); + } + return false; + }; + + for (auto source : lifetimeDependentRepr->getLifetimeEntry()->getSources()) + { + switch (source.getDescriptorKind()) { + case LifetimeDescriptor::DescriptorKind::Ordered: { + auto index = source.getIndex(); + if (index > capacity) { + diags.diagnose(source.getLoc(), + diag::lifetime_dependence_invalid_param_index, index); + return std::nullopt; + } + auto param = params[index]; + auto paramConvention = param.getConvention(); + if (updateLifetimeDependenceInfo(source, index, paramConvention)) { + return std::nullopt; + } + switch (source.isAddressable()) { + case LifetimeDescriptor::IsNotAddressable: + break; + case LifetimeDescriptor::IsConditionallyAddressable: + conditionallyAddressableLifetimeParamIndices.set(index); + addressableLifetimeParamIndices.set(index); + break; + case LifetimeDescriptor::IsAddressable: + addressableLifetimeParamIndices.set(index); + break; + } + break; + } + case LifetimeDescriptor::DescriptorKind::Named: { + assert(source.isImmortal()); + return LifetimeDependenceInfo(/*inheritLifetimeParamIndices*/ nullptr, + /*scopeLifetimeParamIndices*/ nullptr, + targetIndex, + /*isImmortal*/ true); + } + default: + llvm_unreachable("SIL can only have ordered or immortal lifetime " + "dependence specifier kind"); + } } - lifetimeDependencies.push_back(*resultDependence); - return afd->getASTContext().AllocateCopy(lifetimeDependencies); + + return LifetimeDependenceInfo( + inheritLifetimeParamIndices.any() + ? IndexSubset::get(ctx, inheritLifetimeParamIndices) + : nullptr, + scopeLifetimeParamIndices.any() + ? IndexSubset::get(ctx, scopeLifetimeParamIndices) + : nullptr, + targetIndex, + /*isImmortal*/ false, + addressableLifetimeParamIndices.any() + ? IndexSubset::get(ctx, addressableLifetimeParamIndices) + : nullptr, + conditionallyAddressableLifetimeParamIndices.any() + ? IndexSubset::get(ctx, conditionallyAddressableLifetimeParamIndices) + : nullptr); } std::optional> -LifetimeDependenceInfo::get(FunctionTypeRepr *funcRepr, - ArrayRef params, - ArrayRef results, DeclContext *dc) { +LifetimeDependenceInfo::getFromSIL(FunctionTypeRepr *funcRepr, + ArrayRef params, + ArrayRef results, + DeclContext *dc) { SmallVector lifetimeDependencies; - auto getLifetimeDependenceFromDependsOnTypeModifier = + auto getLifetimeDependenceFromTypeModifiers = [&](TypeRepr *typeRepr, unsigned targetIndex) -> std::optional { auto *lifetimeTypeRepr = @@ -766,20 +1189,19 @@ LifetimeDependenceInfo::get(FunctionTypeRepr *funcRepr, if (!lifetimeTypeRepr) { return std::nullopt; } - return LifetimeDependenceInfo::fromDependsOn(lifetimeTypeRepr, targetIndex, - params, dc); + return checkSILTypeModifiers(lifetimeTypeRepr, targetIndex, params, dc); }; auto argsTypeRepr = funcRepr->getArgsTypeRepr()->getElements(); for (unsigned targetIndex : indices(argsTypeRepr)) { - if (auto result = getLifetimeDependenceFromDependsOnTypeModifier( + if (auto result = getLifetimeDependenceFromTypeModifiers( argsTypeRepr[targetIndex].Type, targetIndex)) { lifetimeDependencies.push_back(*result); } } - auto result = getLifetimeDependenceFromDependsOnTypeModifier( - funcRepr->getResultTypeRepr(), params.size()); + auto result = getLifetimeDependenceFromTypeModifiers( + funcRepr->getResultTypeRepr(), params.size()); if (result) { lifetimeDependencies.push_back(*result); } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index dbd9e46c029a1..e313e14d8fcfb 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2036,9 +2036,20 @@ namespace { } void markReturnsUnsafeNonescapable(AbstractFunctionDecl *fd) { - fd->getAttrs().add(new (Impl.SwiftContext) - UnsafeNonEscapableResultAttr(/*Implicit=*/true)); fd->getAttrs().add(new (Impl.SwiftContext) UnsafeAttr(/*Implicit=*/true)); + + unsigned resultIndex = fd->getParameters()->size(); + if (fd->hasImplicitSelfDecl()) { + ++resultIndex; + } + SmallVector lifetimeDependencies; + LifetimeDependenceInfo immortalLifetime(nullptr, nullptr, resultIndex, + /*isImmortal*/ true); + lifetimeDependencies.push_back(immortalLifetime); + Impl.SwiftContext.evaluator.cacheOutput( + LifetimeDependenceInfoRequest{fd}, + Impl.SwiftContext.AllocateCopy(lifetimeDependencies)); + return; } Decl *VisitRecordDecl(const clang::RecordDecl *decl) { @@ -4060,6 +4071,9 @@ namespace { CxxEscapability::Unknown) != CxxEscapability::NonEscapable; }; + // FIXME: this uses '0' as the result index. That only works for + // standalone functions with no parameters. + // See markReturnsUnsafeNonescapable() for a general approach. auto &ASTContext = result->getASTContext(); SmallVector lifetimeDependencies; LifetimeDependenceInfo immortalLifetime(nullptr, nullptr, 0, diff --git a/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift b/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift index 9cb9f0befef7b..1030900d1c335 100644 --- a/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift +++ b/lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift @@ -468,7 +468,7 @@ struct CxxSpanReturnThunkBuilder: BoundsCheckedThunkBuilder { func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax]) throws -> ExprSyntax { let call = try base.buildFunctionCall(pointerArgs) - return "_unsafeRemoveLifetime(Span(_unsafeCxxSpan: \(call)))" + return "_cxxOverrideLifetime(Span(_unsafeCxxSpan: \(call)), copying: ())" } } @@ -1084,9 +1084,13 @@ public struct SwiftifyImportMacro: PeerMacro { } var args : [LabeledExprSyntax] = [] for dependence in returnDependencies { - if (dependence.type == .borrow) { + switch dependence.type { + case .borrow: args.append(LabeledExprSyntax(expression: - DeclReferenceExprSyntax(baseName: TokenSyntax("borrow")))) + DeclReferenceExprSyntax(baseName: TokenSyntax("borrow")))) + case .copy: + args.append(LabeledExprSyntax(expression: + DeclReferenceExprSyntax(baseName: TokenSyntax("copy")))) } args.append(LabeledExprSyntax(expression: DeclReferenceExprSyntax(baseName: TokenSyntax(tryGetParamName(funcDecl, dependence.dependsOn))!), diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index b7041625c4550..22044bc4b33e1 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -716,7 +716,7 @@ static bool parseDeclSILOptional( *useStackForPackMetadata = DoNotUseStackForPackMetadata; else if (hasUnsafeNonEscapableResult && SP.P.Tok.getText() == "unsafe_nonescapable_result") - *useStackForPackMetadata = DoNotUseStackForPackMetadata; + *hasUnsafeNonEscapableResult = hasUnsafeNonEscapableResult; else if (isExactSelfClass && SP.P.Tok.getText() == "exact_self_class") *isExactSelfClass = IsExactSelfClass; else if (isCanonical && SP.P.Tok.getText() == "canonical") diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 86ca6b6d176e1..0d71c48eb18aa 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -4679,7 +4679,8 @@ NeverNullType TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, } auto lifetimeDependencies = - LifetimeDependenceInfo::get(repr, params, results, getDeclContext()); + LifetimeDependenceInfo::getFromSIL(repr, params, results, + getDeclContext()); if (lifetimeDependencies.has_value()) { extInfoBuilder = extInfoBuilder.withLifetimeDependencies(*lifetimeDependencies); diff --git a/stdlib/public/Cxx/CxxSpan.swift b/stdlib/public/Cxx/CxxSpan.swift index 7600c3401d9a3..ba288e7468f91 100644 --- a/stdlib/public/Cxx/CxxSpan.swift +++ b/stdlib/public/Cxx/CxxSpan.swift @@ -18,27 +18,40 @@ internal func unsafeBitCast( Builtin.reinterpretCast(x) } -/// Used by SwiftifyImport to work around a compiler diagnostic. It should be removed once the -/// workaround is no longer needed. +/// Unsafely discard any lifetime dependency on the `dependent` argument. Return +/// a value identical to `dependent` with a lifetime dependency on the caller's +/// borrow scope of the `source` argument. +/// +/// This mimics the stdlib definition. It is public for use with import macros. +@unsafe @_unsafeNonescapableResult @_alwaysEmitIntoClient @_transparent -public func _unsafeRemoveLifetime(_ dependent: consuming T) -> T { +@lifetime(borrow source) +public func _cxxOverrideLifetime< + T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable +>( + _ dependent: consuming T, borrowing source: borrowing U +) -> T { + // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence + // should be expressed by a builtin that is hidden within the function body. dependent } /// Unsafely discard any lifetime dependency on the `dependent` argument. Return -/// a value identical to `dependent` with a lifetime dependency on the caller's -/// borrow scope of the `source` argument. +/// a value identical to `dependent` that inherits all lifetime dependencies from +/// the `source` argument. +/// +/// This mimics the stdlib definition. It is public for use with import macros. @unsafe @_unsafeNonescapableResult @_alwaysEmitIntoClient @_transparent -@lifetime(borrow source) -internal func _overrideLifetime< +@lifetime(copy source) +public func _cxxOverrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( - _ dependent: consuming T, borrowing source: borrowing U + _ dependent: consuming T, copying source: borrowing U ) -> T { // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence // should be expressed by a builtin that is hidden within the function body. @@ -91,13 +104,14 @@ extension Span { @_alwaysEmitIntoClient @unsafe @_unsafeNonescapableResult + @lifetime(borrow span) public init>( _unsafeCxxSpan span: borrowing T, ) { let buffer = unsafe UnsafeBufferPointer(start: span.__dataUnsafe(), count: Int(span.size())) let newSpan = unsafe Span(_unsafeElements: buffer) // 'self' is limited to the caller's scope of the variable passed to the 'span' argument. - self = unsafe _overrideLifetime(newSpan, borrowing: span) + self = unsafe _cxxOverrideLifetime(newSpan, borrowing: span) } } diff --git a/stdlib/public/core/LifetimeManager.swift b/stdlib/public/core/LifetimeManager.swift index fb16a2c09618f..2a0cb3154a077 100644 --- a/stdlib/public/core/LifetimeManager.swift +++ b/stdlib/public/core/LifetimeManager.swift @@ -287,7 +287,7 @@ internal func _overrideLifetime< @_unsafeNonescapableResult @_alwaysEmitIntoClient @_transparent -@lifetime(source) +@lifetime(copy source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( diff --git a/stdlib/public/core/Span/RawSpan.swift b/stdlib/public/core/Span/RawSpan.swift index 551d7b5da5056..86cd2415472c3 100644 --- a/stdlib/public/core/Span/RawSpan.swift +++ b/stdlib/public/core/Span/RawSpan.swift @@ -45,11 +45,9 @@ public struct RawSpan: ~Escapable, Copyable, BitwiseCopyable { @usableFromInline internal let _count: Int - /// FIXME: Remove once supported old compilers can recognize lifetime dependence - @unsafe - @_unsafeNonescapableResult @_alwaysEmitIntoClient @inline(__always) + @lifetime(immortal) internal init() { _pointer = nil _count = 0 @@ -366,7 +364,7 @@ extension RawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(_ bounds: Range) -> Self { _precondition( UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && @@ -393,7 +391,7 @@ extension RawSpan { /// - Complexity: O(1) @unsafe @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(unchecked bounds: Range) -> Self { let newStart = unsafe _pointer?.advanced(by: bounds.lowerBound) let newSpan = unsafe RawSpan(_unchecked: newStart, byteCount: bounds.count) @@ -414,7 +412,7 @@ extension RawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(_ bounds: some RangeExpression) -> Self { _extracting(bounds.relative(to: byteOffsets)) } @@ -436,7 +434,7 @@ extension RawSpan { /// - Complexity: O(1) @unsafe @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting( unchecked bounds: ClosedRange ) -> Self { @@ -456,7 +454,7 @@ extension RawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(_: UnboundedRange) -> Self { self } @@ -511,7 +509,7 @@ extension RawSpan { /// - Returns: A typed span viewing these bytes as instances of `T`. @unsafe @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) consuming public func _unsafeView( as type: T.Type ) -> Span { @@ -691,7 +689,7 @@ extension RawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(first maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, byteCount) @@ -713,7 +711,7 @@ extension RawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(droppingLast k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of bytes") let droppedCount = min(k, byteCount) @@ -737,7 +735,7 @@ extension RawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(last maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, byteCount) @@ -763,7 +761,7 @@ extension RawSpan { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) 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/Span.swift b/stdlib/public/core/Span/Span.swift index 517d157902b9c..cfe6a5814b528 100644 --- a/stdlib/public/core/Span/Span.swift +++ b/stdlib/public/core/Span/Span.swift @@ -47,11 +47,9 @@ public struct Span @usableFromInline internal let _count: Int - /// FIXME: Remove once supported old compilers can recognize lifetime dependence - @unsafe - @_unsafeNonescapableResult @_alwaysEmitIntoClient @inline(__always) + @lifetime(immortal) internal init() { _pointer = nil _count = 0 @@ -360,7 +358,7 @@ extension Span where Element: BitwiseCopyable { /// - bytes: An existing `RawSpan`, which will define both this /// `Span`'s lifetime and the memory it represents. @_alwaysEmitIntoClient - @lifetime(bytes) + @lifetime(copy bytes) public init(_bytes bytes: consuming RawSpan) { let rawBuffer = unsafe UnsafeRawBufferPointer( start: bytes._pointer, count: bytes.byteCount @@ -511,7 +509,7 @@ extension Span where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(_ bounds: Range) -> Self { _precondition( UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && @@ -538,7 +536,7 @@ extension Span where Element: ~Copyable { /// - Complexity: O(1) @unsafe @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(unchecked bounds: Range) -> Self { let delta = bounds.lowerBound &* MemoryLayout.stride let newStart = unsafe _pointer?.advanced(by: delta) @@ -562,7 +560,7 @@ extension Span where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting( _ bounds: some RangeExpression ) -> Self { @@ -586,7 +584,7 @@ extension Span where Element: ~Copyable { /// - Complexity: O(1) @unsafe @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting( unchecked bounds: ClosedRange ) -> Self { @@ -606,7 +604,7 @@ extension Span where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(_: UnboundedRange) -> Self { self } @@ -732,7 +730,7 @@ extension Span where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(first maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a prefix of negative length") let newCount = min(maxLength, count) @@ -754,7 +752,7 @@ extension Span where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(droppingLast k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) @@ -777,7 +775,7 @@ extension Span where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(last maxLength: Int) -> Self { _precondition(maxLength >= 0, "Can't have a suffix of negative length") let newCount = min(maxLength, count) @@ -804,7 +802,7 @@ extension Span where Element: ~Copyable { /// /// - Complexity: O(1) @_alwaysEmitIntoClient - @lifetime(self) + @lifetime(copy self) public func _extracting(droppingFirst k: Int) -> Self { _precondition(k >= 0, "Can't drop a negative number of elements") let droppedCount = min(k, count) diff --git a/test/ASTGen/attrs.swift b/test/ASTGen/attrs.swift index 6f4e23d4e96b1..d55a408c48d46 100644 --- a/test/ASTGen/attrs.swift +++ b/test/ASTGen/attrs.swift @@ -219,13 +219,13 @@ struct OpTest { struct E {} struct NE : ~Escapable {} -@lifetime(ne) func derive(_ ne: NE) -> NE { ne } -@lifetime(borrow ne1, ne2) func derive(_ ne1: NE, _ ne2: NE) -> NE { +@lifetime(copy ne) func derive(_ ne: NE) -> NE { ne } +@lifetime(borrow ne1, copy ne2) func derive(_ ne1: NE, _ ne2: NE) -> NE { if (Int.random(in: 1..<100) < 50) { return ne1 } return ne2 } @lifetime(borrow borrow) func testNameConflict(_ borrow: E) -> NE { NE() } -@lifetime(result: source) func testTarget(_ result: inout NE, _ source: consuming NE) { result = source } +@lifetime(result: copy source) func testTarget(_ result: inout NE, _ source: consuming NE) { result = source } actor MyActor { nonisolated let constFlag: Bool = false diff --git a/test/Generics/inverse_casting_availability.swift b/test/Generics/inverse_casting_availability.swift index 9be4896c6c7dd..8f9208da74e1f 100644 --- a/test/Generics/inverse_casting_availability.swift +++ b/test/Generics/inverse_casting_availability.swift @@ -1,6 +1,9 @@ // RUN: %target-typecheck-verify-swift \ +// RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -debug-diagnostic-names -target arm64-apple-macos14.4 +// REQUIRES: swift_feature_LifetimeDependence + // REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchOS || OS=xros protocol P {} diff --git a/test/Generics/inverse_conditional_conformance_copyable_and_escapable.swift b/test/Generics/inverse_conditional_conformance_copyable_and_escapable.swift index abbbe12d46a60..3eb0bdd2e4a1a 100644 --- a/test/Generics/inverse_conditional_conformance_copyable_and_escapable.swift +++ b/test/Generics/inverse_conditional_conformance_copyable_and_escapable.swift @@ -1,4 +1,8 @@ -// RUN: %target-swift-frontend -typecheck -verify %s +// RUN: %target-swift-frontend \ +// RUN: -enable-experimental-feature LifetimeDependence \ +// RUN: -typecheck -verify %s + +// REQUIRES: swift_feature_LifetimeDependence struct C_C1: ~Copyable {} extension C_C1: Copyable where T: Copyable {} diff --git a/test/Generics/inverse_conditional_conformance_restriction.swift b/test/Generics/inverse_conditional_conformance_restriction.swift index 20f97e2192e7c..09d6163eea974 100644 --- a/test/Generics/inverse_conditional_conformance_restriction.swift +++ b/test/Generics/inverse_conditional_conformance_restriction.swift @@ -1,6 +1,8 @@ // RUN: %target-typecheck-verify-swift \ +// RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -enable-experimental-feature SuppressedAssociatedTypes +// REQUIRES: swift_feature_LifetimeDependence // REQUIRES: swift_feature_SuppressedAssociatedTypes protocol P {} diff --git a/test/Generics/inverse_extension_signatures.swift b/test/Generics/inverse_extension_signatures.swift index 75c3266e2eca6..863626580c4c5 100644 --- a/test/Generics/inverse_extension_signatures.swift +++ b/test/Generics/inverse_extension_signatures.swift @@ -1,7 +1,9 @@ // RUN: %target-swift-frontend \ +// RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -verify -typecheck %s -debug-generic-signatures \ // RUN: -debug-inverse-requirements 2>&1 | %FileCheck %s --implicit-check-not "error:" +// REQUIRES: swift_feature_LifetimeDependence // CHECK-LABEL: .Outer@ // CHECK: Generic signature: diff --git a/test/Generics/inverse_generics.swift b/test/Generics/inverse_generics.swift index 5184690d39380..3930eda3a3c1e 100644 --- a/test/Generics/inverse_generics.swift +++ b/test/Generics/inverse_generics.swift @@ -249,6 +249,7 @@ struct BuggerView: ~Escapable, Copyable {} struct MutableBuggerView: ~Copyable, ~Escapable {} +@lifetime(mutRef: copy mutRef) func checkNominals(_ mutRef: inout MutableBuggerView, _ ref: BuggerView, _ intMutRef: borrowing MutableBuggerView, diff --git a/test/Interop/C/swiftify-import/counted-by-noescape.swift b/test/Interop/C/swiftify-import/counted-by-noescape.swift index 1ea63d99315ec..df2719945aa8f 100644 --- a/test/Interop/C/swiftify-import/counted-by-noescape.swift +++ b/test/Interop/C/swiftify-import/counted-by-noescape.swift @@ -14,7 +14,7 @@ import CountedByNoEscapeClang // CHECK: @_alwaysEmitIntoClient public func complexExpr(_ len: Int{{.*}}, _ offset: Int{{.*}}, _ p: MutableSpan) // CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ p: MutableSpan) // CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ p: MutableSpan) -// CHECK-NEXT: @lifetime(p) +// CHECK-NEXT: @lifetime(copy p) // CHECK-NEXT: @_alwaysEmitIntoClient public func returnLifetimeBound(_ len1: Int32, _ p: MutableSpan) -> MutableSpan // CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func returnPointer(_ len: Int{{.*}}) -> UnsafeMutableBufferPointer // CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int{{.*}}, _ p1: MutableSpan, _ p2: MutableSpan) diff --git a/test/Interop/Cxx/class/nonescapable-errors.swift b/test/Interop/Cxx/class/nonescapable-errors.swift index 2a41c4e513206..12ae97b0b6f88 100644 --- a/test/Interop/Cxx/class/nonescapable-errors.swift +++ b/test/Interop/Cxx/class/nonescapable-errors.swift @@ -99,8 +99,8 @@ const View* usedToCrash(const View* p) { import Test import CxxStdlib -// CHECK: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership -// CHECK-NO-LIFETIMES: test.swift:6:32: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' +// CHECK: error: a function with a ~Escapable result needs a parameter to depend on +// CHECK-NO-LIFETIMES: test.swift:6:32: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' public func noAnnotations() -> View { // CHECK: nonescapable.h:16:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies // CHECK-NO-LIFETIMES: nonescapable.h:16:7: warning: the returned type 'Owner' is annotated as escapable; it cannot have lifetime dependencies @@ -111,16 +111,13 @@ public func noAnnotations() -> View { // CHECK-NOT: nonescapable.h:20 f2(nil, nil) // CHECK: nonescapable.h:24:6: warning: the returned type 'View' is annotated as non-escapable; its lifetime dependencies must be annotated - // CHECK: nonescapable.h:24:6: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership // CHECK-NO-LIFETIMES: nonescapable.h:24:6: warning: the returned type 'View' is annotated as non-escapable; its lifetime dependencies must be annotated - // CHECK-NO-LIFETIMES: nonescapable.h:24:6: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' + // CHECK-NO-LIFETIMES: nonescapable.h:24:6: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' g(nil) h1(nil) - // CHECK: nonescapable.h:34:21: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership - // CHECK-NO-LIFETIMES: nonescapable.h:34:21: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' + // CHECK-NO-LIFETIMES: nonescapable.h:34:21: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' h2(nil) - // CHECK: nonescapable.h:35:21: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership - // CHECK-NO-LIFETIMES: nonescapable.h:35:21: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' + // CHECK-NO-LIFETIMES: nonescapable.h:35:21: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' h3(nil) i1() // CHECK: nonescapable.h:39:39: error: template parameter 'Missing' does not exist @@ -129,22 +126,18 @@ public func noAnnotations() -> View { // CHECK: nonescapable.h:45:33: error: template parameter 'S' expected to be a type parameter // CHECK-NO-LIFETIMES: nonescapable.h:45:33: error: template parameter 'S' expected to be a type parameter j1() - // CHECK: nonescapable.h:63:41: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership - // CHECK-NO-LIFETIMES: nonescapable.h:63:41: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' + // CHECK-NO-LIFETIMES: nonescapable.h:63:41: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' j2() - // CHECK: nonescapable.h:64:41: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership - // CHECK-NO-LIFETIMES: nonescapable.h:64:41: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' + // CHECK-NO-LIFETIMES: nonescapable.h:64:41: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' j3() k1(); - // CHECK: nonescapable.h:70:15: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership - // CHECK-NO-LIFETIMES: nonescapable.h:70:15: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' + // CHECK-NO-LIFETIMES: nonescapable.h:70:15: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' k2(); - // CHECK: nonescapable.h:71:22: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership - // CHECK-NO-LIFETIMES: nonescapable.h:71:22: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' + // CHECK-NO-LIFETIMES: nonescapable.h:71:22: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' k3(); l1(); - // CHECK: nonescapable.h:77:12: error: cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership - // CHECK-NO-LIFETIMES: nonescapable.h:77:12: error: returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence' + // CHECK: nonescapable.h:77:12: error: a function with a ~Escapable result needs a parameter to depend on + // CHECK-NO-LIFETIMES: nonescapable.h:77:12: error: a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence' l2(); return View() } @@ -163,4 +156,4 @@ public func test3(_ x: inout View) { // CHECK-NOT: error // CHECK-NOT: warning // CHECK-NO-LIFETIMES-NOT: error - // CHECK-NO-LIFETIMES-NOT: warning \ No newline at end of file + // CHECK-NO-LIFETIMES-NOT: warning diff --git a/test/Interop/Cxx/class/safe-interop-mode.swift b/test/Interop/Cxx/class/safe-interop-mode.swift index b8ffb5ba68053..fab1c93fbc307 100644 --- a/test/Interop/Cxx/class/safe-interop-mode.swift +++ b/test/Interop/Cxx/class/safe-interop-mode.swift @@ -21,7 +21,7 @@ module Test { struct SWIFT_NONESCAPABLE View { __attribute__((swift_attr("@lifetime(immortal)"))) View() : member(nullptr) {} - __attribute__((swift_attr("@lifetime(p)"))) + __attribute__((swift_attr("@lifetime(copy p)"))) View(const int *p [[clang::lifetimebound]]) : member(p) {} View(const View&) = default; private: diff --git a/test/Interop/Cxx/stdlib/std-span-interface.swift b/test/Interop/Cxx/stdlib/std-span-interface.swift index 5b1cabbeb8bcd..904c7d84e9fd5 100644 --- a/test/Interop/Cxx/stdlib/std-span-interface.swift +++ b/test/Interop/Cxx/stdlib/std-span-interface.swift @@ -35,11 +35,11 @@ import CxxStdlib // CHECK-NEXT: mutating func foo(_ s: std.{{.*}}span<__cxxConst, _C{{.*}}_{{.*}}>) // CHECK-NEXT: } // CHECK: @_alwaysEmitIntoClient public func funcWithSafeWrapper(_ s: Span) -// CHECK-NEXT: @lifetime(s) +// CHECK-NEXT: @lifetime(copy s) // CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper2(_ s: Span) -> Span // CHECK-NEXT: @lifetime(borrow v) // CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func funcWithSafeWrapper3(_ v: borrowing VecOfInt) -> Span -// CHECK-NEXT: @lifetime(p) +// CHECK-NEXT: @lifetime(copy p) // CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper1(_ p: Span) -> Span // CHECK-NEXT: @lifetime(borrow v) // CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func mixedFuncWithSafeWrapper2(_ v: borrowing VecOfInt, _ len: Int32) -> Span diff --git a/test/Macros/SwiftifyImport/CountedBy/PointerReturn.swift b/test/Macros/SwiftifyImport/CountedBy/PointerReturn.swift index 5b8cdac51e6b6..ba721430b9c21 100644 --- a/test/Macros/SwiftifyImport/CountedBy/PointerReturn.swift +++ b/test/Macros/SwiftifyImport/CountedBy/PointerReturn.swift @@ -28,7 +28,7 @@ func lifetimeDependentBorrow(_ p: borrowing UnsafePointer, _ len1: CInt, _ // CHECK-NEXT: func nonEscaping(_ len: CInt) -> UnsafeBufferPointer { // CHECK-NEXT: return unsafe UnsafeBufferPointer (start: nonEscaping(len), count: Int(len)) -// CHECK: @_alwaysEmitIntoClient @lifetime(p) +// CHECK: @_alwaysEmitIntoClient @lifetime(copy p) // CHECK-NEXT: func lifetimeDependentCopy(_ p: Span, _ len2: CInt) -> Span { // CHECK-NEXT: return unsafe Span (_unsafeStart: p.withUnsafeBufferPointer { _pPtr in // CHECK-NEXT: return unsafe lifetimeDependentCopy(_pPtr.baseAddress!, CInt(exactly: p.count)!, len2) diff --git a/test/Macros/SwiftifyImport/CxxSpan/LifetimeboundSpan.swift b/test/Macros/SwiftifyImport/CxxSpan/LifetimeboundSpan.swift index 95bfc14955ad9..9da6abb3a8610 100644 --- a/test/Macros/SwiftifyImport/CxxSpan/LifetimeboundSpan.swift +++ b/test/Macros/SwiftifyImport/CxxSpan/LifetimeboundSpan.swift @@ -32,27 +32,27 @@ struct X { func myFunc5() -> SpanOfInt {} } -// CHECK: @_alwaysEmitIntoClient @lifetime(span) +// CHECK: @_alwaysEmitIntoClient @lifetime(copy span) // CHECK-NEXT: func myFunc(_ span: Span) -> Span { -// CHECK-NEXT: return unsafe _unsafeRemoveLifetime(Span(_unsafeCxxSpan: myFunc(SpanOfInt(span)))) +// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc(SpanOfInt(span))), copying: ()) // CHECK-NEXT: } // CHECK: @_alwaysEmitIntoClient @lifetime(borrow vec) @_disfavoredOverload // CHECK-NEXT: func myFunc2(_ vec: borrowing VecOfInt) -> Span { -// CHECK-NEXT: return unsafe _unsafeRemoveLifetime(Span(_unsafeCxxSpan: myFunc2(vec))) +// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc2(vec)), copying: ()) // CHECK-NEXT: } -// CHECK: @_alwaysEmitIntoClient @lifetime(span1, span2) +// CHECK: @_alwaysEmitIntoClient @lifetime(copy span1, copy span2) // CHECK-NEXT: func myFunc3(_ span1: Span, _ span2: Span) -> Span { -// CHECK-NEXT: return unsafe _unsafeRemoveLifetime(Span(_unsafeCxxSpan: myFunc3(SpanOfInt(span1), SpanOfInt(span2)))) +// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc3(SpanOfInt(span1), SpanOfInt(span2))), copying: ()) // CHECK-NEXT: } -// CHECK: @_alwaysEmitIntoClient @lifetime(borrow vec, span) +// CHECK: @_alwaysEmitIntoClient @lifetime(borrow vec, copy span) // CHECK-NEXT: func myFunc4(_ vec: borrowing VecOfInt, _ span: Span) -> Span { -// CHECK-NEXT: return unsafe _unsafeRemoveLifetime(Span(_unsafeCxxSpan: myFunc4(vec, SpanOfInt(span)))) +// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc4(vec, SpanOfInt(span))), copying: ()) // CHECK-NEXT: } // CHECK: @_alwaysEmitIntoClient @lifetime(borrow self) @_disfavoredOverload // CHECK-NEXT: func myFunc5() -> Span { -// CHECK-NEXT: return unsafe _unsafeRemoveLifetime(Span(_unsafeCxxSpan: myFunc5())) +// CHECK-NEXT: return unsafe _cxxOverrideLifetime(Span(_unsafeCxxSpan: myFunc5()), copying: ()) // CHECK-NEXT: } diff --git a/test/ModuleInterface/Inputs/lifetime_dependence.swift b/test/ModuleInterface/Inputs/lifetime_dependence.swift index f0f4a1af719be..8c04ab6867d72 100644 --- a/test/ModuleInterface/Inputs/lifetime_dependence.swift +++ b/test/ModuleInterface/Inputs/lifetime_dependence.swift @@ -10,7 +10,7 @@ internal func _overrideLifetime( _ dependent: consuming T, copying source: borrowing U) -> T { dependent @@ -43,7 +43,7 @@ public struct BufferView : ~Escapable { self = _overrideLifetime(bv, borrowing: a) } @inlinable - @lifetime(a) + @lifetime(copy a) internal init(_ ptr: UnsafeRawBufferPointer, _ a: consuming AnotherView) { let bv = BufferView(ptr, a._count) self = _overrideLifetime(bv, copying: a) @@ -51,7 +51,7 @@ public struct BufferView : ~Escapable { } @inlinable -@lifetime(x) +@lifetime(copy x) public func derive(_ x: consuming BufferView) -> BufferView { let pointer = x._ptr let bv = BufferView(pointer, x._count) @@ -62,7 +62,7 @@ public func derive(_ x: consuming BufferView) -> BufferView { public func use(_ x: consuming BufferView) {} @inlinable -@lifetime(view) +@lifetime(copy view) public func consumeAndCreate(_ view: consuming BufferView) -> BufferView { let pointer = view._ptr let bv = BufferView(pointer, view._count) @@ -70,7 +70,7 @@ public func consumeAndCreate(_ view: consuming BufferView) -> BufferView { } @inlinable -@lifetime(this, that) +@lifetime(copy this, copy that) public func deriveThisOrThat(_ this: consuming BufferView, _ that: consuming BufferView) -> BufferView { if (Int.random(in: 1..<100) == 0) { return BufferView(this._ptr, this._count) diff --git a/test/ModuleInterface/lifetime_dependence_test.swift b/test/ModuleInterface/lifetime_dependence_test.swift index 0813c15ade351..b705ee4cd4480 100644 --- a/test/ModuleInterface/lifetime_dependence_test.swift +++ b/test/ModuleInterface/lifetime_dependence_test.swift @@ -24,16 +24,16 @@ import lifetime_dependence // CHECK: @lifetime(borrow a) // CHECK-NEXT: @inlinable internal init(_ ptr: Swift.UnsafeRawBufferPointer, _ a: borrowing Swift.Array) { -// CHECK: @lifetime(a) +// CHECK: @lifetime(copy a) // CHECK-NEXT: @inlinable internal init(_ ptr: Swift.UnsafeRawBufferPointer, _ a: consuming lifetime_dependence.AnotherView) { -// CHECK: @lifetime(x) +// CHECK: @lifetime(copy x) // CHECK-NEXT: @inlinable public func derive(_ x: consuming lifetime_dependence.BufferView) -> lifetime_dependence.BufferView { -// CHECK: @lifetime(view) +// CHECK: @lifetime(copy view) // CHECK-NEXT: @inlinable public func consumeAndCreate(_ view: consuming lifetime_dependence.BufferView) -> lifetime_dependence.BufferView { -// CHECK: @lifetime(this, that) +// CHECK: @lifetime(copy this, copy that) // CHECK-NEXT: @inlinable public func deriveThisOrThat(_ this: consuming lifetime_dependence.BufferView, _ that: consuming lifetime_dependence.BufferView) -> lifetime_dependence.BufferView { // Check that an implicitly dependent variable accessor is guarded by LifetimeDependence. diff --git a/test/ModuleInterface/nonescapable_types.swift b/test/ModuleInterface/nonescapable_types.swift index 11934ff994f6e..4ea5418897f9a 100644 --- a/test/ModuleInterface/nonescapable_types.swift +++ b/test/ModuleInterface/nonescapable_types.swift @@ -50,19 +50,19 @@ public enum Y: ~Escapable { extension Y: Escapable where T: Escapable { } // CHECK: #if compiler(>=5.3) && $NonescapableTypes -// CHECK: @lifetime(y) +// CHECK: @lifetime(copy y) // CHECK: public func derive(_ y: Test.Y) -> Test.Y where T : ~Escapable // CHECK: #endif -@lifetime(y) +@lifetime(copy y) public func derive(_ y: Y) -> Y { y } // CHECK: #if compiler(>=5.3) && $NonescapableTypes -// CHECK: @lifetime(x) +// CHECK: @lifetime(copy x) // CHECK: public func derive(_ x: Test.X) -> Test.X where T : ~Escapable // CHECK: #endif -@lifetime(x) +@lifetime(copy x) public func derive(_ x: X) -> X { x } diff --git a/test/Parse/inverse_escapable_feature.swift b/test/Parse/inverse_escapable_feature.swift index 247462fc4439f..3709c541f83b0 100644 --- a/test/Parse/inverse_escapable_feature.swift +++ b/test/Parse/inverse_escapable_feature.swift @@ -1,4 +1,7 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift \ +// RUN: -enable-experimental-feature LifetimeDependence + +// REQUIRES: swift_feature_LifetimeDependence struct S: ~Escapable {} diff --git a/test/Parse/lifetime_attr.swift b/test/Parse/lifetime_attr.swift index 5940db6a7f0b9..f7cb627a89436 100644 --- a/test/Parse/lifetime_attr.swift +++ b/test/Parse/lifetime_attr.swift @@ -6,12 +6,12 @@ struct E {} struct NE : ~Escapable {} -@lifetime(ne) +@lifetime(copy ne) func derive(_ ne: NE) -> NE { ne } -@lifetime(borrow ne1, ne2) +@lifetime(borrow ne1, copy ne2) func derive(_ ne1: NE, _ ne2: NE) -> NE { if (Int.random(in: 1..<100) < 50) { return ne1 @@ -19,13 +19,13 @@ func derive(_ ne1: NE, _ ne2: NE) -> NE { return ne2 } -@lifetime // expected-error{{expected '(' after lifetime dependence specifier}} -func testMissingLParenError(_ ne: 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)'}} ne } @lifetime() // expected-error{{expected identifier, index or self in lifetime dependence specifier}} -func testMissingDependence(_ ne: NE) -> NE { +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)'}} ne } diff --git a/test/Runtime/demangleToMetadata.swift b/test/Runtime/demangleToMetadata.swift index 3dc04953cf8b3..49df5df76c272 100644 --- a/test/Runtime/demangleToMetadata.swift +++ b/test/Runtime/demangleToMetadata.swift @@ -1,9 +1,11 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -target %target-swift-5.1-abi-triple -parse-stdlib %s -module-name main -o %t/a.out +// RUN: %target-build-swift -target %target-swift-5.1-abi-triple -parse-stdlib %s -module-name main -o %t/a.out \ +// RUN: -enable-experimental-feature LifetimeDependence // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out // REQUIRES: executable_test // REQUIRES: concurrency +// REQUIRES: swift_feature_LifetimeDependence // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime diff --git a/test/SIL/Parser/basic2_noncopyable_generics.sil b/test/SIL/Parser/basic2_noncopyable_generics.sil index 96e341ddd572b..092986d125f0d 100644 --- a/test/SIL/Parser/basic2_noncopyable_generics.sil +++ b/test/SIL/Parser/basic2_noncopyable_generics.sil @@ -29,6 +29,7 @@ struct NCG : ~Copyable { // CHECK-NEXT: var t: T struct NEG : ~Escapable { var t: T + @lifetime(copy t) init(_ t: consuming T) { self.t = t } diff --git a/test/SIL/explicit_lifetime_dependence_specifiers.swift b/test/SIL/explicit_lifetime_dependence_specifiers.swift index de943b0be194a..1d74064d689ca 100644 --- a/test/SIL/explicit_lifetime_dependence_specifiers.swift +++ b/test/SIL/explicit_lifetime_dependence_specifiers.swift @@ -21,7 +21,7 @@ internal func _overrideLifetime< } @_unsafeNonescapableResult -@lifetime(source) +@lifetime(copy source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -56,12 +56,12 @@ struct BufferView : ~Escapable { self.ptr = ptr } // CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers10BufferViewVyACSW_AA7WrapperVtcfC : $@convention(method) (UnsafeRawBufferPointer, @owned Wrapper, @thin BufferView.Type) -> @lifetime(copy 1) @owned BufferView { - @lifetime(a) + @lifetime(copy a) init(_ ptr: UnsafeRawBufferPointer, _ a: consuming Wrapper) { self.ptr = ptr } // CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers10BufferViewVyACSW_AA7WrapperVSaySiGhtcfC : $@convention(method) (UnsafeRawBufferPointer, @owned Wrapper, @guaranteed Array, @thin BufferView.Type) -> @lifetime(copy 1, borrow 2) @owned BufferView { - @lifetime(a, borrow b) + @lifetime(copy a, borrow b) init(_ ptr: UnsafeRawBufferPointer, _ a: consuming Wrapper, _ b: borrowing Array) { self.ptr = ptr } @@ -93,14 +93,14 @@ func derive(_ x: borrowing BufferView) -> BufferView { } // CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers16consumeAndCreateyAA10BufferViewVADnF : $@convention(thin) (@owned BufferView) -> @lifetime(copy 0) @owned BufferView { -@lifetime(x) +@lifetime(copy x) func consumeAndCreate(_ x: consuming BufferView) -> BufferView { let bv = BufferView(independent: x.ptr) return _overrideLifetime(bv, copying: x) } // CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers17deriveThisOrThat1yAA10BufferViewVAD_ADtF : $@convention(thin) (@guaranteed BufferView, @guaranteed BufferView) -> @lifetime(copy 1, borrow 0) @owned BufferView { -@lifetime(borrow this, that) +@lifetime(borrow this, copy that) func deriveThisOrThat1(_ this: borrowing BufferView, _ that: borrowing BufferView) -> BufferView { if (Int.random(in: 1..<100) == 0) { return BufferView(independent: this.ptr) @@ -109,7 +109,7 @@ func deriveThisOrThat1(_ this: borrowing BufferView, _ that: borrowing BufferVie } // CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers17deriveThisOrThat2yAA10BufferViewVAD_ADntF : $@convention(thin) (@guaranteed BufferView, @owned BufferView) -> @lifetime(copy 1, borrow 0) @owned BufferView { -@lifetime(borrow this, that) +@lifetime(borrow this, copy that) func deriveThisOrThat2(_ this: borrowing BufferView, _ that: consuming BufferView) -> BufferView { if (Int.random(in: 1..<100) == 0) { return BufferView(independent: this.ptr) @@ -122,6 +122,7 @@ func use(_ x: borrowing BufferView) {} struct Wrapper : ~Escapable { let view: BufferView + @lifetime(copy view) init(_ view: consuming BufferView) { self.view = view } @@ -132,7 +133,7 @@ struct Wrapper : ~Escapable { } // CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers7WrapperV8getView2AA10BufferViewVyF : $@convention(method) (@owned Wrapper) -> @lifetime(copy 0) @owned BufferView { - @lifetime(self) + @lifetime(copy self) consuming func getView2() -> BufferView { return view } @@ -147,7 +148,7 @@ struct Container : ~Escapable { } // CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers16getConsumingViewyAA06BufferG0VAA9ContainerVnF : $@convention(thin) (@owned Container) -> @lifetime(copy 0) @owned BufferView { -@lifetime(x) +@lifetime(copy x) func getConsumingView(_ x: consuming Container) -> BufferView { let bv = BufferView(independent: x.ptr) return _overrideLifetime(bv, copying: x) diff --git a/test/SIL/implicit_lifetime_dependence.swift b/test/SIL/implicit_lifetime_dependence.swift index 0d8d64eb18b3e..181235c3b7daa 100644 --- a/test/SIL/implicit_lifetime_dependence.swift +++ b/test/SIL/implicit_lifetime_dependence.swift @@ -18,7 +18,7 @@ internal func _overrideLifetime< } @_unsafeNonescapableResult -@lifetime(source) +@lifetime(copy source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -43,16 +43,19 @@ struct BufferView : ~Escapable { self.c = c } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence10BufferViewVyA2ChcfC : $@convention(method) (@guaranteed BufferView, @thin BufferView.Type) -> @lifetime(copy 0) @owned BufferView { + @lifetime(copy otherBV) init(_ otherBV: borrowing BufferView) { self.ptr = otherBV.ptr self.c = otherBV.c } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence10BufferViewVyA2CcfC : $@convention(method) (@owned BufferView, @thin BufferView.Type) -> @lifetime(copy 0) @owned BufferView { + @lifetime(copy otherBV) init(_ otherBV: consuming BufferView) { self.ptr = otherBV.ptr self.c = otherBV.c } -// CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence10BufferViewVyACSW_SaySiGhtcfC : $@convention(method) (UnsafeRawBufferPointer, @guaranteed Array, @thin BufferView.Type) -> @lifetime(borrow 1) @owned BufferView { +// CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence10BufferViewVyACSW_SaySiGhtcfC : $@convention(method) (UnsafeRawBufferPointer, @guaranteed Array, @thin BufferView.Type) -> @lifetime(borrow 0) @owned BufferView { + @lifetime(borrow ptr) init(_ ptr: UnsafeRawBufferPointer, _ a: borrowing Array) { self.ptr = ptr self.c = a.count @@ -80,15 +83,18 @@ func testBasic() { } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence6deriveyAA10BufferViewVADF : $@convention(thin) (@guaranteed BufferView) -> @lifetime(copy 0) @owned BufferView { +@lifetime(copy x) func derive(_ x: borrowing BufferView) -> BufferView { return BufferView(x.ptr, x.c) } +@lifetime(copy x) func derive(_ unused: Int, _ x: borrowing BufferView) -> BufferView { return BufferView(independent: x.ptr, x.c) } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence16consumeAndCreateyAA10BufferViewVADnF : $@convention(thin) (@owned BufferView) -> @lifetime(copy 0) @owned BufferView { +@lifetime(copy x) func consumeAndCreate(_ x: consuming BufferView) -> BufferView { let bv = BufferView(independent: x.ptr, x.c) return _overrideLifetime(bv, copying: x) @@ -100,24 +106,29 @@ struct Wrapper : ~Escapable { var _view: BufferView var view: BufferView { // CHECK: sil hidden @$s28implicit_lifetime_dependence7WrapperV4viewAA10BufferViewVvr : $@yield_once @convention(method) (@guaranteed Wrapper) -> @lifetime(copy 0) @yields @guaranteed BufferView { + @lifetime(copy self) _read { yield _view } -// CHECK: sil hidden @$s28implicit_lifetime_dependence7WrapperV4viewAA10BufferViewVvM : $@yield_once @convention(method) (@inout Wrapper) -> @lifetime(copy 0) @yields @inout BufferView { +// CHECK: sil hidden @$s28implicit_lifetime_dependence7WrapperV4viewAA10BufferViewVvM : $@yield_once @convention(method) (@inout Wrapper) -> @lifetime(borrow 0) @yields @inout BufferView { + @lifetime(borrow self) _modify { yield &_view } } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence7WrapperVyAcA10BufferViewVcfC : $@convention(method) (@owned BufferView, @thin Wrapper.Type) -> @lifetime(copy 0) @owned Wrapper { + @lifetime(copy view) init(_ view: consuming BufferView) { self._view = view } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence7WrapperV8getView1AA10BufferViewVyKF : $@convention(method) (@guaranteed Wrapper) -> @lifetime(copy 0) (@owned BufferView, @error any Error) { + @lifetime(copy self) borrowing func getView1() throws -> BufferView { return _view } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence7WrapperV8getView2AA10BufferViewVyYaKF : $@convention(method) @async (@owned Wrapper) -> @lifetime(copy 0) (@owned BufferView, @error any Error) { + @lifetime(copy self) consuming func getView2() async throws -> BufferView { return _view } @@ -185,6 +196,7 @@ struct GenericBufferView : ~Escapable { } // CHECK: sil hidden @$s28implicit_lifetime_dependence17GenericBufferViewVyACyxGAA9FakeRangeVySVGcig : $@convention(method) (FakeRange, @guaranteed GenericBufferView) -> @lifetime(copy 1) @owned GenericBufferView { subscript(bounds: FakeRange) -> Self { + @lifetime(copy self) get { let pointer = UnsafeRawPointer(bounds.lowerBound) let result = GenericBufferView( @@ -198,6 +210,7 @@ struct GenericBufferView : ~Escapable { } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence23tupleLifetimeDependenceyAA10BufferViewV_ADtADF : $@convention(thin) (@guaranteed BufferView) -> @lifetime(copy 0) (@owned BufferView, @owned BufferView) { +@lifetime(copy x) func tupleLifetimeDependence(_ x: borrowing BufferView) -> (BufferView, BufferView) { return (BufferView(x.ptr, x.c), BufferView(x.ptr, x.c)) } @@ -206,27 +219,31 @@ public struct OuterNE: ~Escapable { // A public property generates an implicit setter with an infered dependence on 'newValue'. // // [inner1.setter] - // CHECK-LABEL: sil [transparent] @$s28implicit_lifetime_dependence7OuterNEV6inner1AC05InnerE0Vvs : $@convention(method) (@owned OuterNE.InnerNE, @lifetime(copy 0) @inout OuterNE) -> () { + // CHECK-LABEL: sil [transparent] @$s28implicit_lifetime_dependence7OuterNEV6inner1AC05InnerE0Vvs : $@convention(method) (@owned OuterNE.InnerNE, @lifetime(copy 0, copy 1) @inout OuterNE) -> () { public var inner1: InnerNE // Explicit setter with an infered dependence on 'newValue'. public var inner2: InnerNE { + @lifetime(copy self) get { inner1 } + @lifetime(self: copy newValue) set { inner1 = newValue } } public struct InnerNE: ~Escapable { + @lifetime(copy owner) init( owner: borrowing Owner ) {} } + @lifetime(copy owner) init(owner: borrowing Owner) { self.inner1 = InnerNE(owner: owner) } // CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence7OuterNEV8setInner5valueyAC0gE0V_tF : $@convention(method) (@guaranteed OuterNE.InnerNE, @lifetime(copy 0) @inout OuterNE) -> () { - @lifetime(self: value) + @lifetime(self: copy value) mutating func setInner(value: InnerNE) { self.inner1 = value } diff --git a/test/SIL/lifetime_dependence_param_position_test.swift b/test/SIL/lifetime_dependence_param_position_test.swift index c1101d7124802..6988bfbd3a89b 100644 --- a/test/SIL/lifetime_dependence_param_position_test.swift +++ b/test/SIL/lifetime_dependence_param_position_test.swift @@ -8,6 +8,7 @@ public struct Span : ~Escapable { private var baseAddress: UnsafeRawPointer public let count: Int + @lifetime(copy owner) public init( baseAddress: UnsafeRawPointer, count: Int, diff --git a/test/SIL/lifetime_dependence_span_lifetime_attr.swift b/test/SIL/lifetime_dependence_span_lifetime_attr.swift index 94550e8ba1a6d..4d478b19b2523 100644 --- a/test/SIL/lifetime_dependence_span_lifetime_attr.swift +++ b/test/SIL/lifetime_dependence_span_lifetime_attr.swift @@ -18,7 +18,7 @@ internal func _overrideLifetime< } @_unsafeNonescapableResult -@lifetime(source) +@lifetime(copy source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -83,7 +83,7 @@ public struct Span : ~Escapable { public let count: Int private var baseAddress: UnsafeRawPointer { start._rawValue } // CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanV11baseAddress5count9dependsOnACyxGSV_Siqd__htcRi_d__Ri0_d__lufC : $@convention(method) (UnsafeRawPointer, Int, @in_guaranteed Owner, @thin Span.Type) -> @lifetime(copy 2) @owned Span { - @lifetime(owner) + @lifetime(copy owner) public init( baseAddress: UnsafeRawPointer, count: Int, @@ -94,7 +94,7 @@ public struct Span : ~Escapable { ) } // CHECK-LABEL: sil hidden @$s025lifetime_dependence_span_A5_attr4SpanV5start5count9dependsOnACyxGAA0E5IndexVyxG_Siqd__htcRi_d__Ri0_d__lufC : $@convention(method) (SpanIndex, Int, @in_guaranteed Owner, @thin Span.Type) -> @lifetime(copy 2) @owned Span { - @lifetime(owner) + @lifetime(copy owner) init( start index: SpanIndex, count: Int, @@ -150,7 +150,7 @@ extension Span { // CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanVyACyxGAA9FakeRangeVyAA0E5IndexVyxGGcig : $@convention(method) (FakeRange>, @guaranteed Span) -> @lifetime(copy 1) @owned Span { public subscript(bounds: FakeRange>) -> Self { - @lifetime(self) + @lifetime(copy self) get { let span = Span( start: bounds.lowerBound, @@ -161,7 +161,7 @@ extension Span { } // CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanV6prefix4upToACyxGAA0E5IndexVyxG_tF : $@convention(method) (SpanIndex, @guaranteed Span) -> @lifetime(copy 1) @owned Span { - @lifetime(self) + @lifetime(copy self) borrowing public func prefix(upTo index: SpanIndex) -> Self { index == startIndex ? Self(start: start, count: 0, dependsOn: copy self) @@ -169,14 +169,14 @@ extension Span { } // CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanV6prefix7throughACyxGAA0E5IndexVyxG_tF : $@convention(method) (SpanIndex, @guaranteed Span) -> @lifetime(copy 1) @owned Span { - @lifetime(self) + @lifetime(copy self) borrowing public func prefix(through index: Index) -> Self { let nc = distance(from: startIndex, to: index) &+ 1 return Self(start: start, count: nc, dependsOn: copy self) } // CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanV6prefixyACyxGSiF : $@convention(method) (Int, @owned Span) -> @lifetime(copy 1) @owned Span { - @lifetime(self) + @lifetime(copy self) consuming public func prefix(_ maxLength: Int) -> Self { precondition(maxLength >= 0, "Can't have a prefix of negative length.") let nc = maxLength < count ? maxLength : count diff --git a/test/SILGen/bitwise_copyable_stdlib.swift b/test/SILGen/bitwise_copyable_stdlib.swift index 687f9da614dee..66621a40e371c 100644 --- a/test/SILGen/bitwise_copyable_stdlib.swift +++ b/test/SILGen/bitwise_copyable_stdlib.swift @@ -7,9 +7,11 @@ // RUN: -module-name Swift \ // RUN: -disable-availability-checking \ // RUN: -enable-experimental-feature BuiltinModule \ +// RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -enable-builtin-module // REQUIRES: swift_feature_BuiltinModule +// REQUIRES: swift_feature_LifetimeDependence // Force verification of TypeLowering's isTrivial. diff --git a/test/SILGen/typelowering_inverses.swift b/test/SILGen/typelowering_inverses.swift index 0383df9a55ca8..93845bd0aaf48 100644 --- a/test/SILGen/typelowering_inverses.swift +++ b/test/SILGen/typelowering_inverses.swift @@ -135,7 +135,7 @@ func check(_ t: borrowing any NoEscapeP & ~Escapable) {} // CHECK: sil hidden [ossa] @$s4main5checkyyAA9NoEscapeP_pRi0_s_XPnF : $@convention(thin) (@in any NoEscapeP & ~Escapable) -> () { func check(_ t: consuming any NoEscapeP & ~Escapable) {} -// CHECK: sil hidden [ossa] @$s4main5checkyyAA9NoEscapeP_pRi0_s_XPzF : $@convention(thin) (@inout any NoEscapeP & ~Escapable) -> () { +// CHECK: sil hidden [ossa] @$s4main5checkyyAA9NoEscapeP_pRi0_s_XPzF : $@convention(thin) (@lifetime(copy 0) @inout any NoEscapeP & ~Escapable) -> () { func check(_ t: inout any NoEscapeP & ~Escapable) {} // MARK: conditionally Copyable & Escapable SILGen diff --git a/test/SILOptimizer/Inputs/SpanExtras.swift b/test/SILOptimizer/Inputs/SpanExtras.swift index 63db1f96e9763..5775ccce3417d 100644 --- a/test/SILOptimizer/Inputs/SpanExtras.swift +++ b/test/SILOptimizer/Inputs/SpanExtras.swift @@ -25,7 +25,7 @@ internal func _overrideLifetime< @_unsafeNonescapableResult @_alwaysEmitIntoClient @_transparent -@lifetime(source) +@lifetime(copy source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -184,6 +184,7 @@ extension MutableSpan where Element: BitwiseCopyable { extension Span where Element: ~Copyable { @_alwaysEmitIntoClient + @lifetime(borrow mutableSpan) public init(_unsafeMutableSpan mutableSpan: borrowing MutableSpan) { let pointer = mutableSpan._pointer?.assumingMemoryBound(to: Element.self) let buffer = UnsafeBufferPointer(start: pointer, count: mutableSpan.count) @@ -215,6 +216,7 @@ extension MutableSpan where Element: ~Copyable { extension RawSpan { @_alwaysEmitIntoClient + @lifetime(borrow mutableSpan) public init( _unsafeMutableSpan mutableSpan: borrowing MutableSpan ) { @@ -286,7 +288,10 @@ extension MutableSpan where Element: BitwiseCopyable { /// - Returns: a RawSpan over the memory represented by this span @unsafe //FIXME: remove when the lifetime inference is fixed @_alwaysEmitIntoClient - public var _unsafeRawSpan: RawSpan { RawSpan(_unsafeMutableSpan: self) } + public var _unsafeRawSpan: RawSpan { + @lifetime(borrow self) + get { RawSpan(_unsafeMutableSpan: self) } + } } @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, visionOS 9999, *) @@ -300,10 +305,12 @@ extension MutableSpan where Element: ~Copyable { /// - Complexity: O(1) @_alwaysEmitIntoClient public subscript(_ position: Index) -> Element { + @lifetime(borrow self) _read { precondition(indices.contains(position), "index out of bounds") yield self[unchecked: position] } + @lifetime(borrow self) _modify { precondition(indices.contains(position), "index out of bounds") yield &self[unchecked: position] @@ -323,6 +330,7 @@ extension MutableSpan where Element: ~Copyable { unsafeAddress { UnsafePointer(_unsafeAddressOfElement(unchecked: position)) } + @lifetime(self: copy self) unsafeMutableAddress { _unsafeAddressOfElement(unchecked: position) } @@ -342,12 +350,14 @@ extension MutableSpan where Element: ~Copyable { @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, visionOS 9999, *) extension MutableSpan where Element: ~Copyable { + @lifetime(self: copy self) public mutating func swapAt(_ i: Index, _ j: Index) { precondition(indices.contains(Index(i))) precondition(indices.contains(Index(j))) swapAt(unchecked: i, unchecked: j) } + @lifetime(self: copy self) public mutating func swapAt(unchecked i: Index, unchecked j: Index) { let pi = _unsafeAddressOfElement(unchecked: i) let pj = _unsafeAddressOfElement(unchecked: j) @@ -372,6 +382,7 @@ extension MutableSpan where Element: BitwiseCopyable { precondition(indices.contains(position)) return self[unchecked: position] } + @lifetime(self: copy self) set { precondition(indices.contains(position)) self[unchecked: position] = newValue @@ -392,6 +403,7 @@ extension MutableSpan where Element: BitwiseCopyable { let offset = position&*MemoryLayout.stride return _start().loadUnaligned(fromByteOffset: offset, as: Element.self) } + @lifetime(self: copy self) set { let offset = position&*MemoryLayout.stride _start().storeBytes(of: newValue, toByteOffset: offset, as: Element.self) @@ -412,6 +424,7 @@ extension MutableSpan where Element: ~Copyable { //FIXME: mark closure parameter as non-escaping @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func withUnsafeMutableBufferPointer( _ body: (UnsafeMutableBufferPointer) throws(E) -> Result ) throws(E) -> Result { @@ -440,6 +453,7 @@ extension MutableSpan where Element: BitwiseCopyable { //FIXME: mark closure parameter as non-escaping @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func withUnsafeMutableBytes( _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result ) throws(E) -> Result { @@ -456,6 +470,7 @@ extension MutableSpan where Element: BitwiseCopyable { extension MutableSpan { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update(repeating repeatedValue: Element) { _start().withMemoryRebound(to: Element.self, capacity: count) { $0.update(repeating: repeatedValue, count: count) @@ -463,6 +478,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from source: S ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element { @@ -472,6 +488,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from elements: inout some IteratorProtocol ) -> Index { @@ -485,6 +502,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: some Collection ) -> Index { @@ -505,6 +523,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update(fromContentsOf source: Span) -> Index { guard !source.isEmpty else { return 0 } precondition( @@ -520,6 +539,7 @@ extension MutableSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: borrowing MutableSpan ) -> Index { @@ -532,6 +552,7 @@ extension MutableSpan { extension MutableSpan where Element: BitwiseCopyable { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( repeating repeatedValue: Element ) where Element: BitwiseCopyable { @@ -544,6 +565,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from source: S ) -> (unwritten: S.Iterator, index: Index) @@ -554,6 +576,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( from elements: inout some IteratorProtocol ) -> Index { @@ -567,6 +590,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: some Collection ) -> Index where Element: BitwiseCopyable { @@ -587,6 +611,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: Span ) -> Index where Element: BitwiseCopyable { @@ -604,6 +629,7 @@ extension MutableSpan where Element: BitwiseCopyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func update( fromContentsOf source: borrowing MutableSpan ) -> Index where Element: BitwiseCopyable { @@ -667,6 +693,7 @@ extension OutputSpan where Element: ~Copyable { @available(macOS 9999, *) @available(macOS 9999, *) @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func append(_ value: consuming Element) { precondition(_initialized < capacity, "Output buffer overflow") let p = _start.advanced(by: _initialized&*MemoryLayout.stride) @@ -696,6 +723,7 @@ extension OutputSpan where Element: ~Copyable { extension OutputSpan { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func append(repeating repeatedValue: Element, count: Int) { let available = capacity &- _initialized precondition( @@ -711,6 +739,7 @@ extension OutputSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func append( from elements: S ) -> S.Iterator where S: Sequence, S.Element == Element { @@ -720,6 +749,7 @@ extension OutputSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func append( from elements: inout some IteratorProtocol ) { @@ -732,6 +762,7 @@ extension OutputSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func append( fromContentsOf source: some Collection ) { @@ -762,6 +793,7 @@ extension OutputSpan { } //FIXME: remove once rdar://136838539 & rdar://136849171 are fixed + @lifetime(self: copy self) public mutating func append( fromContentsOf source: UnsafeBufferPointer ) { @@ -779,6 +811,7 @@ extension OutputSpan { //FIXME: rdar://136838539 & rdar://136849171 @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func append( fromContentsOf source: Span ) { @@ -797,6 +830,7 @@ extension OutputSpan { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func append(fromContentsOf source: borrowing MutableSpan) { source.withUnsafeBufferPointer { append(fromContentsOf: $0) } } @@ -806,6 +840,7 @@ extension OutputSpan { extension OutputSpan where Element: ~Copyable { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: consuming Self ) { @@ -824,6 +859,7 @@ extension OutputSpan where Element: ~Copyable { } @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: UnsafeMutableBufferPointer ) { @@ -849,6 +885,7 @@ extension OutputSpan where Element: ~Copyable { extension OutputSpan { @_alwaysEmitIntoClient + @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: Slice> ) { @@ -875,9 +912,11 @@ extension OutputSpan where Element: ~Copyable { } } + /* FIXME: rdar://147194789 ([nonescapable] 'mutating get' causes a + type checking error for non-existent _read accessor) @_alwaysEmitIntoClient public var mutableSpan: MutableSpan { -// @lifetime(inout self) // this just crashes the compiler + @lifetime(borrow self) mutating get { // the accessor must provide a mutable projection let pointer = _pointer?.assumingMemoryBound(to: Element.self) let buffer = UnsafeMutableBufferPointer(start: pointer, count: _initialized) @@ -885,6 +924,7 @@ extension OutputSpan where Element: ~Copyable { return _overrideLifetime(span, mutating: &self) } } + */ } @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, visionOS 9999, *) diff --git a/test/SILOptimizer/lifetime_dependence/coroutine.swift b/test/SILOptimizer/lifetime_dependence/coroutine.swift index 19a9e353accef..3ee01ab89185b 100644 --- a/test/SILOptimizer/lifetime_dependence/coroutine.swift +++ b/test/SILOptimizer/lifetime_dependence/coroutine.swift @@ -13,6 +13,7 @@ struct View : ~Escapable { self.ptr = ptr self.c = c } + @lifetime(copy otherBV) init(_ otherBV: borrowing View) { self.ptr = otherBV.ptr self.c = otherBV.c @@ -23,6 +24,7 @@ struct View : ~Escapable { } // This overload requires a separate label because overloading // on borrowing/consuming attributes is not allowed + @lifetime(copy k) init(consumingView k: consuming View) { self.ptr = k.ptr self.c = k.c @@ -34,13 +36,16 @@ struct Wrapper : ~Escapable { // Nested coroutine access. var view: View { + @lifetime(copy self) _read { yield _view } + @lifetime(borrow self) _modify { yield &_view } } + @lifetime(copy view) init(_ view: consuming View) { self._view = view } diff --git a/test/SILOptimizer/lifetime_dependence/initializer.swift b/test/SILOptimizer/lifetime_dependence/initializer.swift index c0590b8403ddb..13f0ad684d453 100644 --- a/test/SILOptimizer/lifetime_dependence/initializer.swift +++ b/test/SILOptimizer/lifetime_dependence/initializer.swift @@ -28,6 +28,7 @@ struct Span: ~Escapable { struct Wrapper: ~Escapable { private let span: Span + @lifetime(copy span) init(span: borrowing Span) { self.span = copy span } @@ -41,6 +42,7 @@ struct SuperWrapper: ~Escapable { // Make sure that LocalVariableUtils can successfully analyze 'self'. That's required to determine that the assignment // of `wrapper` is returned without escaping + @lifetime(copy span) init(span: borrowing Span) { self.wrapper = Wrapper(span: span) } diff --git a/test/SILOptimizer/lifetime_dependence/inout.swift b/test/SILOptimizer/lifetime_dependence/inout.swift index c0590b8403ddb..13f0ad684d453 100644 --- a/test/SILOptimizer/lifetime_dependence/inout.swift +++ b/test/SILOptimizer/lifetime_dependence/inout.swift @@ -28,6 +28,7 @@ struct Span: ~Escapable { struct Wrapper: ~Escapable { private let span: Span + @lifetime(copy span) init(span: borrowing Span) { self.span = copy span } @@ -41,6 +42,7 @@ struct SuperWrapper: ~Escapable { // Make sure that LocalVariableUtils can successfully analyze 'self'. That's required to determine that the assignment // of `wrapper` is returned without escaping + @lifetime(copy span) init(span: borrowing Span) { self.wrapper = Wrapper(span: span) } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_borrow.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_borrow.swift index 1ac698a1d08d5..a3fbd35c50c60 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_borrow.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_borrow.swift @@ -45,7 +45,7 @@ struct MBV : ~Escapable, ~Copyable { } // Requires a borrow. - @lifetime(self) + @lifetime(copy self) borrowing func getBV() -> BV { BV(p, i) } @@ -55,25 +55,26 @@ struct MBV : ~Escapable, ~Copyable { struct NEBV : ~Escapable { var bv: BV + @lifetime(copy bv) init(_ bv: consuming BV) { self.bv = bv } } // Propagate a borrow. -@lifetime(container) +@lifetime(copy container) func bv_get_borrow(container: borrowing MBV) -> BV { container.getBV() } // Copy a borrow. -@lifetime(container) +@lifetime(copy container) func bv_get_copy(container: borrowing MBV) -> BV { return container.getBV() } // Recognize nested accesses as part of the same dependence scope. -@lifetime(container) +@lifetime(copy container) func bv_get_mutate(container: inout MBV) -> BV { container.getBV() } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_borrow_fail.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_borrow_fail.swift index 0338303da8287..7878b675e82e9 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_borrow_fail.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_borrow_fail.swift @@ -60,6 +60,7 @@ struct NE : ~Escapable { } } +@lifetime(copy container) func bv_get_consume(container: consuming NE) -> BV { return container.getBV() // expected-error {{lifetime-dependent value escapes its scope}} // expected-note @-1{{it depends on this scoped access to variable 'container'}} @@ -70,12 +71,12 @@ struct Wrapper : ~Escapable { let bv: BV } -@lifetime(bv2) +@lifetime(copy bv2) func bv_incorrect_annotation1(_ bv1: borrowing BV, _ bv2: borrowing BV) -> BV { // expected-error {{lifetime-dependent variable 'bv1' escapes its scope}} return copy bv1 // expected-note @-1{{it depends on the lifetime of argument 'bv1'}} } // expected-note @-1{{this use causes the lifetime-dependent value to escape}} -@lifetime(w2) +@lifetime(copy w2) func bv_incorrect_annotation2(_ w1: borrowing Wrapper, _ w2: borrowing Wrapper) -> BV { // expected-error {{lifetime-dependent variable 'w1' escapes its scope}} return w1.bv // expected-note @-1{{it depends on the lifetime of argument 'w1'}} } // expected-note @-1{{this use causes the lifetime-dependent value to escape}} diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_diagnostics.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_diagnostics.swift index 91d03eb769c60..012870003132a 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_diagnostics.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_diagnostics.swift @@ -17,7 +17,7 @@ struct BV : ~Escapable { } } -@lifetime(bv) +@lifetime(copy bv) func bv_copy(_ bv: borrowing BV) -> BV { copy bv } @@ -30,7 +30,7 @@ public struct NEInt: ~Escapable { var i: Int // Test yielding an address. - // CHECK-LABEL: sil hidden @$s4test5NEIntV5ipropSivM : $@yield_once @convention(method) (@inout NEInt) -> @yields @inout Int { + // CHECK-LABEL: sil hidden @$s4test5NEIntV5ipropSivM : $@yield_once @convention(method) (@inout NEInt) -> @lifetime(borrow 0) @yields @inout Int // CHECK: bb0(%0 : $*NEInt): // CHECK: [[A:%.*]] = begin_access [modify] [static] %0 : $*NEInt // CHECK: [[E:%.*]] = struct_element_addr [[A]] : $*NEInt, #NEInt.i @@ -39,7 +39,9 @@ public struct NEInt: ~Escapable { // CHECK: end_access [[A]] : $*NEInt // CHECK-LABEL: } // end sil function '$s4test5NEIntV5ipropSivM' var iprop: Int { + @lifetime(copy self) _read { yield i } + @lifetime(borrow self) _modify { yield &i } } @@ -56,6 +58,7 @@ public enum NEOptional: ~Escapable { extension NEOptional where Wrapped: ~Escapable { // Test that enum initialization passes diagnostics. + @lifetime(copy some) public init(_ some: consuming Wrapped) { self = .some(some) } } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_generic.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_generic.swift index e3d9d416c2dee..b4fb8f6ccedfb 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_generic.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_generic.swift @@ -52,7 +52,7 @@ struct NCInt: ~Copyable { struct NEInt: ~Escapable { let value: Builtin.Int64 - @lifetime(o) + @lifetime(copy o) init(v: Builtin.Int64, o: borrowing O) { self.value = v } @@ -64,17 +64,17 @@ struct NEInt: ~Escapable { } } -@lifetime(ne) +@lifetime(copy ne) public func consume_indirect(ne: consuming NE) -> NE { return ne } -@lifetime(ne) +@lifetime(copy ne) public func copy_indirect(ne: borrowing NE) -> NE { return copy ne } -@lifetime(ne) +@lifetime(copy ne) public func copy_inout(ne: inout NE) -> NE { return copy ne } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_inherit.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_inherit.swift index a922dc2d9e919..04c2c95600965 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_inherit.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_inherit.swift @@ -11,7 +11,7 @@ // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence should be expressed by a builtin that is // hidden within the function body. @_unsafeNonescapableResult -@lifetime(source) +@lifetime(copy source) func unsafeLifetime( dependent: consuming T, dependsOn source: borrowing U) -> T { @@ -34,7 +34,7 @@ struct BV : ~Escapable { self.i = i } - @lifetime(self) + @lifetime(copy self) consuming func derive() -> BV { // Technically, this "new" view does not depend on the 'view' argument. // This unsafely creates a new view with no dependence. @@ -48,14 +48,17 @@ struct NEBV : ~Escapable { var bv: BV // Test lifetime inheritance through initialization. + @lifetime(copy bv) init(_ bv: consuming BV) { self.bv = bv } var view: BV { + @lifetime(copy self) _read { yield bv } + @lifetime(borrow self) _modify { yield &bv } @@ -68,18 +71,18 @@ struct NEBV : ~Escapable { } // Test lifetime inheritance through chained consumes. -@lifetime(bv) +@lifetime(copy bv) func bv_derive(bv: consuming BV) -> BV { bv.derive() } // Test lifetime inheritance through stored properties. -@lifetime(nebv) +@lifetime(copy nebv) func ne_extract_member(nebv: consuming NEBV) -> BV { return nebv.bv } -@lifetime(nebv) +@lifetime(copy nebv) func ne_yield_member(nebv: consuming NEBV) -> BV { return nebv.view } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_inherit_fail.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_inherit_fail.swift index 8e5a51d85cd53..fa22ab268c9ea 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_inherit_fail.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_inherit_fail.swift @@ -11,7 +11,7 @@ // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence should be expressed by a builtin that is // hidden within the function body. @_unsafeNonescapableResult -@lifetime(source) +@lifetime(copy source) func unsafeLifetime( dependent: consuming T, dependsOn source: borrowing U) -> T { @@ -46,7 +46,7 @@ struct BV : ~Escapable { struct NE : ~Escapable { var bv: BV - @lifetime(bv) + @lifetime(copy bv) init(_ bv: consuming BV) { self.bv = bv } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift index c50141d61a936..b1b28f35b416a 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_mutate.swift @@ -27,7 +27,7 @@ internal func _overrideLifetime< /// the `source` argument. @_unsafeNonescapableResult @_transparent -@lifetime(source) +@lifetime(copy source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -91,7 +91,10 @@ struct MutableSpan : ~Escapable, ~Copyable { } } - var iterator: Iter { Iter(base: base, count: count) } + var iterator: Iter { + @lifetime(copy self) + get { Iter(base: base, count: count) } + } } extension Array where Element == Int { diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_optional.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_optional.swift index 254f317fea471..291c4421eebf4 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_optional.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_optional.swift @@ -38,6 +38,7 @@ extension Nillable: ExpressibleByNilLiteral where Wrapped: ~Copyable & ~Escapabl extension Nillable where Wrapped: ~Copyable & ~Escapable { @_transparent + @lifetime(copy some) public init(_ some: consuming Wrapped) { self = .some(some) } } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param.swift index 4770f17da9f6e..3299545d1146c 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param.swift @@ -11,7 +11,7 @@ // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence should be expressed by a builtin that is // hidden within the function body. @_unsafeNonescapableResult -@lifetime(source) +@lifetime(copy source) func unsafeLifetime( dependent: consuming T, dependsOn source: borrowing U) -> T { @@ -38,7 +38,7 @@ struct BV : ~Escapable { public var isEmpty: Bool { i == 0 } // Test consuming `self` - @lifetime(self) + @lifetime(copy self) consuming func derive() -> BV { // Technically, this "new" view does not depend on the 'view' argument. // This unsafely creates a new view with no dependence. @@ -51,7 +51,7 @@ struct BV : ~Escapable { struct NE { var bv: BV - @lifetime(bv) + @lifetime(copy bv) init(_ bv: BV) { self.bv = bv } diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param_fail.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param_fail.swift index 9a8e0ca6b1e6e..30608bf43840d 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param_fail.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param_fail.swift @@ -33,44 +33,47 @@ struct NC : ~Copyable { struct NE { var bv: BV - @lifetime(bv) + @lifetime(copy bv) init(_ bv: consuming BV) { self.bv = bv } } -func bv_assign_inout(bv: BV, other: inout BV) { // expected-error{{lifetime-dependent variable 'bv' escapes its scope}} - // expected-note @-1 {{it depends on the lifetime of argument 'bv'}} - other = bv // expected-note {{this use causes the lifetime-dependent value to escape}} +@lifetime(other: copy bv) +func bv_assign_inout_copy(bv: BV, other: inout BV) { + other = bv // OK +} + +@lifetime(other: borrow bv) +func bv_assign_inout_borrow(bv: BV, other: inout BV) { + other = bv } +@lifetime(bv: copy bv) +@lifetime(other: copy bv) func bvmut_assign_inout(bv: inout BV, other: inout BV) { - other = bv // expected-error{{lifetime-dependent value escapes its scope}} - // expected-note @-2 {{it depends on the lifetime of argument 'bv'}} - // expected-note @-2 {{this use causes the lifetime-dependent value to escape}} + other = bv } +@lifetime(other: copy bv) func bvcons_assign_inout(bv: consuming BV, other: inout BV) { - other = bv // expected-error{{lifetime-dependent value escapes its scope}} - // expected-note @-2 {{it depends on the lifetime of argument 'bv'}} - // expected-note @-2 {{this use causes the lifetime-dependent value to escape}} + other = bv } -func bv_assign_field(bv: BV, other: inout NE) { // expected-error{{lifetime-dependent variable 'bv' escapes its scope}} - // expected-note @-1 {{it depends on the lifetime of argument 'bv'}} - other.bv = bv // expected-note {{this use causes the lifetime-dependent value to escape}} +@lifetime(other: copy bv) +func bv_assign_field(bv: BV, other: inout NE) { + other.bv = bv } +@lifetime(bv: copy bv) +@lifetime(other: copy bv) func bvmut_assign_field(bv: inout BV, other: inout NE) { - other.bv = bv // expected-error{{lifetime-dependent value escapes its scope}} - // expected-note @-2 {{it depends on the lifetime of argument 'bv'}} - // expected-note @-2 {{this use causes the lifetime-dependent value to escape}} + other.bv = bv } +@lifetime(other: copy bv) func bvcons_assign_field(bv: consuming BV, other: inout NE) { - other.bv = bv // expected-error{{lifetime-dependent value escapes its scope}} - // expected-note @-2 {{it depends on the lifetime of argument 'bv'}} - // expected-note @-2 {{this use causes the lifetime-dependent value to escape}} + other.bv = bv } func bv_capture_escape(bv: BV) -> ()->Int { // expected-error{{lifetime-dependent variable 'bv' escapes its scope}} diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift index a3737c923b0d4..cbcf3ff5789d5 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_scope_fixup.swift @@ -22,6 +22,7 @@ struct View : ~Escapable { self.ptr = ptr self.c = c } + @lifetime(copy otherBV) init(_ otherBV: borrowing View) { self.ptr = otherBV.ptr self.c = otherBV.c @@ -32,6 +33,7 @@ struct View : ~Escapable { } // This overload requires a separate label because overloading // on borrowing/consuming attributes is not allowed + @lifetime(copy k) init(consumingView k: consuming View) { self.ptr = k.ptr self.c = k.c @@ -46,6 +48,7 @@ struct MutableView : ~Copyable, ~Escapable { self.ptr = ptr self.c = c } + @lifetime(copy otherBV) init(_ otherBV: borrowing View) { self.ptr = otherBV.ptr self.c = otherBV.c @@ -63,7 +66,7 @@ func consume(_ o : consuming View) {} func use(_ o : borrowing MutableView) {} func consume(_ o : consuming MutableView) {} -@lifetime(x) +@lifetime(copy x) func getConsumingView(_ x: consuming View) -> View { return View(consumingView: x) } @@ -190,13 +193,16 @@ func test8(_ a: Array) { struct Wrapper : ~Escapable { var _view: View var view: View { + @lifetime(copy self) _read { yield _view } + @lifetime(borrow self) _modify { yield &_view } } + @lifetime(copy view) init(_ view: consuming View) { self._view = view } @@ -212,6 +218,7 @@ func test9() { } } +@lifetime(copy x) func getViewTuple(_ x: borrowing View) -> (View, View) { return (View(x.ptr, x.c), View(x.ptr, x.c)) } @@ -236,4 +243,5 @@ public func test10() { func testPointeeDependenceOnMutablePointer(p: UnsafePointer) { var ptr = p _ = ptr.pointee + _ = ptr } diff --git a/test/SILOptimizer/lifetime_dependence/semantics.swift b/test/SILOptimizer/lifetime_dependence/semantics.swift index 28e25744614f7..4dfcec2565417 100644 --- a/test/SILOptimizer/lifetime_dependence/semantics.swift +++ b/test/SILOptimizer/lifetime_dependence/semantics.swift @@ -33,7 +33,7 @@ internal func _overrideLifetime< /// the `source` argument. @_unsafeNonescapableResult @_transparent -@lifetime(source) +@lifetime(copy source) internal func _overrideLifetime< T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable >( @@ -58,6 +58,8 @@ internal func _overrideLifetime< dependent } +struct NotEscapable: ~Escapable {} + // Lifetime dependence semantics by example. public struct Span: ~Escapable { private var base: UnsafePointer? @@ -83,6 +85,7 @@ public struct Span: ~Escapable { } extension Span { + @lifetime(copy self) consuming func dropFirst() -> Span { let nextPointer = self.base.flatMap { $0 + 1 } let local = Span(base: nextPointer, count: self.count - 1) @@ -91,6 +94,7 @@ extension Span { } extension Span { + @lifetime(copy self) mutating func droppingPrefix(length: Int) -> /* */ Span { let oldBase = base let result = Span(base: oldBase, count: length) @@ -231,6 +235,7 @@ struct Outer { func parse(_ span: Span) {} +@lifetime(copy arg) func copySpan(_ arg: Span) -> /* */ Span { arg } @lifetime(borrow arg) @@ -272,6 +277,14 @@ struct Container { let count: Int } +// Dependence on an empty initialized value should be scoped to variable decl. +@lifetime(copy x) +func f(x: NotEscapable) -> NotEscapable { + let local = NotEscapable() // expected-error {{lifetime-dependent variable 'local' escapes its scope}} + // expected-note @-1{{it depends on the lifetime of this parent value}} + return local // expected-note {{this use causes the lifetime-dependent value to escape}} +} + // ============================================================================= // Scoped dependence on values // ============================================================================= diff --git a/test/SILOptimizer/lifetime_dependence/spanofspans.swift b/test/SILOptimizer/lifetime_dependence/spanofspans.swift index 177911a4ca22d..7714ad6edb831 100644 --- a/test/SILOptimizer/lifetime_dependence/spanofspans.swift +++ b/test/SILOptimizer/lifetime_dependence/spanofspans.swift @@ -11,7 +11,7 @@ // TODO: uncomment the @lifetime annotations when we have component lifetimes. -// @lifetime(elements) +// @lifetime(copy elements) struct Span: ~Escapable { // Pretend that 'element' is in separate storage. var element: T @@ -22,7 +22,7 @@ struct Span: ~Escapable { } } -// @lifetime(elements) +// @lifetime(copy elements) extension Array { // @lifetime(span: borrow self) // @lifetime(span.elements: copy self.elements) @@ -34,7 +34,7 @@ extension Array { } // use 'scalars' instead of 'elements' to avoid confusion from nesting -// @lifetime(scalars) +// @lifetime(copy scalars) struct Vec: ~Escapable { // Pretend that 't' is in separate storage. var scalar: T diff --git a/test/SILOptimizer/moveonly_addresschecker.swift b/test/SILOptimizer/moveonly_addresschecker.swift index c1cbd2d625b40..f3331c0f84da9 100644 --- a/test/SILOptimizer/moveonly_addresschecker.swift +++ b/test/SILOptimizer/moveonly_addresschecker.swift @@ -64,11 +64,13 @@ struct TestCoroAccessorOfCoroAccessor : ~Copyable & ~Escapable { var t: T var inner: TestCoroAccessorOfCoroAccessor { + @lifetime(copy self) _read { fatalError() } } var outer: TestCoroAccessorOfCoroAccessor { + @lifetime(copy self) _read { yield inner } diff --git a/test/SILOptimizer/mutable_span_bounds_check_tests.swift b/test/SILOptimizer/mutable_span_bounds_check_tests.swift index 41dc480bc1b58..db1de2c39c006 100644 --- a/test/SILOptimizer/mutable_span_bounds_check_tests.swift +++ b/test/SILOptimizer/mutable_span_bounds_check_tests.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module-path %t/SpanExtras.swiftmodule %S/Inputs/SpanExtras.swift -enable-builtin-module -enable-experimental-feature LifetimeDependence -O -// RUN: %target-swift-frontend -I %t -O -emit-sil %s -disable-availability-checking | %FileCheck %s --check-prefix=CHECK-SIL -// RUN: %target-swift-frontend -I %t -O -emit-ir %s -disable-availability-checking | %FileCheck %s --check-prefix=CHECK-IR +// RUN: %target-swift-frontend -I %t -O -emit-sil %s -enable-experimental-feature LifetimeDependence -disable-availability-checking | %FileCheck %s --check-prefix=CHECK-SIL +// RUN: %target-swift-frontend -I %t -O -emit-ir %s -enable-experimental-feature LifetimeDependence -disable-availability-checking | %FileCheck %s --check-prefix=CHECK-IR // REQUIRES: swift_in_compiler // REQUIRES: swift_feature_LifetimeDependence @@ -44,6 +44,7 @@ public func span_zero_init(_ output: inout MutableSpan) { // CHECK-IR: define {{.*}} void @"$s31mutable_span_bounds_check_tests0B14_copy_elemwiseyy10SpanExtras07MutableH0VySiGz_s0H0VySiGtF" // CHECK-IR: vector.body // CHECK-IR: store <{{.*}}> +@lifetime(output: copy output, copy input) public func span_copy_elemwise(_ output: inout MutableSpan, _ input: Span) { precondition(output.count >= input.count) for i in input.indices { @@ -65,6 +66,7 @@ public func span_copy_elemwise(_ output: inout MutableSpan, _ input: Span +@lifetime(output: copy output, copy input) public func span_append_elemwise(_ output: inout OutputSpan, _ input: Span) { for i in input.indices { output.append(input[i]) @@ -83,6 +85,7 @@ public func span_append_elemwise(_ output: inout OutputSpan, _ input: Span< // CHECK-IR: define {{.*}} void @"$s31mutable_span_bounds_check_tests0B12_sum_wo_trapyy10SpanExtras07MutableI0VySiGz_s0I0VySiGAItF" // CHECK-IR: vector.body // CHECK-IR: store <{{.*}}> +@lifetime(output: copy output, copy input1, copy input2) public func span_sum_wo_trap(_ output: inout MutableSpan, _ input1: Span, _ input2: Span) { precondition(input1.count == input2.count) precondition(output.count == input1.count) @@ -97,7 +100,7 @@ public func span_sum_wo_trap(_ output: inout MutableSpan, _ input1: Span, _ input1: Span, _ input2: Span) { precondition(input1.count == input2.count) precondition(output.count == input1.count) diff --git a/test/SILOptimizer/span_bounds_check_tests.swift b/test/SILOptimizer/span_bounds_check_tests.swift index 3c2797e2105fc..0f0f9c0b356ae 100644 --- a/test/SILOptimizer/span_bounds_check_tests.swift +++ b/test/SILOptimizer/span_bounds_check_tests.swift @@ -328,6 +328,7 @@ public func mutate_span(_ v: inout Span) { } // CHECK-SIL-NOT: cond_fail {{.*}}, "Index out of bounds" // CHECK-SIL: cond_br // CHECK-SIL-LABEL: } // end sil function '$s23span_bounds_check_tests06inout_A33_sum_iterate_to_unknown_with_trapySis4SpanVySiGz_SitF' +@lifetime(v: copy v) public func inout_span_sum_iterate_to_unknown_with_trap(_ v: inout Span, _ n: Int) -> Int { var sum = 0 for i in 0...n { @@ -342,6 +343,7 @@ public func inout_span_sum_iterate_to_unknown_with_trap(_ v: inout Span, _ // CHECK-SIL: cond_fail {{.*}}, "Index out of bounds" // CHECK-SIL: cond_br // CHECK-SIL-LABEL: } // end sil function '$s23span_bounds_check_tests06inout_A41_sum_iterate_to_unknown_with_trap_dontoptySis4SpanVySiGz_SitF' +@lifetime(v: copy v) public func inout_span_sum_iterate_to_unknown_with_trap_dontopt(_ v: inout Span, _ n: Int) -> Int { var sum = 0 for i in 0...n { diff --git a/test/Sema/bitwise_copyable.swift b/test/Sema/bitwise_copyable.swift index 0140b4c74e83b..80c1f20290f0d 100644 --- a/test/Sema/bitwise_copyable.swift +++ b/test/Sema/bitwise_copyable.swift @@ -1,10 +1,12 @@ // RUN: %target-typecheck-verify-swift \ // RUN: -disable-availability-checking \ // RUN: -enable-experimental-feature Sensitive \ +// RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -enable-builtin-module \ // RUN: -debug-diagnostic-names // REQUIRES: swift_feature_Sensitive +// REQUIRES: swift_feature_LifetimeDependence //============================================================================== //===========================DEPENDENCY-FREE TESTS=(BEGIN)===================={{ @@ -212,9 +214,7 @@ struct S_Explicit_With_2BitwiseCopyable_Generic_Optional : var o2: T? } -// TODO: When the standard library is built with NonescapableTypes, this should -// be uncommented. -//struct S_Explicit_Nonescapable : ~Escapable, BitwiseCopyable {} +struct S_Explicit_Nonescapable : ~Escapable, BitwiseCopyable {} struct S_Explicit_Noncopyable : ~Copyable, BitwiseCopyable {} // expected-error{{type_does_not_conform}} diff --git a/test/Sema/bitwise_copyable_2.swift b/test/Sema/bitwise_copyable_2.swift index b765c93ed5cde..b380bc84ea8cb 100644 --- a/test/Sema/bitwise_copyable_2.swift +++ b/test/Sema/bitwise_copyable_2.swift @@ -1,7 +1,10 @@ // RUN: %target-typecheck-verify-swift \ // RUN: -enable-builtin-module \ +// RUN: -enable-experimental-feature LifetimeDependence \ // RUN: -debug-diagnostic-names +// REQUIRES: swift_feature_LifetimeDependence + // This test file only exists in order to test without noncopyable_generics and can be deleted once that is always enabled. @_nonescapable diff --git a/test/Sema/implicit_lifetime_dependence.swift b/test/Sema/implicit_lifetime_dependence.swift index c1db77777abef..6b3617a1f724f 100644 --- a/test/Sema/implicit_lifetime_dependence.swift +++ b/test/Sema/implicit_lifetime_dependence.swift @@ -12,7 +12,7 @@ struct BufferView : ~Escapable, ~Copyable { } } -struct ImplicitInit1 : ~Escapable { // expected-error{{cannot infer lifetime dependence on implicit initializer, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} +struct ImplicitInit1 : ~Escapable { let ptr: UnsafeRawBufferPointer } @@ -20,20 +20,21 @@ struct ImplicitInit2 : ~Escapable, ~Copyable { let mbv: BufferView } -struct ImplicitInit3 : ~Escapable, ~Copyable { // expected-error{{cannot infer lifetime dependence on implicit initializer, multiple parameters qualifiy as a candidate}} +struct ImplicitInit3 : ~Escapable, ~Copyable { let mbv1: BufferView let mbv2: BufferView } -func foo1() -> BufferView { // expected-error{{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} +func foo1() -> BufferView { // expected-error{{a function with a ~Escapable result needs a parameter to depend on}} + // expected-note@-1{{'@lifetime(immortal)' can be used to indicate that values produced by this initializer have no lifetime dependencies}} return BufferView(nil, 0) } -func foo2(_ i: borrowing Int) -> BufferView { // expected-error{{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} +func foo2(_ i: borrowing Int) -> BufferView { return BufferView(nil, 0) } -func foo3(arg: borrowing T) -> BufferView { // expected-error{{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} +func foo3(arg: borrowing T) -> BufferView { return BufferView(nil, 0) } diff --git a/test/Sema/lifetime_attr.swift b/test/Sema/lifetime_attr.swift index 83517231aa9af..4c31c7f6ec784 100644 --- a/test/Sema/lifetime_attr.swift +++ b/test/Sema/lifetime_attr.swift @@ -3,16 +3,16 @@ // REQUIRES: swift_feature_LifetimeDependence struct NE : ~Escapable { - @lifetime(self) // expected-error{{invalid lifetime dependence on self in an initializer}} + @lifetime(copy self) // expected-error{{invalid lifetime dependence specifier on non-existent self}} init() {} } -@lifetime(nonexisting) // expected-error{{invalid parameter name specified 'nonexisting'}} +@lifetime(copy nonexisting) // expected-error{{invalid parameter name specified 'nonexisting'}} func invalidAttrOnNonExistingParam(_ ne: NE) -> NE { ne } -@lifetime(self) // expected-error{{invalid lifetime dependence specifier on non-existent self}} +@lifetime(copy self) // expected-error{{invalid lifetime dependence specifier on non-existent self}} func invalidAttrOnNonExistingSelf(_ ne: NE) -> NE { ne } @@ -22,7 +22,7 @@ func invalidAttrOnNonExistingParamIndex(_ ne: NE) -> NE { ne } -@lifetime(ne, ne) // expected-error{{duplicate lifetime dependence specifier}} +@lifetime(copy ne, borrow ne) // expected-error{{duplicate lifetime dependence specifier}} func invalidDuplicateLifetimeDependence1(_ ne: borrowing NE) -> NE { ne } @@ -34,8 +34,8 @@ func invalidDependence(_ x: consuming Klass) -> NE { NE() } -@lifetime(result: source) -@lifetime(result: source) // TODO: display error here +@lifetime(result: copy source) +@lifetime(result: borrow source) // TODO: display error here func invalidTarget(_ result: inout NE, _ source: consuming NE) { // expected-error{{invalid duplicate target lifetime dependencies on function}} result = source } @@ -59,3 +59,7 @@ do { t[keyPath: \.v2] // expected-error {{key path cannot refer to nonescapable type 'Test'}} expected-error {{key path cannot refer to nonescapable type 'NE'}} } } + +// 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'}} diff --git a/test/Sema/lifetime_attr_nofeature.swift b/test/Sema/lifetime_attr_nofeature.swift index 1c6f821f731a8..65834cb459675 100644 --- a/test/Sema/lifetime_attr_nofeature.swift +++ b/test/Sema/lifetime_attr_nofeature.swift @@ -1,10 +1,10 @@ // RUN: %target-typecheck-verify-swift -disable-availability-checking // REQUIRES: asserts -struct NE : ~Escapable { +struct NE : ~Escapable { // expected-error{{an implicit initializer with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence'}} } -@lifetime(ne) // expected-error{{'@lifetime' attribute is only valid when experimental feature LifetimeDependence is enabled}} expected-error{{expected declaration}} -func derive(_ ne: NE) -> NE { // expected-error{{returning ~Escapable type requires '-enable-experimental-feature LifetimeDependence'}} +@lifetime(copy ne) // expected-error{{'@lifetime' attribute is only valid when experimental feature LifetimeDependence is enabled}} expected-error{{expected declaration}} +func derive(_ ne: NE) -> NE { // expected-error{{a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence'}} ne } diff --git a/test/Sema/lifetime_depend_infer.swift b/test/Sema/lifetime_depend_infer.swift new file mode 100644 index 0000000000000..26d2786d2c3d4 --- /dev/null +++ b/test/Sema/lifetime_depend_infer.swift @@ -0,0 +1,537 @@ +// RUN: %target-swift-frontend %s -emit-sil \ +// RUN: -o /dev/null \ +// RUN: -verify \ +// RUN: -sil-verify-all \ +// RUN: -module-name test \ +// RUN: -enable-experimental-feature LifetimeDependence + +// REQUIRES: swift_feature_LifetimeDependence + +// Coverage testing for LifetimeDependence inferrence logic. The tests are grouped according to the design of +// LifetimeDependenceChecker. + +class C {} + +struct NE: ~Escapable {} + +struct NEImmortal: ~Escapable { + @lifetime(immortal) + init() {} +} + +// ============================================================================= +// Handle non-Escapable results with 'self' +// ============================================================================= + +struct NonEscapableSelf: ~Escapable { + func methodNoParam() -> 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)'}} + + @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 methodNoParamLifetime() -> NonEscapableSelf { self } + + @lifetime(copy self) // OK + func methodNoParamCopy() -> NonEscapableSelf { self } + + @lifetime(borrow self) // OK + func methodNoParamBorrow() -> NonEscapableSelf { self } + + mutating func mutatingMethodNoParam() -> NonEscapableSelf { self } // expected-error{{a mutating method with a ~Escapable result requires '@lifetime(...)'}} + // expected-error@-1{{a mutating method with a ~Escapable 'self' requires '@lifetime(self: ...)'}} + + @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 mutatingMethodNoParamLifetime() -> NonEscapableSelf { self } + + @lifetime(copy self) // OK + mutating func mutatingMethodNoParamCopy() -> NonEscapableSelf { self } + + @lifetime(borrow self) // OK + mutating func mutatingMethodNoParamBorrow() -> NonEscapableSelf { self } + + func methodOneParam(_: Int) -> NonEscapableSelf { self } // expected-error{{a method with a ~Escapable result requires '@lifetime(...)'}} + + @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 methodOneParamLifetime(_: Int) -> NonEscapableSelf { self } + + @lifetime(copy self) // OK + func methodOneParamCopy(_: Int) -> NonEscapableSelf { self } + + @lifetime(borrow self) // OK + func methodOneParamBorrow(_: Int) -> NonEscapableSelf { self } + + mutating func mutatingMethodOneParam(_: Int) -> NonEscapableSelf { self } // expected-error{{a mutating method with a ~Escapable result requires '@lifetime(...)'}} + // expected-error@-1{{a mutating method with a ~Escapable 'self' requires '@lifetime(self: ...)'}} + + @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 mutatingMethodOneParamLifetime(_: Int) -> NonEscapableSelf { self } + + @lifetime(copy self) // OK + mutating func mutatingMethodOneParamCopy(_: Int) -> NonEscapableSelf { self } + + @lifetime(borrow self) // OK + mutating func mutatingMethodOneParamBorrow(_: Int) -> NonEscapableSelf { self } +} + +struct EscapableTrivialSelf { + func methodNoParam() -> NEImmortal { NEImmortal() } // expected-error{{cannot infer lifetime dependence on a method because 'self' is BitwiseCopyable}} + + @lifetime(self) // OK + func methodNoParamLifetime() -> NEImmortal { NEImmortal() } + + @lifetime(copy self) // expected-error{{cannot copy the lifetime of an Escapable type, use '@lifetime(borrow self)' instead}} + func methodNoParamCopy() -> NEImmortal { NEImmortal() } + + @lifetime(borrow self) // OK + func methodNoParamBorrow() -> NEImmortal { NEImmortal() } + + func mutatingMethodNoParam() -> NEImmortal { NEImmortal() } // expected-error{{cannot infer lifetime dependence on a method because 'self' is BitwiseCopyable}} + + @lifetime(self) // OK + mutating func mutatingMethodNoParamLifetime() -> NEImmortal { NEImmortal() } + + @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) + mutating func mutatingMethodNoParamBorrow() -> NEImmortal { NEImmortal() } + + func methodOneParam(_: Int) -> NEImmortal { NEImmortal() } // expected-error{{a method with a ~Escapable result requires '@lifetime(...)'}} + + @lifetime(self) + func methodOneParamLifetime(_: Int) -> NEImmortal { NEImmortal() } + + @lifetime(copy self) // expected-error{{cannot copy the lifetime of an Escapable type, use '@lifetime(borrow self)' instead}} + func methodOneParamCopy(_: Int) -> NEImmortal { NEImmortal() } + + @lifetime(borrow self) + func methodOneParamBorrow(_: Int) -> NEImmortal { NEImmortal() } + + mutating func mutatingMethodOneParam(_: Int) -> NEImmortal { NEImmortal() } // expected-error{{a mutating method with a ~Escapable result requires '@lifetime(...)'}} + + @lifetime(self) + mutating func mutatingMethodOneParamLifetime(_: Int) -> NEImmortal { NEImmortal() } + + @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) + mutating func mutatingMethodOneParamBorrow(_: Int) -> NEImmortal { NEImmortal() } +} + +struct EscapableNonTrivialSelf { + let c: C + + init(c: C) { self.c = c } + + func methodNoParam() -> NEImmortal { NEImmortal() } + + @lifetime(self) + func methodNoParamLifetime() -> NEImmortal { NEImmortal() } + + @lifetime(copy self) // expected-error{{cannot copy the lifetime of an Escapable type, use '@lifetime(borrow self)' instead}} + func methodNoParamCopy() -> NEImmortal { NEImmortal() } + + @lifetime(borrow self) + func methodNoParamBorrow() -> NEImmortal { NEImmortal() } + + func mutatingMethodNoParam() -> NEImmortal { NEImmortal() } + + @lifetime(self) + mutating func mutatingMethodNoParamLifetime() -> NEImmortal { NEImmortal() } + + @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) + mutating func mutatingMethodNoParamBorrow() -> NEImmortal { NEImmortal() } + + func methodOneParam(_: Int) -> NEImmortal { NEImmortal() } // expected-error{{a method with a ~Escapable result requires '@lifetime(...)'}} + + @lifetime(self) + func methodOneParamLifetime(_: Int) -> NEImmortal { NEImmortal() } + + @lifetime(copy self) // expected-error{{cannot copy the lifetime of an Escapable type, use '@lifetime(borrow self)' instead}} + func methodOneParamCopy(_: Int) -> NEImmortal { NEImmortal() } + + @lifetime(borrow self) + func methodOneParamBorrow(_: Int) -> NEImmortal { NEImmortal() } + + mutating func mutatingMethodOneParam(_: Int) -> NEImmortal { NEImmortal() } // expected-error{{a mutating method with a ~Escapable result requires '@lifetime(...)'}} + + @lifetime(self) + mutating func mutatingMethodOneParamLifetime(_: Int) -> NEImmortal { NEImmortal() } + + @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) + mutating func mutatingMethodOneParamBorrow(_: Int) -> NEImmortal { NEImmortal() } +} + +// ============================================================================= +// Handle non-Escapable results which must depend on a parameter +// (for initializers and stand-alone functions) +// ============================================================================= + +struct NonescapableInitializers: ~Escapable { + var c: C + + init() { c = C() } // expected-error{{an initializer with a ~Escapable result needs a parameter to depend on}} + // expected-note@-1{{'@lifetime(immortal)' can be used to indicate that values produced by this initializer have no lifetime dependencies}} + + init(c: C) { self.c = c } // expected-error{{cannot borrow the lifetime of 'c', which has consuming ownership on an initializer}} + + init(c: C, _: Int) { self.c = c } // expected-error{{an initializer with a ~Escapable result requires '@lifetime(...)'}} + + init(ne: NE) { c = C() } // expected-error{{cannot infer the lifetime dependence scope on an initializer with a ~Escapable parameter, specify '@lifetime(borrow ne)' or '@lifetime(copy ne)'}} +} + +struct NonescapableConsumingInitializers: ~Escapable { + var c: C // implicit get/set is OK + + init(c: consuming C) { self.c = c } // expected-error{{cannot borrow the lifetime of 'c', which has consuming ownership on an initializer}} + + init(c: consuming C, _: Int) { self.c = c } // expected-error{{an initializer with a ~Escapable result requires '@lifetime(...)'}} + + init(ne: consuming NE) { c = C() } // expected-error{{cannot infer the lifetime dependence scope on an initializer with a ~Escapable parameter, specify '@lifetime(borrow ne)' or '@lifetime(copy ne)'}} +} + +struct NonescapableBorrowingInitializers: ~Escapable { + var c: C // implicit stored property set is OK + + init(c: borrowing C) { self.c = c } // OK + + init(c: borrowing C, _: Int) { self.c = c } // expected-error{{an initializer with a ~Escapable result requires '@lifetime(...)'}} + + init(ne: borrowing NE) { c = C() } // expected-error{{cannot infer the lifetime dependence scope on an initializer with a ~Escapable parameter, specify '@lifetime(borrow ne)' or '@lifetime(copy ne)'}} +} + +struct NonescapableInoutInitializers: ~Escapable { + var c: C // implicit stored property set is OK + + init(c: inout C) { self.c = c } // OK + + init(c: inout C, _: Int) { self.c = c } // expected-error{{an initializer with a ~Escapable result requires '@lifetime(...)'}} + + init(ne: inout NE) { c = C() } // expected-error{{cannot infer the lifetime dependence scope on an initializer with a ~Escapable parameter, specify '@lifetime(borrow ne)' or '@lifetime(copy ne)'}} +} + +func noParam() -> NEImmortal { NEImmortal() } // expected-error{{a function with a ~Escapable result needs a parameter to depend on}} +// expected-note@-1{{'@lifetime(immortal)' can be used to indicate that values produced by this initializer have no lifetime dependencies}} + +@lifetime(immortal) +func noParamImmortal() -> NEImmortal { NEImmortal() } // OK + +func oneParam(c: C) -> NEImmortal { NEImmortal() } + +@lifetime(c) +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}} +func oneParamConsumeLifetime(c: consuming C) -> NEImmortal { NEImmortal() } + +func oneParamBorrow(c: borrowing C) -> NEImmortal { NEImmortal() } // OK + +@lifetime(c) +func oneParamBorrowLifetime(c: borrowing C) -> NEImmortal { NEImmortal() } // OK + +func oneParamInout(c: inout C) -> NEImmortal { NEImmortal() } // OK + +@lifetime(c) +func oneParamInoutLifetime(c: inout C) -> NEImmortal { NEImmortal() } // OK + +func twoParams(c: C, _: Int) -> NEImmortal { NEImmortal() } // expected-error{{a function with a ~Escapable result requires '@lifetime(...)'}} + +@lifetime(c) +func twoParamsLifetime(c: C, _: Int) -> NEImmortal { NEImmortal() } + +func twoParamsConsume(c: consuming C, _: Int) -> NEImmortal { NEImmortal() } // expected-error{{a function with a ~Escapable result requires '@lifetime(...)'}} + +func twoParamsBorrow(c: borrowing C, _: Int) -> NEImmortal { NEImmortal() } // expected-error{{a function with a ~Escapable result requires '@lifetime(...)'}} + +func twoParamsInout(c: inout C, _: Int) -> NEImmortal { NEImmortal() } // expected-error{{a function with a ~Escapable result requires '@lifetime(...)'}} + +func neParam(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)'}} + +@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(ne: NE) -> NE { ne } + +func neParamBorrow(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)'}} + +@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(ne: borrowing NE) -> NE { ne } + +func neParamConsume(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)'}} + +@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(ne: consuming NE) -> NE { ne } + +func neParamInout(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)'}} + +@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 neParamInoutLifetime(ne: inout NE) -> NE { ne } + +func neTwoParam(ne: NE, _:Int) -> NE { ne } // expected-error{{a function with a ~Escapable result requires '@lifetime(...)'}} + +// ============================================================================= +// Handle Accessors: +// +// 'get', '_read', and '_modify' are inferred as methods that return ~Escpable results dependent on 'self' +// +// 'set' is only inferred when implicit. This allows for the declaration of non-Escapable stored properties. Knowing +// that the implicit setter assigns a stored property is sufficient for the compiler to assume Inherit dependency on +// both 'self' and 'newValue'. A full assignment would not need the 'self' dependency. +// ============================================================================= + +struct Accessors { + let c: C + + var neComputed: NEImmortal { + get { // OK + NEImmortal() + } + + set { // OK (no dependency) + } + } + + var neYielded: NEImmortal { + _read { // OK + yield NEImmortal() + } + + _modify { // OK + var ne = NEImmortal() + yield &ne + } + } +} + +struct NonescapableSelfAccessors: ~Escapable { + var ne: NE + + @lifetime(immortal) + init() {} + + var neComputed: NE { + get { // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@lifetime(borrow self)' or '@lifetime(copy self)'}} + ne + } + + set { // expected-error{{a mutating method with a ~Escapable 'self' requires '@lifetime(self: ...)'}} + ne = newValue + } + } + + var neYielded: NE { + _read { // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@lifetime(borrow self)' or '@lifetime(copy self)'}} + yield ne + } + + @lifetime(borrow self) + _modify { + yield &ne + } + } + + var neComputedLifetime: NE { + @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)'}} + get { + 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)'}} + set { + ne = newValue + } + } + + var neYieldedLifetime: NE { + @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)'}} + _read { + yield 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)'}} + _modify { + yield &ne + } + } + + var neComputedCopy: NE { + @lifetime(copy self) + get { + ne + } + + @lifetime(copy self) + set { + ne = newValue + } + } + + var neYieldedCopy: NE { + @lifetime(copy self) + _read { + yield ne + } + + @lifetime(copy self) + _modify { + yield &ne + } + } + + var neComputedBorrow: NE { + @lifetime(borrow self) + get { + ne + } + + @lifetime(borrow self) + set { + ne = newValue + } + } + + var neYieldedBorrow: NE { + @lifetime(borrow self) + _read { + yield ne + } + + @lifetime(borrow self) + _modify { + yield &ne + } + } +} + +struct NoncopyableSelfAccessors: ~Copyable & ~Escapable { + var ne: NE + + var neComputed: NE { + get { // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@lifetime(borrow self)' or '@lifetime(copy self)'}} + ne + } + + set { // expected-error{{a mutating method with a ~Escapable 'self' requires '@lifetime(self: ...)'}} + ne = newValue + } + } + + var neYielded: NE { + _read { // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@lifetime(borrow self)' or '@lifetime(copy self)'}} + yield ne + } + + @lifetime(borrow self) + _modify { + yield &ne + } + } + + var neComputedLifetime: NE { + @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)'}} + get { + 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)'}} + set { + ne = newValue + } + } + + var neYieldedLifetime: NE { + @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)'}} + _read { + yield 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)'}} + _modify { + yield &ne + } + } + + var neComputedCopy: NE { + @lifetime(copy self) + get { + ne + } + + @lifetime(copy self) + set { + ne = newValue + } + } + + var neYieldedCopy: NE { + @lifetime(copy self) + _read { + yield ne + } + + @lifetime(copy self) + _modify { + yield &ne + } + } + + var neComputedBorrow: NE { + @lifetime(borrow self) + get { + ne + } + + @lifetime(borrow self) + set { + ne = newValue + } + } + + var neYieldedBorrow: NE { + @lifetime(borrow self) + _read { + yield ne + } + + @lifetime(borrow self) + _modify { + yield &ne + } + } +} + +// ============================================================================= +// Handle mutating methods with no return value +// ============================================================================= + +struct NonEscapableMutableSelf: ~Escapable { + // This is unambiguous: inout 'self' needs a dependency, and it can't be a borrow dependency because the original + // value is consumed. + /* @lifetime(self: copy self) */ + mutating func mutatingMethodNoParam() {} // OK + + @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 mutatingMethodNoParamLifetime() {} + + @lifetime(self: copy self) // OK + mutating func mutatingMethodNoParamCopy() {} + + @lifetime(self: borrow self) // expected-error{{invalid use of borrow dependence on the same inout parameter}} + mutating func mutatingMethodNoParamBorrow() {} + + mutating func mutatingMethodOneParam(_: NE) {} // expected-error{{a mutating method with a ~Escapable 'self' requires '@lifetime(self: ...)'}} + + @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 + mutating func mutatingMethodOneParamCopy(_: NE) {} + + @lifetime(borrow self) + mutating func mutatingMethodOneParamBorrow(_: NE) {} +} diff --git a/test/Sema/lifetime_depend_noattr.swift b/test/Sema/lifetime_depend_noattr.swift new file mode 100644 index 0000000000000..7dd87d26f7560 --- /dev/null +++ b/test/Sema/lifetime_depend_noattr.swift @@ -0,0 +1,34 @@ +// RUN: %target-swift-frontend %s -emit-sil \ +// RUN: -o /dev/null \ +// RUN: -verify \ +// RUN: -sil-verify-all \ +// RUN: -disable-availability-checking \ +// RUN: -module-name test \ +// RUN: -enable-experimental-feature LifetimeDependence + +// REQUIRES: swift_feature_LifetimeDependence + +// These tests complement lifetime_depend_nofeature.swift. If you add a test here, add one there. + +// Check that missing lifetime dependencies are diagnosed. Enabling LifetimeDependencies will issue more detailed +// diagnostics. + +// Allow empty initialization. +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 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 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 neInout(span: inout RawSpan) {} // OK - inferred +} diff --git a/test/Sema/lifetime_depend_nofeature.swift b/test/Sema/lifetime_depend_nofeature.swift new file mode 100644 index 0000000000000..8edb3ee4a8740 --- /dev/null +++ b/test/Sema/lifetime_depend_nofeature.swift @@ -0,0 +1,30 @@ +// RUN: %target-swift-frontend %s -emit-sil \ +// RUN: -o /dev/null \ +// RUN: -verify \ +// RUN: -sil-verify-all \ +// RUN: -disable-availability-checking \ +// RUN: -module-name test + +// These tests complement lifetime_depend_noattr.swift. If you add a test here, add one there. + +// Check that functions that require lifetime dependence are prohibited without the flag. + +// Don't allow empty initialization. +struct EmptyNonEscapable: ~Escapable {} // expected-error{{an implicit initializer with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence'}} + +// Don't allow non-Escapable return values. +func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{a function with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence'}} + +func neInout(span: inout RawSpan) {} // expected-error{{a function with a ~Escapable 'inout' parameter requires '-enable-experimental-feature LifetimeDependence'}} + +struct S { + func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{a method with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence'}} + + func neInout(span: inout RawSpan) {} // expected-error{{a method with a ~Escapable 'inout' parameter requires '-enable-experimental-feature LifetimeDependence'}} +} + +class C { + func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{a method with a ~Escapable result requires '-enable-experimental-feature LifetimeDependence'}} + + func neInout(span: inout RawSpan) {} // expected-error{{a method with a ~Escapable 'inout' parameter requires '-enable-experimental-feature LifetimeDependence'}} +} diff --git a/test/Sema/lifetime_dependence_functype.swift b/test/Sema/lifetime_dependence_functype.swift index b3d42da55e70b..3ff6e74946e24 100644 --- a/test/Sema/lifetime_dependence_functype.swift +++ b/test/Sema/lifetime_dependence_functype.swift @@ -13,14 +13,17 @@ struct NE: ~Escapable { init() {} } +@lifetime(copy ne) func transfer(_ ne: NE) -> NE { ne } +@lifetime(copy ne) func applyAnnotatedTransfer(ne: NE, @lifetime(0) transfer: (NE) -> NE) -> NE { // expected-error{{'@lifetime' attribute cannot be applied to this declaration}} transfer(ne) } +@lifetime(copy ne) func applyTransfer(ne: NE, transfer: (NE) -> NE) -> NE { transfer(ne) } @@ -36,6 +39,7 @@ func borrow(_ nc: borrowing NC) -> NE { nc.ne } +@lifetime(borrow nc) func applyBorrow(nc: borrowing NC, borrow: (borrowing NC) -> NE) -> NE { borrow(nc) } diff --git a/test/Serialization/Inputs/def_explicit_lifetime_dependence.swift b/test/Serialization/Inputs/def_explicit_lifetime_dependence.swift index e62b67c403d84..05094ef6e9689 100644 --- a/test/Serialization/Inputs/def_explicit_lifetime_dependence.swift +++ b/test/Serialization/Inputs/def_explicit_lifetime_dependence.swift @@ -17,11 +17,11 @@ public struct BufferView : ~Escapable { public init(_ ptr: UnsafeRawBufferPointer, _ a: borrowing Array) { self.ptr = ptr } - @lifetime(a) + @lifetime(copy a) public init(_ ptr: UnsafeRawBufferPointer, _ a: consuming AnotherView) { self.ptr = ptr } - @lifetime(a, borrow b) + @lifetime(copy a, borrow b) public init(_ ptr: UnsafeRawBufferPointer, _ a: consuming AnotherView, _ b: borrowing Array) { self.ptr = ptr } @@ -48,12 +48,12 @@ public func borrowAndCreate(_ view: borrowing BufferView) -> BufferView { return BufferView(view.ptr) } -@lifetime(view) +@lifetime(copy view) public func consumeAndCreate(_ view: consuming BufferView) -> BufferView { return BufferView(view.ptr) } -@lifetime(borrow this, that) +@lifetime(borrow this, copy that) public func deriveThisOrThat(_ this: borrowing BufferView, _ that: borrowing BufferView) -> BufferView { if (Int.random(in: 1..<100) == 0) { return BufferView(this.ptr) @@ -63,13 +63,16 @@ public func deriveThisOrThat(_ this: borrowing BufferView, _ that: borrowing Buf public struct Wrapper : ~Escapable { var _view: BufferView + @lifetime(copy view) public init(_ view: consuming BufferView) { self._view = view } public var view: BufferView { + @lifetime(copy self) _read { yield _view } + @lifetime(borrow 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 a726bf10ea393..8a327ce4fdbdf 100644 --- a/test/Serialization/Inputs/def_implicit_lifetime_dependence.swift +++ b/test/Serialization/Inputs/def_implicit_lifetime_dependence.swift @@ -7,6 +7,7 @@ public struct BufferView : ~Escapable { self.c = c } @inlinable + @lifetime(copy otherBV) public init(_ otherBV: borrowing BufferView) { self.ptr = otherBV.ptr self.c = otherBV.c @@ -24,14 +25,17 @@ public struct MutableBufferView : ~Escapable, ~Copyable { } @inlinable +@lifetime(copy x) public func derive(_ x: borrowing BufferView) -> BufferView { return BufferView(x.ptr, x.c) } +@lifetime(copy view) public func borrowAndCreate(_ view: borrowing BufferView) -> BufferView { return BufferView(view.ptr, view.c ) } +@lifetime(copy view) public func consumeAndCreate(_ view: consuming BufferView) -> BufferView { return BufferView(view.ptr, view.c) } @@ -54,13 +58,16 @@ public struct Container : ~Copyable { public struct Wrapper : ~Escapable { var _view: BufferView public var view: BufferView { + @lifetime(copy self) _read { yield _view } + @lifetime(borrow self) _modify { yield &_view } } + @lifetime(copy view) public init(_ view: consuming BufferView) { self._view = view }