diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 68a173916c950..19c9a8ced9321 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -2574,6 +2574,13 @@ BridgedDifferentiableTypeAttr BridgedDifferentiableTypeAttr_createParsed( swift::SourceLoc nameLoc, swift::SourceRange parensRange, BridgedDifferentiabilityKind cKind, swift::SourceLoc kindLoc); +SWIFT_NAME("BridgedLifetimeTypeAttr.createParsed(_:atLoc:nameLoc:" + "parensRange:entry:)") +BridgedLifetimeTypeAttr BridgedLifetimeTypeAttr_createParsed( + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceLoc nameLoc, swift::SourceRange parensRange, + BridgedLifetimeEntry entry); + SWIFT_NAME("BridgedIsolatedTypeAttr.createParsed(_:atLoc:nameLoc:parensRange:" "isolationKind:isolationKindLoc:)") BridgedIsolatedTypeAttr BridgedIsolatedTypeAttr_createParsed( diff --git a/include/swift/AST/ASTPrinter.h b/include/swift/AST/ASTPrinter.h index ea24c38f3cab7..2db30a672375b 100644 --- a/include/swift/AST/ASTPrinter.h +++ b/include/swift/AST/ASTPrinter.h @@ -368,7 +368,9 @@ class ASTPrinter { return printedClangDecl.insert(d).second; } - void printLifetimeDependence( + /// Print lifetimeDependence as a SIL lifetime attribute, attached to a + /// parameter or result of a function. + void printSILLifetimeDependence( std::optional lifetimeDependence) { if (!lifetimeDependence.has_value()) { return; @@ -380,10 +382,15 @@ class ASTPrinter { ArrayRef lifetimeDependencies, unsigned index) { if (auto lifetimeDependence = getLifetimeDependenceFor(lifetimeDependencies, index)) { - printLifetimeDependence(*lifetimeDependence); + printSILLifetimeDependence(*lifetimeDependence); } } + /// Print lifetimeDependence as a Swift lifetime attribute. + void + printSwiftLifetimeDependence(LifetimeDependenceInfo const &lifetimeDependence, + ArrayRef params); + private: virtual void anchor(); }; diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 5bf834c7b0a0f..c6e6d75719acc 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -4274,6 +4274,17 @@ class alignas(1 << AttrAlignInBits) TypeAttribute SWIFT_DEBUG_DUMPER(dump()); void print(ASTPrinter &Printer, const PrintOptions &Options) const; + + /// Returns true if multiple instances of an attribute kind + /// can appear on a type. + static constexpr bool allowMultipleAttributes(TypeAttrKind TK) { + switch (TK) { + case swift::TypeAttrKind::Lifetime: + return true; + default: + return false; + } + } }; class AtTypeAttrBase : public TypeAttribute { @@ -4383,6 +4394,19 @@ class DifferentiableTypeAttr void printImpl(ASTPrinter &printer, const PrintOptions &options) const; }; +class LifetimeTypeAttr : public SimpleTypeAttrWithArgs { + LifetimeEntry *entry; + +public: + LifetimeTypeAttr(SourceLoc atLoc, SourceLoc kwLoc, SourceRange parens, + LifetimeEntry *entry) + : SimpleTypeAttr(atLoc, kwLoc, parens), entry(entry) {} + + LifetimeEntry *getLifetimeEntry() const { return entry; } + + void printImpl(ASTPrinter &printer, const PrintOptions &options) const; +}; + class OpaqueReturnTypeOfTypeAttr : public SimpleTypeAttrWithArgs { Located MangledName; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e0c9777435fa9..9ebb721e99d74 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8511,6 +8511,8 @@ ERROR(lifetime_target_requires_nonescapable, none, "invalid lifetime dependence on an Escapable %0", (StringRef)) NOTE(lifetime_escapable_source_requires_escapable_note, none, "use '@_lifetime(%0%1)' instead", (StringRef, StringRef)) +ERROR(lifetime_dependence_unknown_type, none, + "lifetime dependence checking failed due to unknown %0 type", (StringRef)) //------------------------------------------------------------------------------ // MARK: Lifetime Dependence Requirements diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index 7b501920d6cc0..e46775f205fa8 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -31,8 +31,10 @@ namespace swift { class AbstractFunctionDecl; +class AnyFunctionType; class FunctionTypeRepr; class LifetimeDependentTypeRepr; +class LifetimeTypeAttr; class SILParameterInfo; class SILResultInfo; @@ -190,23 +192,28 @@ class LifetimeEntry final class LifetimeDependenceInfo { IndexSubset *inheritLifetimeParamIndices; IndexSubset *scopeLifetimeParamIndices; - llvm::PointerIntPair - addressableParamIndicesAndImmortal; + // The outer bool is the "isFromAnnotation" bit. The inner one is the + // "isImmortal" bit. + llvm::PointerIntPair, 1, bool> + addressableParamIndicesAndImmortalAndFromAnnotation; IndexSubset *conditionallyAddressableParamIndices; unsigned targetIndex; public: + /// Fully-initialized dependence info. LifetimeDependenceInfo(IndexSubset *inheritLifetimeParamIndices, IndexSubset *scopeLifetimeParamIndices, unsigned targetIndex, bool isImmortal, - // set during SIL type lowering - IndexSubset *addressableParamIndices = nullptr, - IndexSubset *conditionallyAddressableParamIndices = nullptr) + bool isFromAnnotation, + IndexSubset *addressableParamIndices, + IndexSubset *conditionallyAddressableParamIndices) : inheritLifetimeParamIndices(inheritLifetimeParamIndices), scopeLifetimeParamIndices(scopeLifetimeParamIndices), - addressableParamIndicesAndImmortal(addressableParamIndices, isImmortal), - conditionallyAddressableParamIndices(conditionallyAddressableParamIndices), + addressableParamIndicesAndImmortalAndFromAnnotation( + {addressableParamIndices, isImmortal}, isFromAnnotation), + conditionallyAddressableParamIndices( + conditionallyAddressableParamIndices), targetIndex(targetIndex) { ASSERT(this->isImmortal() || inheritLifetimeParamIndices || scopeLifetimeParamIndices); @@ -238,6 +245,18 @@ class LifetimeDependenceInfo { } } + /// Partially-initialized dependence info, with addressable & conditionally + /// addressable parameter indices unset for now. + LifetimeDependenceInfo(IndexSubset *inheritLifetimeParamIndices, + IndexSubset *scopeLifetimeParamIndices, + unsigned targetIndex, bool isImmortal, + bool isFromAnnotation) + : LifetimeDependenceInfo(inheritLifetimeParamIndices, + scopeLifetimeParamIndices, targetIndex, + isImmortal, isFromAnnotation, + // set during SIL type lowering + nullptr, nullptr) {} + operator bool() const { return !empty(); } bool empty() const { @@ -245,7 +264,18 @@ class LifetimeDependenceInfo { scopeLifetimeParamIndices == nullptr; } - bool isImmortal() const { return addressableParamIndicesAndImmortal.getInt(); } + bool isImmortal() const { + return addressableParamIndicesAndImmortalAndFromAnnotation.getPointer() + .getInt(); + } + + /// Whether this lifetime dependence corresponds to a @lifetime annotation in + /// the source program (Swift or SIL). Such dependencies are likely to differ + /// from the default (inferred) ones, so they must be included when printing a + /// Swift function's type. + bool isFromAnnotation() const { + return addressableParamIndicesAndImmortalAndFromAnnotation.getInt(); + } unsigned getTargetIndex() const { return targetIndex; } @@ -256,7 +286,8 @@ class LifetimeDependenceInfo { return scopeLifetimeParamIndices != nullptr; } bool hasAddressableParamIndices() const { - return addressableParamIndicesAndImmortal.getPointer() != nullptr; + return addressableParamIndicesAndImmortalAndFromAnnotation.getPointer() + .getPointer() != nullptr; } unsigned getParamIndicesLength() const { @@ -282,7 +313,8 @@ class LifetimeDependenceInfo { /// not only on the value, but the memory location of a particular instance /// of the value. IndexSubset *getAddressableIndices() const { - return addressableParamIndicesAndImmortal.getPointer(); + return addressableParamIndicesAndImmortalAndFromAnnotation.getPointer() + .getPointer(); } /// Return the set of parameters which may have addressable dependencies /// depending on the type of the parameter. @@ -307,6 +339,12 @@ class LifetimeDependenceInfo { && scopeLifetimeParamIndices->contains(index); } + /// Get a string representation of this LifetimeDependenceInfo suitable for + /// printing in SIL. The target is not included, since this is determined by + /// the position of the attribute in SIL. + /// + /// For printing function type lifetimes in Swift, see + /// ASTPrinter::printSwiftLifetimeDependence. std::string getString() const; void Profile(llvm::FoldingSetNodeID &ID) const; void getConcatenatedData(SmallVectorImpl &concatenatedData) const; @@ -321,6 +359,12 @@ class LifetimeDependenceInfo { getFromSIL(FunctionTypeRepr *funcRepr, ArrayRef params, ArrayRef results, DeclContext *dc); + /// Builds LifetimeDependenceInfo from a function type. + static std::optional> + getFromAST(FunctionTypeRepr *funcRepr, AnyFunctionType *funcType, + ArrayRef lifetimeAttributes, DeclContext *dc, + GenericEnvironment *env); + bool operator==(const LifetimeDependenceInfo &other) const { return this->isImmortal() == other.isImmortal() && this->getTargetIndex() == other.getTargetIndex() && diff --git a/include/swift/AST/Ownership.h b/include/swift/AST/Ownership.h index 9ec8ab6b4ba6f..1f458cf03cbee 100644 --- a/include/swift/AST/Ownership.h +++ b/include/swift/AST/Ownership.h @@ -153,7 +153,7 @@ ValueOwnership asValueOwnership(ParameterOwnership o); static inline llvm::StringRef getOwnershipSpelling(ValueOwnership ownership) { switch (ownership) { case ValueOwnership::Default: - return ""; + return "default"; case ValueOwnership::InOut: return "inout"; case ValueOwnership::Shared: diff --git a/include/swift/AST/TypeAttr.def b/include/swift/AST/TypeAttr.def index 9aa07d1ff0160..d34858e35a16b 100644 --- a/include/swift/AST/TypeAttr.def +++ b/include/swift/AST/TypeAttr.def @@ -62,6 +62,7 @@ SIMPLE_TYPE_ATTR(reparented, Reparented) SIMPLE_TYPE_ATTR(unchecked, Unchecked) SIMPLE_TYPE_ATTR(preconcurrency, Preconcurrency) SIMPLE_TYPE_ATTR(unsafe, Unsafe) +TYPE_ATTR(_lifetime, Lifetime) SIMPLE_TYPE_ATTR(_local, Local) SIMPLE_TYPE_ATTR(_noMetadata, NoMetadata) TYPE_ATTR(_opaqueReturnTypeOf, OpaqueReturnTypeOf) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index f444897fb1656..370c561978bd2 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -3790,6 +3790,10 @@ class AnyFunctionType : public TypeBase { return Bits.AnyFunctionType.HasLifetimeDependencies; } + /// Type has lifetime dependencies derived from explicit @_lifetime + /// attributes. + bool hasExplicitLifetimeDependencies() const; + ClangTypeInfo getClangTypeInfo() const; ClangTypeInfo getCanonicalClangTypeInfo() const; diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 912e9c0e8edb5..cee8e41c33be6 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -278,6 +278,7 @@ LANGUAGE_FEATURE(InoutLifetimeDependence, 0, "Support @_lifetime(&)") SUPPRESSIBLE_LANGUAGE_FEATURE(NonexhaustiveAttribute, 487, "Nonexhaustive Enums") LANGUAGE_FEATURE(ModuleSelector, 491, "Module selectors (`Module::name` syntax)") LANGUAGE_FEATURE(BuiltinConcurrencyStackNesting, 0, "Concurrency Stack Nesting Builtins") +LANGUAGE_FEATURE(ClosureLifetimes, 0, "Support @_lifetime on function types") // TEMPORARY: Never use this, because it is only meant to allow us to model // suppression of @c in Swift interfaces as a temporary measure. diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 391fae79ff6a2..f892e7e742dae 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -198,6 +198,7 @@ struct BridgedLifetimeDependenceInfo { swift::IndexSubset *_Nullable conditionallyAddressableParamIndices; SwiftUInt targetIndex; bool immortal; + bool fromAnnotation; BRIDGED_INLINE BridgedLifetimeDependenceInfo(swift::LifetimeDependenceInfo info); diff --git a/include/swift/SIL/SILBridgingImpl.h b/include/swift/SIL/SILBridgingImpl.h index e0717fffbe670..d547a28299198 100644 --- a/include/swift/SIL/SILBridgingImpl.h +++ b/include/swift/SIL/SILBridgingImpl.h @@ -171,13 +171,15 @@ BridgedParameterInfo BridgedParameterInfoArray::at(SwiftInt parameterIndex) cons // BridgedLifetimeDependenceInfo //===----------------------------------------------------------------------===// -BridgedLifetimeDependenceInfo::BridgedLifetimeDependenceInfo(swift::LifetimeDependenceInfo info) +BridgedLifetimeDependenceInfo::BridgedLifetimeDependenceInfo( + swift::LifetimeDependenceInfo info) : inheritLifetimeParamIndices(info.getInheritIndices()), scopeLifetimeParamIndices(info.getScopeIndices()), addressableParamIndices(info.getAddressableIndices()), conditionallyAddressableParamIndices( - info.getConditionallyAddressableIndices()), - targetIndex(info.getTargetIndex()), immortal(info.isImmortal()) {} + info.getConditionallyAddressableIndices()), + targetIndex(info.getTargetIndex()), immortal(info.isImmortal()), + fromAnnotation(info.isFromAnnotation()) {} SwiftInt BridgedLifetimeDependenceInfoArray::count() const { return lifetimeDependenceInfoArray.unbridged().size(); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index d4f53b82d72f9..197a247a529fc 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -504,6 +504,108 @@ bool TypeTransformContext::isPrintingSynthesizedExtension() const { return !Decl.isNull(); } +/// Get a string representation of the parameter at params[index]. This will be +/// the label or internal label of a normal parameter, or the string form of +/// index. We cannot currently print lifetimes with indices in swiftinterface +/// files because SwiftSyntax cannot parse lifetime entries that use them. +static std::string +getLifetimeDependenceIdentifier(unsigned index, + ArrayRef params) { + // TODO: Print "self" for the self parameter. Requires a way to determine if a + // type is the interface type of an instance method. + if (index < params.size() && params[index].hasInternalLabel()) { + return params[index].getInternalLabel().get(); + } + + return std::to_string(index); +} + +/// Get a string representation for the list of sources of the given +/// LifetimeDependenceInfo, if they can be printed. +/// +/// See getLifetimeDependenceIdentifier. +static std::string getLifetimeDependenceInfoSourceListString( + LifetimeDependenceInfo const &info, + ArrayRef params) { + + std::string lifetimeDependenceString = ""; + auto addressable = info.getAddressableIndices(); + auto condAddressable = info.getConditionallyAddressableIndices(); + + auto getSourceString = [&](IndexSubset *bitvector, + StringRef kind) -> std::string { + std::string result; + bool isFirstSetBit = true; + for (unsigned i = 0; i < bitvector->getCapacity(); i++) { + if (bitvector->contains(i)) { + if (!isFirstSetBit) { + result += ", "; + } + result += kind; + if (addressable && addressable->contains(i)) { + result += "address "; + } else if (condAddressable && condAddressable->contains(i)) { + result += "address_for_deps "; + } + // Print source labels for explicit lifetime annotations. Otherwise, + // print the index. Indices should ideally never be used in Swift + // lifetime annotations, especially in module interfaces. + result += getLifetimeDependenceIdentifier(i, params); + isFirstSetBit = false; + } + } + return result; + }; + auto inheritLifetimeParamIndices = info.getInheritIndices(); + if (inheritLifetimeParamIndices) { + assert(!inheritLifetimeParamIndices->isEmpty()); + lifetimeDependenceString += + getSourceString(inheritLifetimeParamIndices, "copy "); + } + if (auto scopeLifetimeParamIndices = info.getScopeIndices()) { + assert(!scopeLifetimeParamIndices->isEmpty()); + if (inheritLifetimeParamIndices) { + lifetimeDependenceString += ", "; + } + lifetimeDependenceString += + getSourceString(scopeLifetimeParamIndices, "borrow "); + } + if (info.isImmortal()) { + lifetimeDependenceString += "immortal"; + } + return lifetimeDependenceString; +} + +/// Get a string representation for this dependence info as a Swift lifetime +/// attribute (for a type or decl). The target and sources are referred to by +/// their labels if possible (see getLifetimeDependenceIdentifier). +static std::string +getLifetimeDependenceInfoSwiftString(LifetimeDependenceInfo const &info, + ArrayRef params) { + std::string lifetimeDependenceString = "@_lifetime("; + + // Only print the target if it is a parameter or self. + const auto resultIndex = params.size(); + const auto targetIndex = info.getTargetIndex(); + if (targetIndex != resultIndex) { + lifetimeDependenceString += + getLifetimeDependenceIdentifier(targetIndex, params); + lifetimeDependenceString += ": "; + } + + lifetimeDependenceString += + getLifetimeDependenceInfoSourceListString(info, params); + lifetimeDependenceString += ") "; + return lifetimeDependenceString; +} + +void ASTPrinter::printSwiftLifetimeDependence( + LifetimeDependenceInfo const &lifetimeDependence, + ArrayRef params) { + + *this << getLifetimeDependenceInfoSwiftString(lifetimeDependence, params); +} + void ASTPrinter::anchor() {} void ASTPrinter::printIndent() { @@ -6755,6 +6857,17 @@ class TypePrinter : public TypeVisitorhasLifetimeDependencies()) { + ArrayRef params = fnType->getParams(); + + for (const auto &lifetimeDependence : info.getLifetimeDependencies()) { + if (lifetimeDependence.isFromAnnotation()) { + Printer.printSwiftLifetimeDependence(lifetimeDependence, params); + } + } + } + SmallString<64> buf; switch (Options.PrintFunctionRepresentationAttrs) { case PrintOptions::FunctionRepresentationMode::None: @@ -6940,8 +7053,16 @@ class TypePrinter : public TypeVisitor Params, bool printLabels, + ArrayRef Params, bool printExternalLabels, + bool printAllLabels, ArrayRef lifetimeDependencies) { Printer << "("; @@ -6955,16 +7076,23 @@ class TypePrinter : public TypeVisitorhasExplicitLifetimeDependencies(); + } + void visitFunctionType(FunctionType *T, NonRecursivePrintOptions nrOptions) { Printer.callPrintStructurePre(PrintStructureKind::FunctionType); @@ -7002,7 +7138,10 @@ class TypePrinter : public TypeVisitorgetParams(), /*printLabels*/ false, + visitAnyFunctionTypeParams(T->getParams(), + /*printExternalLabels*/ false, + /*printAllLabels*/ + shouldPrintLabelsForLifetimeAttributes(T), T->getLifetimeDependencies()); if (T->hasExtInfo()) { @@ -7028,7 +7167,10 @@ class TypePrinter : public TypeVisitorgetLifetimeDependenceForResult()); + if (Options.PrintInSILBody) { + Printer.printLifetimeDependenceAt(T->getLifetimeDependencies(), + T->getParams().size()); + } Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); T->getResult().print(Printer, Options); @@ -7063,7 +7205,9 @@ class TypePrinter : public TypeVisitorgetParams(), /*printLabels*/ true, + visitAnyFunctionTypeParams(T->getParams(), + /*printExternalLabels*/ true, /*printAllLabels*/ + shouldPrintLabelsForLifetimeAttributes(T), T->getLifetimeDependencies()); if (T->hasExtInfo()) { @@ -7085,8 +7229,10 @@ class TypePrinter : public TypeVisitor "; - Printer.printLifetimeDependenceAt(T->getLifetimeDependencies(), - T->getParams().size()); + if (Options.PrintInSILBody) { + Printer.printLifetimeDependenceAt(T->getLifetimeDependencies(), + T->getParams().size()); + } Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType); T->getResult().print(Printer, Options); @@ -7191,7 +7337,7 @@ class TypePrinter : public TypeVisitor "; - Printer.printLifetimeDependence(T->getLifetimeDependenceForResult()); + Printer.printSILLifetimeDependence(T->getLifetimeDependenceForResult()); bool parenthesizeResults = mustParenthesizeResults(T); if (parenthesizeResults) @@ -7835,12 +7981,13 @@ void AnyFunctionType::printParams(ArrayRef Params, printParams(Params, Printer, PO); } void AnyFunctionType::printParams(ArrayRef Params, - ASTPrinter &Printer, - const PrintOptions &PO) { - // TODO: Handle lifetime dependence printing here + ASTPrinter &Printer, const PrintOptions &PO) { + // Function type lifetimes are printed as part of the ExtInfo, not the + // parameter list, so we do not need to print them here. TypePrinter(Printer, PO) .visitAnyFunctionTypeParams(Params, - /*printLabels*/ true, {}); + /*printExternalLabels*/ true, + /*printAllLabels*/ false, {}); } std::string @@ -8029,7 +8176,7 @@ void SILParameterInfo::print( } if (lifetimeDependence) { - Printer.printLifetimeDependence(*lifetimeDependence); + Printer.printSILLifetimeDependence(*lifetimeDependence); } if (options.contains(SILParameterInfo::ImplicitLeading)) { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 6eb1d9d7208cc..8b2bb176d7da9 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -238,6 +238,14 @@ void DifferentiableTypeAttr::printImpl(ASTPrinter &printer, printer.printStructurePost(PrintStructureKind::BuiltinAttribute); } +void LifetimeTypeAttr::printImpl(ASTPrinter &printer, + const PrintOptions &options) const { + printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); + printer.printAttrName("@_lifetime"); + printer << entry->getString(); + printer.printStructurePost(PrintStructureKind::BuiltinAttribute); +} + void ConventionTypeAttr::printImpl(ASTPrinter &printer, const PrintOptions &options) const { printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); diff --git a/lib/AST/Bridging/TypeAttributeBridging.cpp b/lib/AST/Bridging/TypeAttributeBridging.cpp index 975dacf3ce928..34acabf269579 100644 --- a/lib/AST/Bridging/TypeAttributeBridging.cpp +++ b/lib/AST/Bridging/TypeAttributeBridging.cpp @@ -69,6 +69,14 @@ BridgedDifferentiableTypeAttr BridgedDifferentiableTypeAttr_createParsed( atLoc, nameLoc, parensRange, {unbridged(cKind), kindLoc}); } +BridgedLifetimeTypeAttr BridgedLifetimeTypeAttr_createParsed( + BridgedASTContext cContext, swift::SourceLoc atLoc, + swift::SourceLoc nameLoc, swift::SourceRange parensRange, + BridgedLifetimeEntry entry) { + return new (cContext.unbridged()) + LifetimeTypeAttr(atLoc, nameLoc, parensRange, entry.unbridged()); +} + BridgedIsolatedTypeAttr BridgedIsolatedTypeAttr_createParsed( BridgedASTContext cContext, SourceLoc atLoc, SourceLoc nameLoc, SourceRange parensRange, BridgedIsolatedTypeAttrIsolationKind cIsolation, diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 79574ae1000f1..efaaaa724a765 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -233,6 +233,56 @@ static bool usesFeatureLifetimes(Decl *decl) { return false; } +static bool hasLifetimeDependencies(Type type) { + if (auto *aft = type->getAs()) { + return aft->hasExplicitLifetimeDependencies(); + } + return false; +} + +/// Search for any types within decl with lifetime dependencies. Ignore +/// lifetimes on AbstractFunctionDecl decls, since those are supported by the +/// Lifetimes feature, not ClosureLifetimes. +static bool findClosureLifetimes(Decl *decl) { + // Search for any Decl that uses a type with lifetime dependencies, possibly + // restricting this to explicit dependencies. + class ClosureLifetimesWalker : public ASTWalker { + + public: + bool useFound = false; + + PreWalkAction walkToDeclPre(Decl *D) override { + if (auto *afd = dyn_cast(D)) { + // Check the parameters and result, but not the AFD itself, since + // lifetimes on AFDs are supported by the Lifetimes feature. + Type resultType = + afd->getInterfaceType()->getAs()->getResult(); + useFound = resultType.findIf(hasLifetimeDependencies); + return Action::StopIf(useFound); + } + + // Check for lifetime dependence info on param and type decls. + if (isa(D) || isa(D)) { + useFound = usesTypeMatching(D, hasLifetimeDependencies); + return Action::StopIf(useFound); + } + + // Any other Decl kinds are irrelevant. + return Action::SkipChildren(); + } + }; + + ClosureLifetimesWalker walker; + decl->walk(walker); + return walker.useFound; +} + +static bool usesFeatureClosureLifetimes(Decl *decl) { + // This will find function types with lifetimes & closures with lifetimes, + // since it walks the AST rooted at decl, checking types. + return findClosureLifetimes(decl); +} + static bool usesFeatureInoutLifetimeDependence(Decl *decl) { auto hasInoutLifetimeDependence = [](Decl *decl) { for (auto attr : decl->getAttrs().getAttributes()) { diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 2d653fa9d4590..f4bfc8369787a 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -17,6 +17,7 @@ #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/SourceFile.h" @@ -32,6 +33,20 @@ using namespace swift; +/// Determine whether Type t is "unknown", meaning we cannot safely determine +/// whether it is Escapable by calling TypeBase::isEscapable. +static bool isTypeUnknown(Type t) { + // These types would hit an assertion in + // TypeBase::computeInvertibleConformances. + if (t->hasUnboundGenericType() || t->hasTypeParameter()) + return true; + // This type would hit an assertion in checkRequirements. + if (t->hasTypeVariable()) + return true; + + return false; +} + std::string LifetimeDescriptor::getString() const { switch (kind) { case DescriptorKind::Named: { @@ -194,7 +209,8 @@ std::string LifetimeDependenceInfo::getString() const { } void LifetimeDependenceInfo::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddBoolean(addressableParamIndicesAndImmortal.getInt()); + ID.AddBoolean(isImmortal()); + ID.AddBoolean(isFromAnnotation()); ID.AddInteger(targetIndex); if (inheritLifetimeParamIndices) { ID.AddInteger((uint8_t)LifetimeDependenceKind::Inherit); @@ -204,11 +220,11 @@ void LifetimeDependenceInfo::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger((uint8_t)LifetimeDependenceKind::Scope); scopeLifetimeParamIndices->Profile(ID); } - if (addressableParamIndicesAndImmortal.getPointer()) { + if (hasAddressableParamIndices()) { ID.AddBoolean(true); - addressableParamIndicesAndImmortal.getPointer()->Profile(ID); + getAddressableIndices()->Profile(ID); } else { - ID.AddBoolean(false); + ID.AddBoolean(false); } } @@ -235,7 +251,7 @@ void LifetimeDependenceInfo::getConcatenatedData( pushData(scopeLifetimeParamIndices); } if (hasAddressableParamIndices()) { - pushData(addressableParamIndicesAndImmortal.getPointer()); + pushData(getAddressableIndices()); } } @@ -337,8 +353,8 @@ struct LifetimeDependenceBuilder { } TargetDeps *createAnnotatedTargetDeps(unsigned targetIndex) { - auto iterAndInserted = depsArray.try_emplace(targetIndex, true, - sourceIndexCap); + auto iterAndInserted = + depsArray.try_emplace(targetIndex, true, sourceIndexCap); if (!iterAndInserted.second) return nullptr; @@ -394,10 +410,11 @@ struct LifetimeDependenceBuilder { ASSERT(!deps.isImmortal && "cannot combine immortal lifetime with parameter dependency"); } - lifetimeDependencies.push_back(LifetimeDependenceInfo{ + lifetimeDependencies.push_back(LifetimeDependenceInfo( /*inheritLifetimeParamIndices*/ inheritIndices, /*scopeLifetimeParamIndices*/ scopeIndices, targetIndex, - /*isImmortal*/ deps.isImmortal}); + /*isImmortal*/ deps.isImmortal, + /*isFromAnnotation*/ deps.hasAnnotation)); } if (lifetimeDependencies.empty()) { return std::nullopt; @@ -452,7 +469,7 @@ class LifetimeDependenceChecker { // A parameter corresponding to the implicit self declaration of // the function, if it has one. Otherwise, std::nullopt. - std::optional implicitSelfParam; + std::optional implicitSelfParamInfo; LifetimeDependenceBuilder depBuilder; @@ -506,8 +523,8 @@ class LifetimeDependenceChecker { } const ParamInfo &getParamForIndex(unsigned paramIndex) { - if (implicitSelfParam && paramIndex == implicitSelfParam->index) - return *implicitSelfParam; + if (implicitSelfParamInfo && paramIndex == implicitSelfParamInfo->index) + return *implicitSelfParamInfo; assert(paramIndex < parameterInfos.size() && "unexpected result index"); return parameterInfos[paramIndex]; @@ -517,8 +534,9 @@ class LifetimeDependenceChecker { if (paramOrResultIndex == resultIndex) return resultInterfaceType; - if (implicitSelfParam && paramOrResultIndex == implicitSelfParam->index) - return implicitSelfParam->getInterfaceType(); + if (implicitSelfParamInfo && + paramOrResultIndex == implicitSelfParamInfo->index) + return implicitSelfParamInfo->getInterfaceType(); return parameterInfos[paramOrResultIndex].getInterfaceType(); } @@ -527,8 +545,9 @@ class LifetimeDependenceChecker { if (paramOrResultIndex == resultIndex) return resultTy; - if (implicitSelfParam && paramOrResultIndex == implicitSelfParam->index) - return implicitSelfParam->typeInContext; + if (implicitSelfParamInfo && + paramOrResultIndex == implicitSelfParamInfo->index) + return implicitSelfParamInfo->typeInContext; return parameterInfos[paramOrResultIndex].typeInContext; } @@ -557,31 +576,107 @@ class LifetimeDependenceChecker { LifetimeDependenceChecker(AbstractFunctionDecl *afd) : lifetimeEntries(collectLifetimeEntries(afd->getAttrs())), parameterInfos(collectParameterInfo(afd->getParameters(), afd)), - afd(afd), - ctx(afd->getDeclContext()->getASTContext()), + afd(afd), ctx(afd->getDeclContext()->getASTContext()), sourceFile(afd->getParentSourceFile()), - escapableDecl( - ctx.getProtocol( + escapableDecl(ctx.getProtocol( swift::getKnownProtocolKind(InvertibleProtocolKind::Escapable))), genericEnv(afd->getGenericEnvironment()), resultIndex(getResultIndex(afd)), resultInterfaceType(getResultOrYieldInterface(afd)), resultTy(afd->mapTypeIntoEnvironment(resultInterfaceType)), returnLoc(getReturnLoc(afd)), - implicitSelfParam(getSelfParamInfo(afd)), + implicitSelfParamInfo(getSelfParamInfo(afd)), depBuilder(/*sourceIndexCap*/ resultIndex), - isImplicit(afd->isImplicit()), - isInit(isa(afd)), + isImplicit(afd->isImplicit()), isInit(isa(afd)), hasUnsafeNonEscapableResult( - afd->getAttrs().hasAttribute()) {} + afd->getAttrs().hasAttribute()) {} + + LifetimeDependenceChecker(FunctionTypeRepr *funcRepr, + AnyFunctionType *funcType, + ArrayRef lifetimeAttrs, + DeclContext *dc, GenericEnvironment *env) + : afd(nullptr), ctx(funcType->getASTContext()), + sourceFile(dc->getParentSourceFile()), + resultIndex(funcType->getParams().size()), + returnLoc(funcRepr->getResultTypeRepr()->getLoc()), + implicitSelfParamInfo(std::nullopt), depBuilder(resultIndex), + isImplicit(false), isInit(false), hasUnsafeNonEscapableResult(false) { + for (auto functionLifetimeEntry : lifetimeAttrs) + lifetimeEntries.push_back(functionLifetimeEntry->getLifetimeEntry()); + + // We only ever use the second names of function type parameters for + // lifetimes. + auto const params = funcType->getParams(); + auto const argReprs = funcRepr->getArgsTypeRepr()->getElements(); + + resultTy = + GenericEnvironment::mapTypeIntoEnvironment(env, funcType->getResult()); + + assert(params.size() == argReprs.size()); + for (unsigned paramIndex = 0; paramIndex < params.size(); ++paramIndex) { + auto const ¶meter = params[paramIndex]; + auto const &arg = argReprs[paramIndex]; + parameterInfos.push_back( + {parameter, paramIndex, + // If an argument has no second name, use the location of its type. + arg.SecondNameLoc.isValid() ? arg.SecondNameLoc : arg.Type->getLoc(), + GenericEnvironment::mapTypeIntoEnvironment( + env, parameter.getPlainType())}); + } + } std::optional> currentDependencies() const { return depBuilder.initializeDependenceInfoArray(ctx); } + /// Perform lifetime dependence checks for a function type. + std::optional> checkFuncType() { + // Check if the function type contains any types for which we cannot + // determine Escapability. We cannot perform lifetime inference for such + // types, or check the correctness of lifetime annotations on them, so bail + // out. Emit diagnostics if there are any lifetime attributes. + // + // We could make this more granular, only bailing out if a lifetime source + // or target contains an unbound generic, but these cases seem too niche to + // be worth the effort. + // + // Even if there were no explicit lifetime entries, we still need to + // diagnose failed inference if a parameter or the result was ~Escapable. + const auto isNonEscapableSafe = [](Type t) { + return !isTypeUnknown(t) && isDiagnosedNonEscapable(t); + }; + const bool shouldDiagnose = + !lifetimeEntries.empty() || + llvm::any_of(parameterInfos, + [&](const ParamInfo ¶mInfo) { + return isNonEscapableSafe(paramInfo.typeInContext); + }) || + isNonEscapableSafe(resultTy); + bool unknownTypeFound = false; + for (const auto ¶mInfo : parameterInfos) { + if (isTypeUnknown(paramInfo.typeInContext)) { + unknownTypeFound = true; + if (shouldDiagnose) + diagnose(paramInfo.loc, diag::lifetime_dependence_unknown_type, + "parameter"); + } + } + if (isTypeUnknown(resultTy)) { + unknownTypeFound = true; + if (shouldDiagnose) + diagnose(returnLoc, diag::lifetime_dependence_unknown_type, "result"); + } + + if (unknownTypeFound) + return std::nullopt; + + return checkCommon(); + } + + /// Perform lifetime dependence checks for a function declaration. std::optional> checkFuncDecl() { - assert(isa(afd) || isa(afd)); + assert(nullptr != afd && (isa(afd) || isa(afd))); assert(depBuilder.empty()); // Handle Builtins first because, even though Builtins require @@ -592,6 +687,10 @@ class LifetimeDependenceChecker { return currentDependencies(); } + return checkCommon(); + } + + std::optional> checkCommon() { if (!ctx.LangOpts.hasFeature(Feature::LifetimeDependence) && !ctx.LangOpts.hasFeature(Feature::Lifetimes) && !ctx.SourceMgr.isImportMacroGeneratedLoc(returnLoc)) { @@ -695,7 +794,7 @@ class LifetimeDependenceChecker { // the extra formal self parameter, a dependency targeting the formal result // index would incorrectly target the SIL metatype parameter. bool hasImplicitSelfParam() const { - return !isInit && implicitSelfParam.has_value(); + return !isInit && implicitSelfParamInfo.has_value(); } // In SIL, implicit initializers and accessors become explicit. @@ -746,11 +845,11 @@ class LifetimeDependenceChecker { return qualifier; } } - if (implicitSelfParam.has_value()) { + if (implicitSelfParamInfo.has_value()) { if (isInit) { return "an initializer"; } - if (implicitSelfParam->param.isInOut()) { + if (implicitSelfParamInfo->param.isInOut()) { return "a mutating method"; } return "a method"; @@ -779,14 +878,14 @@ class LifetimeDependenceChecker { if (!hasImplicitSelfParam()) { return; } - if (!implicitSelfParam->param.isInOut()) { + if (!implicitSelfParamInfo->param.isInOut()) { return; } - if (!isDiagnosedNonEscapable(implicitSelfParam->typeInContext)) { + if (!isDiagnosedNonEscapable(implicitSelfParamInfo->typeInContext)) { return; } - if (!depBuilder.hasTargetDeps(implicitSelfParam->index)) { - ctx.Diags.diagnose(implicitSelfParam->loc, diagID, + if (!depBuilder.hasTargetDeps(implicitSelfParamInfo->index)) { + ctx.Diags.diagnose(implicitSelfParamInfo->loc, diagID, {StringRef(diagnosticQualifier())}); } } @@ -846,13 +945,13 @@ class LifetimeDependenceChecker { // Inferrence helper. bool isCompatibleWithOwnership(LifetimeDependenceKind kind, - ParamInfo const ¶m) const { + ParamInfo const ¶mInfo) const { if (kind == LifetimeDependenceKind::Inherit) { return true; } - auto paramType = param.typeInContext; - auto loweredOwnership = getLoweredOwnership(param.param); + auto paramType = paramInfo.typeInContext; + auto loweredOwnership = getLoweredOwnership(paramInfo.param); // Lifetime dependence always propagates through temporary BitwiseCopyable // values, even if the dependence is scoped. if (isBitwiseCopyable(paramType, ctx)) { @@ -1007,7 +1106,7 @@ class LifetimeDependenceChecker { diag::lifetime_dependence_invalid_self_in_init); return nullptr; } - return &implicitSelfParam.value(); + return &implicitSelfParamInfo.value(); } } } @@ -1054,17 +1153,17 @@ class LifetimeDependenceChecker { // Get the value ownership of param if it is non-default. Otherwise, compute // the lowered value ownership. The supplied Param must be a member of - // parameters or implicitSelfParam.value(). + // parameters or implicitSelfParamInfo.value(). ValueOwnership getLoweredOwnership(Param const ¶m) const { auto const ownership = param.getValueOwnership(); if (ownership != ValueOwnership::Default) return ownership; - if (isa(afd)) { + if (nullptr != afd && isa(afd)) { return ValueOwnership::Owned; } if (auto *ad = dyn_cast_or_null(afd)) { - auto const isSelfParameter = implicitSelfParam.has_value() && - ¶m == &(implicitSelfParam->param); + auto const isSelfParameter = implicitSelfParamInfo.has_value() && + ¶m == &(implicitSelfParamInfo->param); if (ad->getAccessorKind() == AccessorKind::Set) { return isSelfParameter ? ValueOwnership::InOut : ValueOwnership::Owned; } @@ -1224,9 +1323,10 @@ class LifetimeDependenceChecker { // Ignore mutating self. An 'inout' modifier effectively makes the parameter // a different type for lifetime inference. - if (hasImplicitSelfParam() && !implicitSelfParam->param.isInOut()) { - if (implicitSelfParam->typeInContext->getCanonicalType() == canResultTy) { - targetDeps->inheritIndices.set(implicitSelfParam->index); + if (hasImplicitSelfParam() && !implicitSelfParamInfo->param.isInOut()) { + if (implicitSelfParamInfo->typeInContext->getCanonicalType() == + canResultTy) { + targetDeps->inheritIndices.set(implicitSelfParamInfo->index); } } @@ -1259,7 +1359,7 @@ class LifetimeDependenceChecker { return; } bool nonEscapableSelf = - isDiagnosedNonEscapable(implicitSelfParam->typeInContext); + isDiagnosedNonEscapable(implicitSelfParamInfo->typeInContext); if (nonEscapableSelf && accessor->getImplicitSelfDecl()->isInOut()) { // First, infer the dependency of the inout non-Escapable 'self'. This may // result in two inferred dependencies for accessors (one targetting @@ -1273,7 +1373,7 @@ class LifetimeDependenceChecker { // Infer the result dependency of the result or yielded value on 'self' // based on the kind of accessor called by this wrapper accessor. if (auto dependenceKind = getImplicitAccessorResultDependence(accessor)) { - depBuilder.inferDependency(resultIndex, implicitSelfParam->index, + depBuilder.inferDependency(resultIndex, implicitSelfParamInfo->index, *dependenceKind); } } @@ -1302,7 +1402,7 @@ class LifetimeDependenceChecker { if (paramTypeInContext->hasError()) { return; } - depBuilder.inferInoutDependency(implicitSelfParam->index); + depBuilder.inferInoutDependency(implicitSelfParamInfo->index); // The 'newValue' dependence kind must match the getter's dependence kind // because the generated '_modify' accessor composes the getter's result @@ -1310,7 +1410,7 @@ class LifetimeDependenceChecker { // Escapable then the getter does not have any lifetime dependency, so the // setter cannot depend on 'newValue'. if (!paramTypeInContext->isEscapable()) { - depBuilder.inferDependency(implicitSelfParam->index, newValIdx, + depBuilder.inferDependency(implicitSelfParamInfo->index, newValIdx, LifetimeDependenceKind::Inherit); } break; @@ -1322,7 +1422,7 @@ class LifetimeDependenceChecker { // is the only useful dependence (a borrow of self is possible but not // useful), explicit annotation is required for now to confirm that the // mutated self cannot depend on anything stored at this address. - depBuilder.inferInoutDependency(implicitSelfParam->index); + depBuilder.inferInoutDependency(implicitSelfParamInfo->index); } break; default: @@ -1367,9 +1467,9 @@ class LifetimeDependenceChecker { for (auto &dep : *deps) { if (dep.getTargetIndex() != resultIndex) continue; - if (dep.checkInherit(implicitSelfParam->index)) + if (dep.checkInherit(implicitSelfParamInfo->index)) return LifetimeDependenceKind::Inherit; - if (dep.checkScope(implicitSelfParam->index)) + if (dep.checkScope(implicitSelfParamInfo->index)) return LifetimeDependenceKind::Scope; } } @@ -1378,7 +1478,7 @@ class LifetimeDependenceChecker { } // Either a Get or Modify without any wrapped accessor. Handle these like a // read of the stored property. - return inferLifetimeDependenceKind(*implicitSelfParam); + return inferLifetimeDependenceKind(*implicitSelfParamInfo); } // Infer implicit initialization. A non-Escapable initializer parameter can @@ -1445,26 +1545,26 @@ class LifetimeDependenceChecker { return; // same-type inferrence applied; don't issue diagnostics. bool nonEscapableSelf = - isDiagnosedNonEscapable(implicitSelfParam->typeInContext); + isDiagnosedNonEscapable(implicitSelfParamInfo->typeInContext); // Do not infer the result's dependence when the method is mutating and // 'self' is non-Escapable. Independently, a missing dependence on inout // 'self' will be diagnosed. Since an explicit annotation will be needed for // 'self', we also require the method's result to have an explicit // annotation. - if (nonEscapableSelf && implicitSelfParam->param.isInOut()) { + if (nonEscapableSelf && implicitSelfParamInfo->param.isInOut()) { return; } // Methods with parameters only apply to lazy inference. This does not // include accessors because a subscript's index is assumed not to be the // source of the result's dependency. - if (!isa(afd) && !useLazyInference() && + if (!(nullptr != afd && isa(afd)) && !useLazyInference() && parameterInfos.size() > 0) { return; } if (!useLazyInference() && !isImplicitOrSIL()) { // Require explicit @_lifetime(borrow self) for UnsafePointer-like self. if (!nonEscapableSelf && - isBitwiseCopyable(implicitSelfParam->typeInContext, ctx)) { + isBitwiseCopyable(implicitSelfParamInfo->typeInContext, ctx)) { diagnose(returnLoc, diag::lifetime_dependence_cannot_infer_bitwisecopyable, diagnosticQualifier(), "self"); @@ -1479,7 +1579,7 @@ class LifetimeDependenceChecker { } // Infer based on ownership if possible for either explicit accessors or // methods as long as they pass preceding ambiguity checks. - auto kind = inferLifetimeDependenceKind(*implicitSelfParam); + auto kind = inferLifetimeDependenceKind(*implicitSelfParamInfo); if (!kind) { // Special diagnostic for an attempt to depend on a consuming parameter. diagnose(returnLoc, @@ -1487,7 +1587,7 @@ class LifetimeDependenceChecker { "self", diagnosticQualifier()); return; } - resultDeps->addIfNew(implicitSelfParam->index, *kind); + resultDeps->addIfNew(implicitSelfParamInfo->index, *kind); } // Infer result dependence on a function or intitializer parameter. @@ -1602,15 +1702,15 @@ class LifetimeDependenceChecker { if (!hasImplicitSelfParam()) return; - if (!isDiagnosedNonEscapable(implicitSelfParam->typeInContext)) + if (!isDiagnosedNonEscapable(implicitSelfParamInfo->typeInContext)) return; assert(!isInit && "class initializers have Escapable self"); - if (!implicitSelfParam->param.isInOut()) + if (!implicitSelfParamInfo->param.isInOut()) return; // Assume that a mutating method does not depend on its parameters. - depBuilder.inferInoutDependency(implicitSelfParam->index); + depBuilder.inferInoutDependency(implicitSelfParamInfo->index); } // Infer @_lifetime(param: copy param) for 'inout' non-Escapable parameters. @@ -1714,6 +1814,16 @@ LifetimeDependenceInfo::get(ValueDecl *decl) { return LifetimeDependenceChecker::checkEnumElementDecl(eed); } +std::optional> +LifetimeDependenceInfo::getFromAST( + FunctionTypeRepr *funcRepr, AnyFunctionType *funcType, + ArrayRef lifetimeAttributes, DeclContext *dc, + GenericEnvironment *env) { + return LifetimeDependenceChecker(funcRepr, funcType, lifetimeAttributes, dc, + env) + .checkFuncType(); +} + void LifetimeDependenceInfo::dump() const { llvm::errs() << "target: " << getTargetIndex() << '\n'; if (isImmortal()) { @@ -1809,10 +1919,10 @@ static std::optional checkSILTypeModifiers( } case LifetimeDescriptor::DescriptorKind::Named: { assert(source.isImmortal()); - return LifetimeDependenceInfo(/*inheritLifetimeParamIndices*/ nullptr, - /*scopeLifetimeParamIndices*/ nullptr, - targetIndex, - /*isImmortal*/ true); + return LifetimeDependenceInfo( + /*inheritLifetimeParamIndices*/ nullptr, + /*scopeLifetimeParamIndices*/ nullptr, targetIndex, + /*isImmortal*/ true, /*isFromAnnotation*/ true); } default: llvm_unreachable("SIL can only have ordered or immortal lifetime " @@ -1829,6 +1939,7 @@ static std::optional checkSILTypeModifiers( : nullptr, targetIndex, /*isImmortal*/ false, + /*isFromAnnotation*/ true, addressableLifetimeParamIndices.any() ? IndexSubset::get(ctx, addressableLifetimeParamIndices) : nullptr, diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index cf8cb872cf180..fa5b1ad231fd2 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -4218,6 +4218,14 @@ CanType ProtocolCompositionType::getMinimalCanonicalType() const { return result.subst(existentialSig.Generalization)->getCanonicalType(); } +bool AnyFunctionType::hasExplicitLifetimeDependencies() const { + return hasLifetimeDependencies() && + llvm::any_of(getLifetimeDependencies(), + [](const LifetimeDependenceInfo &dep) { + return dep.isFromAnnotation(); + }); +} + ClangTypeInfo AnyFunctionType::getClangTypeInfo() const { switch (getKind()) { case TypeKind::Function: diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 5d402544766e2..a23467f04a720 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -2769,6 +2769,7 @@ void LifetimeDependenceInfoRequest::cacheResult( } auto *eed = cast(decl); eed->LazySemanticInfo.NoLifetimeDependenceInfo = 1; + return; } decl->getASTContext().evaluator.cacheNonEmptyOutput(*this, std::move(result)); diff --git a/lib/ASTGen/Sources/ASTGen/TypeAttrs.swift b/lib/ASTGen/Sources/ASTGen/TypeAttrs.swift index eedfcd0ffaece..4faf5e2d5b79d 100644 --- a/lib/ASTGen/Sources/ASTGen/TypeAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/TypeAttrs.swift @@ -85,6 +85,9 @@ extension ASTGenVisitor { case .Differentiable: return (self.generateDifferentiableTypeAttr(attribute: node)?.asTypeAttribute) .map(BridgedTypeOrCustomAttr.typeAttr(_:)) + case .Lifetime: + return (self.generateLifetimeTypeAttr(attribute: node)?.asTypeAttribute) + .map(BridgedTypeOrCustomAttr.typeAttr(_:)) case .OpaqueReturnTypeOf: return (self.generateOpaqueReturnTypeOfTypeAttr(attribute: node)?.asTypeAttribute) .map(BridgedTypeOrCustomAttr.typeAttr(_:)) @@ -247,6 +250,20 @@ extension ASTGenVisitor { kindLoc: differentiabilityLoc ) } + + func generateLifetimeTypeAttr(attribute node: AttributeSyntax) -> BridgedLifetimeTypeAttr? { + guard let entry = self.generateLifetimeEntry(attribute: node) else { + return nil + } + + return .createParsed( + self.ctx, + atLoc: self.generateSourceLoc(node.atSign), + nameLoc: self.generateSourceLoc(node.attributeName), + parensRange: self.generateAttrParensRange(attribute: node), + entry: entry + ) + } func generateIsolatedTypeAttr(attribute node: AttributeSyntax) -> BridgedIsolatedTypeAttr? { let isolationKindLoc = self.generateSourceLoc(node.arguments) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 51e0192019901..76bc796b32439 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2102,7 +2102,8 @@ namespace { } SmallVector lifetimeDependencies; LifetimeDependenceInfo immortalLifetime(nullptr, nullptr, resultIndex, - /*isImmortal*/ true); + /*isImmortal*/ true, + /*isFromAnnotation*/ true); lifetimeDependencies.push_back(immortalLifetime); Impl.SwiftContext.evaluator.cacheOutput( LifetimeDependenceInfoRequest{fd}, @@ -4216,7 +4217,7 @@ namespace { lifetimeDependencies.push_back(LifetimeDependenceInfo( nullptr, IndexSubset::get(Impl.SwiftContext, dependenciesOfRet), returnIdx, - /*isImmortal*/ false)); + /*isImmortal*/ false, /*isFromAnnotation*/ true)); Impl.SwiftContext.evaluator.cacheOutput( LifetimeDependenceInfoRequest{result}, Impl.SwiftContext.AllocateCopy(lifetimeDependencies)); @@ -4285,7 +4286,8 @@ namespace { SmallVector lifetimeDependencies; LifetimeDependenceInfo immortalLifetime(nullptr, nullptr, 0, - /*isImmortal*/ true); + /*isImmortal*/ true, + /*isFromAnnotation*/ true); if (hasUnsafeAPIAttr(decl) && !isEscapable(decl->getReturnType())) { lifetimeDependencies.push_back(immortalLifetime); Impl.SwiftContext.evaluator.cacheOutput( @@ -4373,8 +4375,11 @@ namespace { cast(decl)->getThisType()->getPointeeType()); for (auto& [idx, inheritedDepVec]: inheritedArgDependences) { - lifetimeDependencies.push_back(LifetimeDependenceInfo(inheritedDepVec.any() ? IndexSubset::get(Impl.SwiftContext, - inheritedDepVec): nullptr, nullptr, idx, /*isImmortal=*/false)); + lifetimeDependencies.push_back(LifetimeDependenceInfo( + inheritedDepVec.any() + ? IndexSubset::get(Impl.SwiftContext, inheritedDepVec) + : nullptr, + nullptr, idx, /*isImmortal=*/false, /*isFromAnnotation=*/true)); } if (inheritLifetimeParamIndicesForReturn.any() || @@ -4389,7 +4394,7 @@ namespace { scopedLifetimeParamIndicesForReturn) : nullptr, returnIdx, - /*isImmortal*/ false)); + /*isImmortal*/ false, /*isFromAnnotation*/ true)); else if (auto *ctordecl = dyn_cast(decl)) { // Assume default constructed view types have no dependencies. if (ctordecl->isDefaultConstructor() && diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 49a1ef2124166..e5f629231bf9b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5076,6 +5076,22 @@ ParserStatus Parser::parseTypeAttribute(TypeOrCustomAttr &result, return makeParserSuccess(); } + case TypeAttrKind::Lifetime: { + const auto entryResult = parseLifetimeEntry(AtLoc); + if (entryResult.isNull()) { + return makeParserError(); + } + + auto *entry = entryResult.get(); + + if (!justChecking) { + result = new (Context) LifetimeTypeAttr( + AtLoc, attrLoc, SourceRange(entry->getStartLoc(), entry->getEndLoc()), + entryResult.get()); + } + return makeParserSuccess(); + } + case TypeAttrKind::Convention: { ConventionTypeAttr *convention = nullptr; if (parseConventionAttributeInternal(AtLoc, attrLoc, convention, diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 5f3636dfcf6d3..df3fe77fb0871 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2957,8 +2957,10 @@ static CanSILFunctionType getSILFunctionType( = [&](const LifetimeDependenceInfo &formalDeps, unsigned target) -> LifetimeDependenceInfo { if (formalDeps.isImmortal()) { - return LifetimeDependenceInfo(nullptr, nullptr, - target, /*immortal*/ true); + return LifetimeDependenceInfo( + nullptr, nullptr, target, + /*immortal*/ true, + /*fromAnnotation*/ formalDeps.isFromAnnotation()); } auto lowerIndexSet = [&](IndexSubset *formal) -> IndexSubset * { @@ -2992,8 +2994,10 @@ static CanSILFunctionType getSILFunctionType( // entirely (such as if they were of `()` type), then there is effectively // no dependency, leaving behind an immortal value. if (!inheritIndicesSet && !scopeIndicesSet) { - return LifetimeDependenceInfo(nullptr, nullptr, target, - /*immortal*/ true); + return LifetimeDependenceInfo( + nullptr, nullptr, target, + /*immortal*/ true, + /*fromAnnotation*/ formalDeps.isFromAnnotation()); } SmallBitVector addressableDeps = scopeIndicesSet @@ -3009,12 +3013,11 @@ static CanSILFunctionType getSILFunctionType( IndexSubset *condAddressableSet = condAddressableDeps.any() ? IndexSubset::get(TC.Context, condAddressableDeps) : nullptr; - - return LifetimeDependenceInfo(inheritIndicesSet, - scopeIndicesSet, - target, /*immortal*/ false, - addressableSet, - condAddressableSet); + + return LifetimeDependenceInfo( + inheritIndicesSet, scopeIndicesSet, target, /*immortal*/ false, + /*fromAnnotation*/ formalDeps.isFromAnnotation(), addressableSet, + condAddressableSet); }; // Lower parameter dependencies. for (unsigned i = 0; i < parameterMap.size(); ++i) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index b482e57325e66..90e60f02d1fbb 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2468,7 +2468,7 @@ namespace { CallerIsolatedTypeRepr *nonisolatedNonsendingAttr; llvm::TinyPtrVector customAttrs; - EnumMap typeAttrs; + EnumMap> typeAttrs; llvm::SmallBitVector claimedCustomAttrs; FixedBitSet claimedTypeAttrs; @@ -2515,33 +2515,35 @@ namespace { claimedCustomAttrs.set(it - customAttrs.begin()); } - TypeAttribute *getWithoutClaiming(TypeAttrKind attrKind) { + ArrayRef getWithoutClaiming(TypeAttrKind attrKind) { auto it = typeAttrs.find(attrKind); if (it != typeAttrs.end()) { return *it; } else { - return nullptr; + return {}; } } /// Claim the attribute matching the given representative kind. /// It will not be diagnosed as unused. - TypeAttribute *claim(TypeAttrKind attrKind) { + ArrayRef claim(TypeAttrKind attrKind) { assert(getRepresentative(attrKind) == attrKind); auto it = typeAttrs.find(attrKind); if (it != typeAttrs.end()) { claimedTypeAttrs.insert(it - typeAttrs.begin()); return *it; } else { - return nullptr; + return {}; } } /// Claim all attributes for which the given function returns true. void claimAllWhere(ContextualTypeAttrResolver resolver) { size_t i = 0; - for (TypeAttribute *attr : typeAttrs) { - if (resolver(attr)) + for (auto &attrVector : typeAttrs) { + // Only claim the attribute if the resolver matches for every instance + // of it. + if (llvm::all_of(attrVector, resolver)) claimedTypeAttrs.insert(i); i++; } @@ -2551,8 +2553,10 @@ namespace { /// but process them in reverse source order. void reversedClaimAllWhere(ContextualTypeAttrResolver resolver) { for (size_t i = typeAttrs.size(); i > 0; --i) { - TypeAttribute *attr = typeAttrs.begin()[i - 1]; - if (resolver(attr)) + auto &attrVector = typeAttrs.begin()[i - 1]; + // Only claim the attribute if the resolver matches for every instance + // of it. + if (llvm::all_of(attrVector, resolver)) claimedTypeAttrs.insert(i - 1); } } @@ -2584,33 +2588,66 @@ namespace { } }; - template - AttrClass *claim(TypeAttrSet &attrs) { - auto attr = attrs.claim(AttrClass::StaticKind); - return cast_or_null(attr); + template + auto claim(TypeAttrSet &attrs) { + auto attrVector = attrs.claim(Kind); + if constexpr (!TypeAttribute::allowMultipleAttributes(Kind)) { + return attrVector.empty() ? nullptr : attrVector.front(); + } else { + return attrVector; + } } template - AttrClass *claim(TypeAttrSet *attrs) { - return (attrs ? claim(*attrs) : nullptr); + auto claim(TypeAttrSet &attrs) { + ArrayRef attrVector = attrs.claim(AttrClass::StaticKind); + if constexpr (!TypeAttribute::allowMultipleAttributes( + AttrClass::StaticKind)) { + return attrVector.empty() ? nullptr + : cast_or_null(attrVector.front()); + } else { + // The type attributes in attrVector are all instances of the type + // attribute that corresponds to the claimed type attribute kind (i.e. + // AttrClass) so casting the data pointer should always be safe. Since we + // cannot statically prove this, perform a paranoid check. + if (llvm::all_of(attrVector, [](TypeAttribute *attr) { + return isa(attr); + })) { + return ArrayRef( + reinterpret_cast(attrVector.data()), + attrVector.size()); + } else { + return ArrayRef{}; + } + } } template - AttrClass *getWithoutClaiming(TypeAttrSet &attrs) { - auto attr = attrs.getWithoutClaiming(AttrClass::StaticKind); - return cast_or_null(attr); + auto claim(TypeAttrSet *attrs) { + return (attrs ? claim(*attrs) + : decltype(claim(*attrs)){}); } template - std::enable_if_t, AttrClass *> - getWithoutClaiming(TypeAttrSet *attrs) { - return (attrs ? getWithoutClaiming(*attrs) : nullptr); + auto getWithoutClaiming(TypeAttrSet &attrs) { + auto attrVector = attrs.getWithoutClaiming(AttrClass::StaticKind); + if constexpr (!TypeAttribute::allowMultipleAttributes( + AttrClass::StaticKind)) { + return attrVector.empty() ? nullptr + : cast_or_null(attrVector.front()); + } else { + return attrVector; + } } template - std::enable_if_t, - CallerIsolatedTypeRepr *> - getWithoutClaiming(TypeAttrSet *attrs) { + auto getWithoutClaiming(TypeAttrSet *attrs) { + return (attrs ? getWithoutClaiming(*attrs) + : decltype(getWithoutClaiming(*attrs)){}); + } + + template <> + auto getWithoutClaiming(TypeAttrSet *attrs) { return attrs ? attrs->getNonisolatedNonsendingAttr() : nullptr; } } // end anonymous namespace @@ -3282,8 +3319,15 @@ void TypeAttrSet::accumulate(ArrayRef attrs) { auto insertResult = typeAttrs.insert(representativeKind, typeAttr); if (insertResult.second) continue; + // If an attribute with the same kind already exists, only add this one if + // there can be more than one. + if (TypeAttribute::allowMultipleAttributes(representativeKind)) { + insertResult.first->push_back(typeAttr); + continue; + } + // Dignose the conflict. - TypeAttribute *previousAttr = *insertResult.first; + TypeAttribute *previousAttr = insertResult.first->front(); diagnoseConflict(representativeKind, previousAttr, typeAttr); } @@ -3340,11 +3384,12 @@ void TypeAttrSet::diagnoseUnclaimed(const TypeResolution &resolution, // Type attributes size_t i = 0; - for (auto attr : typeAttrs) { + for (auto const &attrVector : typeAttrs) { if (claimedTypeAttrs.contains(i)) continue; i++; - diagnoseUnclaimed(attr, resolution, options, resolvedType); + for (auto attr : attrVector) + diagnoseUnclaimed(attr, resolution, options, resolvedType); } } @@ -3517,7 +3562,7 @@ TypeResolver::resolveAttributedType(TypeRepr *repr, TypeResolutionOptions option // These are the total type transforms. Type ty; - if (auto attr = attrs.claim(TAR_TypeTransformer)) { + if (auto attr = claim(attrs)) { if (auto opaqueAttr = dyn_cast(attr)) { ty = resolveOpaqueReturnType(repr, opaqueAttr->getMangledName(), opaqueAttr->getIndex(), @@ -3531,8 +3576,8 @@ TypeResolver::resolveAttributedType(TypeRepr *repr, TypeResolutionOptions option // The SIL metatype attributes are basically total type transforms, too. // TODO: this should really be restricted to lowered types - } else if (auto attr = isSILSourceFile() - ? attrs.claim(TAR_SILMetatype) : nullptr) { + } else if (auto attr = + isSILSourceFile() ? claim(attrs) : nullptr) { ty = resolveSILMetatype(repr, options, attr); // Okay, propagate attributes down to specific resolvers. @@ -4527,21 +4572,50 @@ NeverNullType TypeResolver::resolveASTFunctionType( .build(); // SIL uses polymorphic function types to resolve overloaded member functions. + AnyFunctionType *aft; if (auto genericSig = repr->getGenericSignature()) { - return GenericFunctionType::get(genericSig, params, outputTy, extInfo); + aft = GenericFunctionType::get(genericSig, params, outputTy, extInfo); + } else { + + auto fnTy = FunctionType::get(params, outputTy, extInfo); + if (fnTy->hasError()) + return fnTy; + + if (TypeChecker::diagnoseInvalidFunctionType(fnTy, repr->getLoc(), repr, + getDeclContext(), + resolution.getStage())) + return ErrorType::get(fnTy); + + aft = fnTy; } - auto fnTy = FunctionType::get(params, outputTy, extInfo); - - if (fnTy->hasError()) - return fnTy; + auto const lifetimeAttributes = claim(attrs); - if (TypeChecker::diagnoseInvalidFunctionType(fnTy, repr->getLoc(), repr, - getDeclContext(), - resolution.getStage())) - return ErrorType::get(fnTy); + // Lifetime dependence inference has to map parameter and result types into + // the generic environment of the DeclContext, so it must request the generic + // signature of the type. In order to prevent cycles in request evaluation, we + // defer lifetime dependence checking until the Interface type resolution + // stage. Since the analysis depends on the type's generic environment, it can + // only run after a type has already been produced. + if (!ctx.LangOpts.hasFeature(Feature::Lifetimes) && + !lifetimeAttributes.empty()) { + diagnose(lifetimeAttributes[0]->getAttrLoc(), + diag::requires_experimental_feature, "@_lifetime", false, + Feature::Lifetimes.getName()); + return ErrorType::get(getASTContext()); + } + + if (inStage(TypeResolutionStage::Interface)) { + if (auto const resolvedLifetimeDependence = + LifetimeDependenceInfo::getFromAST( + repr, aft, lifetimeAttributes, getDeclContext(), + resolution.getGenericSignature().getGenericEnvironment())) { + aft = aft->withExtInfo(aft->getExtInfo().withLifetimeDependencies( + *resolvedLifetimeDependence)); + } + } - return fnTy; + return aft; } NeverNullType TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, @@ -4617,7 +4691,7 @@ NeverNullType TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, bool hasError = false; auto coroutineKind = SILCoroutineKind::None; - if (auto coroAttr = attrs ? attrs->claim(TAR_SILCoroutine) : nullptr) { + if (auto coroAttr = attrs ? claim(*attrs) : nullptr) { assert(isa(coroAttr) || isa(coroAttr) || isa(coroAttr)); @@ -4637,7 +4711,8 @@ NeverNullType TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, } ParameterConvention callee = ParameterConvention::Direct_Unowned; - if (auto calleeAttr = attrs ? attrs->claim(TAR_SILCalleeConvention) : nullptr) { + if (auto calleeAttr = + attrs ? claim(*attrs) : nullptr) { assert(isa(calleeAttr) || isa(calleeAttr)); callee = (isa(calleeAttr) @@ -5054,7 +5129,7 @@ bool TypeResolver::resolveSingleSILResult( return false; } - if (auto conventionAttr = attrs.claim(TAR_SILValueConvention)) { + if (auto conventionAttr = claim(attrs)) { switch (conventionAttr->getKind()) { #define ERROR(ATTR, CONVENTION) \ case TypeAttrKind::ATTR: \ diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 197e7738ddc2b..0ad85efa772b8 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -9563,12 +9563,13 @@ ModuleFile::maybeReadLifetimeDependence() { unsigned targetIndex; unsigned paramIndicesLength; bool isImmortal; + bool isFromAnnotation; bool hasInheritLifetimeParamIndices; bool hasScopeLifetimeParamIndices; bool hasAddressableParamIndices; ArrayRef lifetimeDependenceData; LifetimeDependenceLayout::readRecord( - scratch, targetIndex, paramIndicesLength, isImmortal, + scratch, targetIndex, paramIndicesLength, isImmortal, isFromAnnotation, hasInheritLifetimeParamIndices, hasScopeLifetimeParamIndices, hasAddressableParamIndices, lifetimeDependenceData); @@ -9604,8 +9605,9 @@ ModuleFile::maybeReadLifetimeDependence() { hasScopeLifetimeParamIndices ? IndexSubset::get(ctx, scopeLifetimeParamIndices) : nullptr, - targetIndex, isImmortal, + targetIndex, isImmortal, isFromAnnotation, hasAddressableParamIndices ? IndexSubset::get(ctx, addressableParamIndices) - : nullptr); + : nullptr, + nullptr); } diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 47741ff33fd00..61295b365d77f 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 983; // @reparented inherited entry in support for Reparenting feature +const uint16_t SWIFTMODULE_VERSION_MINOR = 984; // @_lifetime attribute isFromAnnotation flag /// A standard hash seed used for all string hashes in a serialized module. /// @@ -2366,6 +2366,7 @@ namespace decls_block { BCVBR<4>, // targetIndex BCVBR<4>, // paramIndicesLength BCFixed<1>, // isImmortal + BCFixed<1>, // isFromAnnotation BCFixed<1>, // hasInheritLifetimeParamIndices BCFixed<1>, // hasScopeLifetimeParamIndices BCFixed<1>, // hasAddressableParamIndices diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index d9fb420ef2cf7..68e2c8c8e2c3e 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2780,7 +2780,7 @@ void Serializer::writeLifetimeDependencies( LifetimeDependenceLayout::emitRecord( Out, ScratchRecord, abbrCode, info.getTargetIndex(), info.getParamIndicesLength(), info.isImmortal(), - info.hasInheritLifetimeParamIndices(), + info.isFromAnnotation(), info.hasInheritLifetimeParamIndices(), info.hasScopeLifetimeParamIndices(), info.hasAddressableParamIndices(), paramIndices); paramIndices.clear(); diff --git a/test/ModuleInterface/Inputs/lifetime_underscored_dependence.swift b/test/ModuleInterface/Inputs/lifetime_underscored_dependence.swift index 9fe204db4b5ec..0f3712714b111 100644 --- a/test/ModuleInterface/Inputs/lifetime_underscored_dependence.swift +++ b/test/ModuleInterface/Inputs/lifetime_underscored_dependence.swift @@ -89,3 +89,30 @@ public struct RigidArray : ~Copyable { } } +// Function types + +@inlinable +@_lifetime(copy ne0) +public func takeCopier(f: @_lifetime(io: copy io) @_lifetime(copy inview) (_ inview: consuming AnotherView, _ io: inout AnotherView) -> AnotherView, ne0: consuming AnotherView, ne1: inout AnotherView) -> AnotherView { + let ne2 = f(ne0, &ne1) + return ne2 +} + +@inlinable +public func takeCopierUnannotated(f: (consuming AnotherView) -> AnotherView) {} + +public typealias ExplicitNestedType = @_lifetime(copy ne0) @_lifetime(ne1: copy ne0) ((AnotherView) -> AnotherView, _ ne0: consuming AnotherView, _ ne1: inout AnotherView) -> AnotherView + +@inlinable +public func takeExplicitNestedType(f: ExplicitNestedType) {} + + +@inlinable +public func returnableCopier(_ aView: AnotherView) -> AnotherView { + return aView +} + +@inlinable +public func returnCopier() -> @_lifetime(copy a) (_ a: AnotherView) -> AnotherView { + return returnableCopier +} diff --git a/test/ModuleInterface/lifetime_underscored_dependence_test.swift b/test/ModuleInterface/lifetime_underscored_dependence_test.swift index 4e6aeba094aa7..662ba3bb94911 100644 --- a/test/ModuleInterface/lifetime_underscored_dependence_test.swift +++ b/test/ModuleInterface/lifetime_underscored_dependence_test.swift @@ -123,3 +123,45 @@ import lifetime_underscored_dependence // CHECK: } // CHECK: #endif // CHECK: } + +// CHECK: #if compiler(>=5.3) && $ClosureLifetimes +// CHECK-NEXT: #if compiler(>=5.3) && $Lifetimes +// CHECK-NEXT: @_lifetime(copy ne0) +// CHECK-NEXT: @inlinable public func takeCopier(f: @_lifetime(io: copy io) @_lifetime(copy inview) (_ inview: consuming lifetime_underscored_dependence.AnotherView, _ io: inout lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView, ne0: consuming lifetime_underscored_dependence.AnotherView, ne1: inout lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView { +// CHECK-NEXT: let ne2 = f(ne0, &ne1) +// CHECK-NEXT: return ne2 +// CHECK-NEXT: } +// CHECK-NEXT: #else +// CHECK-NEXT: @lifetime(copy ne0) +// CHECK-NEXT: @inlinable public func takeCopier(f: @_lifetime(io: copy io) @_lifetime(copy inview) (_ inview: consuming lifetime_underscored_dependence.AnotherView, _ io: inout lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView, ne0: consuming lifetime_underscored_dependence.AnotherView, ne1: inout lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView { +// CHECK-NEXT: let ne2 = f(ne0, &ne1) +// CHECK-NEXT: return ne2 +// CHECK-NEXT: } +// CHECK-NEXT: #endif +// CHECK-NEXT: #endif + +// CHECK: @inlinable public func takeCopierUnannotated(f: (consuming lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView) {} + +// CHECK: #if compiler(>=5.3) && $ClosureLifetimes +// CHECK-NEXT: public typealias ExplicitNestedType = @_lifetime(copy ne0) @_lifetime(ne1: copy ne0) ((lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView, _ ne0: consuming lifetime_underscored_dependence.AnotherView, _ ne1: inout lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView +// CHECK-NEXT: #endif + +// CHECK: #if compiler(>=5.3) && $ClosureLifetimes +// CHECK-NEXT: @inlinable public func takeExplicitNestedType(f: @_lifetime(copy ne0) @_lifetime(ne1: copy ne0) ((lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView, _ ne0: consuming lifetime_underscored_dependence.AnotherView, _ ne1: inout lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView) {} +// CHECK-NEXT: #endif + +// CHECK: #if compiler(>=5.3) && $Lifetimes +// CHECK-NEXT: @inlinable public func returnableCopier(_ aView: lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView { +// CHECK-NEXT: return aView +// CHECK-NEXT: } +// CHECK-NEXT: #else +// CHECK-NEXT: @inlinable public func returnableCopier(_ aView: lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView { +// CHECK-NEXT: return aView +// CHECK-NEXT: } +// CHECK-NEXT: #endif + +// CHECK: #if compiler(>=5.3) && $ClosureLifetimes +// CHECK-NEXT: @inlinable public func returnCopier() -> @_lifetime(copy a) (_ a: lifetime_underscored_dependence.AnotherView) -> lifetime_underscored_dependence.AnotherView { +// CHECK-NEXT: return returnableCopier +// CHECK-NEXT: } +// CHECK-NEXT: #endif diff --git a/test/SIL/explicit_lifetime_dependence_specifiers.swift b/test/SIL/explicit_lifetime_dependence_specifiers.swift index 2393028abf162..5dd906e119946 100644 --- a/test/SIL/explicit_lifetime_dependence_specifiers.swift +++ b/test/SIL/explicit_lifetime_dependence_specifiers.swift @@ -8,6 +8,46 @@ import Builtin +struct NE: ~Escapable {} + +// Function type lifetime dependencies are printed as they appeared in Swift. +// Internal labels are preserved when a function type has explicit lifetime +// dependence information, since these may be used to refer to the sources and +// targets of lifetimes. + +// CHECK-LABEL: typealias LabelledNE2NE = @_lifetime(copy ne) (_ ne: NE) -> NE +typealias LabelledNE2NE = @_lifetime(copy ne) (_ ne: NE) -> NE +// CHECK-LABEL: typealias InferredNE2NE = (NE) -> NE +typealias InferredNE2NE = (NE) -> NE +// CHECK-LABEL: typealias InferredLabelledNE2NE = (NE) -> NE +typealias InferredLabelledNE2NE = (_ ne: NE) -> NE + +// CHECK-LABEL: typealias NamedLifetimeType = @_lifetime(copy ne) (_ ne: NE, _ ne2: NE) -> NE +typealias NamedLifetimeType = @_lifetime(copy ne) (_ ne: NE, _ ne2: NE) -> NE +// CHECK-LABEL: typealias InferredLifetimeType = (NE, NE) -> NE +typealias InferredLifetimeType = (_ ne: NE, NE) -> NE +// CHECK-LABEL: typealias ImmortalLifetimeType = @_lifetime(immortal) (_ ne: NE, _ ne2: NE) -> NE +typealias ImmortalLifetimeType = @_lifetime(immortal) (_ ne: NE, _ ne2: NE) -> NE + +// CHECK-LABEL: typealias NoLifetimeType = (Int) -> Int +typealias NoLifetimeType = (_ x: Int) -> Int + +// CHECK: typealias MixedLifetimeType = @_lifetime(copy ne0) (_ ne0: NE, _ neio: inout NE) -> NE +typealias MixedLifetimeType = @_lifetime(copy ne0) (_ ne0: NE, _ neio: inout NE) -> NE +// CHECK: typealias NestedLifetimeType = @_lifetime(neo: copy ne0) (_ nei: inout NE, _ ne0: NE, _ neo: inout NE) -> (NE) -> NE +typealias NestedLifetimeType = @_lifetime(neo: copy ne0) (_ nei: inout NE, _ ne0: NE, _ neo: inout NE) -> (_ ne1: NE) -> NE + +// CHECK-LABEL: typealias NestedType = @_lifetime(copy ne2) @_lifetime(ne3: copy ne2) (@_lifetime(copy ne0) @_lifetime(ne1: copy ne1) (_ ne0: NE, _ ne1: inout NE) -> NE, _ ne2: consuming NE, _ ne3: inout NE) -> NE +typealias NestedType = + @_lifetime(copy ne2) @_lifetime(ne3: copy ne2) + (@_lifetime(copy ne0) @_lifetime(ne1: copy ne1) (_ ne0: NE, _ ne1: inout NE) -> NE, + _ ne2: consuming NE, _ ne3: inout NE) -> NE + +// CHECK-LABEL: func takeNestedType(f: @_lifetime(copy ne2) @_lifetime(ne3: copy ne2) (@_lifetime(copy ne0) @_lifetime(ne1: copy ne1) (_ ne0: NE, _ ne1: inout NE) -> NE, _ ne2: consuming NE, _ ne3: inout NE) -> NE) + +// CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers14takeNestedType1fyAA2NEVA2E_AEztXE_AEnAEztXE_tF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (@guaranteed @noescape @callee_guaranteed (@guaranteed NE, @lifetime(copy 1) @inout NE) -> @lifetime(copy 0) @owned NE, @owned NE, @lifetime(copy 1) @inout NE) -> @lifetime(copy 1) @owned NE) -> () { +func takeNestedType(f: NestedType) {} + struct BufferView : ~Escapable { let ptr: UnsafeRawBufferPointer // CHECK-LABEL: sil hidden @$s39explicit_lifetime_dependence_specifiers10BufferViewVyACSWcfC : $@convention(method) (UnsafeRawBufferPointer, @thin BufferView.Type) -> @lifetime(borrow 0) @owned BufferView { diff --git a/test/SIL/implicit_lifetime_dependence.swift b/test/SIL/implicit_lifetime_dependence.swift index 9774c26df922e..1bcec61eef7f5 100644 --- a/test/SIL/implicit_lifetime_dependence.swift +++ b/test/SIL/implicit_lifetime_dependence.swift @@ -5,6 +5,16 @@ // REQUIRES: swift_feature_Lifetimes +struct NE: ~Escapable {} + +// CHECK-LABEL: typealias ImplicitNestedType = ((NE, inout NE) -> NE, consuming NE, inout NE) -> NE +typealias ImplicitNestedType = ((NE, inout NE) -> NE, consuming NE, inout NE) -> NE + +// CHECK-LABEL: func takeImplicitNestedType(f: ((NE, inout NE) -> NE, consuming NE, inout NE) -> NE) + +// CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence22takeImplicitNestedType1fyAA2NEVA2E_AEztXE_AEnAEztXE_tF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (@guaranteed @noescape @callee_guaranteed (@guaranteed NE, @lifetime(copy 1) @inout NE) -> @lifetime(copy 0) @owned NE, @owned NE, @lifetime(copy 2) @inout NE) -> @lifetime(copy 1) @owned NE) -> () { +func takeImplicitNestedType(f: ImplicitNestedType) {} + struct BufferView : ~Escapable { let ptr: UnsafeRawBufferPointer let c: Int @@ -228,3 +238,9 @@ public struct OuterNE: ~Escapable { self.inner1 = value } } + +// rdar://160894371 (Infer @_lifetime(param: copy param) for inout closure arguments) +// CHECK-LABEL: sil hidden @$s28implicit_lifetime_dependence20inoutClosureArgument1fyyAA2NEVzXE_tF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (@lifetime(copy 0) @inout NE) -> ()) -> () { +// CHECK-LABEL: } // end sil function '$s28implicit_lifetime_dependence20inoutClosureArgument1fyyAA2NEVzXE_tF' +func inoutClosureArgument(f: (inout NE) -> ()) { +} diff --git a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param_fail.swift b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param_fail.swift index dc12135660ae4..ad10c885ac7ee 100644 --- a/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param_fail.swift +++ b/test/SILOptimizer/lifetime_dependence/lifetime_dependence_param_fail.swift @@ -103,3 +103,38 @@ func bvcons_capture_escapelet(bv: consuming BV) -> ()->Int { // expected-error * let closure = { bv.c } return closure } + +// Higher-Order Function Tests + +@_lifetime(other: copy bv) +func take_assign_inout_copy(bv: BV, other: inout BV, f: @_lifetime(other: copy bv) (_ bv: BV, _ other: inout BV) -> ()) { + f(bv, &other) // OK +} + +@_lifetime(other: borrow bv) +func take_assign_inout_borrow(bv: BV, other: inout BV, f: @_lifetime(other: borrow bv) (_ bv: BV, _ other: inout BV) -> ()) { + f(bv, &other) // OK +} + +@_lifetime(bv: copy bv) +@_lifetime(other: copy bv) +func take_bvmut_assign_inout(bv: inout BV, other: inout BV, + f: @_lifetime(bv: copy bv) @_lifetime(other: copy bv) + (_ bv: inout BV, _ other: inout BV) -> ()) { + f(&bv, &other) // OK +} + +struct NE2: ~Escapable {} + +@_lifetime(oNE: copy ne) +func succeed_transfer_ne(ne: consuming NE2, oNE: inout NE2, f: (NE2) -> NE2) { + oNE = f(ne) // OK +} + +func fail_smuggle_ne(oNE: inout NE2, f: (NE2) -> NE2) { + // expected-error @-1 {{lifetime-dependent variable 'oNE' escapes its scope}} + let ne = NE2() + // expected-note @-1 {{it depends on the lifetime of this parent value}} + oNE = f(ne) + // expected-note @+1 {{this use causes the lifetime-dependent value to escape}} +} diff --git a/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift b/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift index bac45aa706381..7405bc231d9e2 100644 --- a/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift +++ b/test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift @@ -237,13 +237,6 @@ func testIndirectNonForwardedResult(arg1: GNE, arg2: GNE) -> GNE { forward(arg2) // expected-note {{this use causes the lifetime-dependent value to escape}} } -func testIndirectClosureResult(f: () -> GNE) -> GNE { - f() - // expected-error @-1{{lifetime-dependent variable '$return_value' escapes its scope}} - // expected-note @-3{{it depends on the lifetime of argument '$return_value'}} - // expected-note @-3{{this use causes the lifetime-dependent value to escape}} -} - // ============================================================================= // Coroutines // ============================================================================= diff --git a/test/Sema/lifetime_attr_nofeature.swift b/test/Sema/lifetime_attr_nofeature.swift index a86d46ec238b5..68a935e46f227 100644 --- a/test/Sema/lifetime_attr_nofeature.swift +++ b/test/Sema/lifetime_attr_nofeature.swift @@ -13,3 +13,8 @@ func f_inout_infer(a: inout MutableRawSpan) {} // DEFAULT OK func f_inout_no_infer(a: inout MutableRawSpan, b: RawSpan) {} // DEFAULT OK +typealias DeriveType = @_lifetime(copy ne) (_ ne: NE) -> NE // expected-error{{'@_lifetime' attribute is only valid when experimental feature Lifetimes is enabled}} + +typealias InoutInferType = (inout MutableRawSpan) -> Void // DEFAULT OK + +typealias InoutNoInferType = (inout MutableRawSpan, RawSpan) -> Void // DEFAULT OK diff --git a/test/Sema/lifetime_dependence_functype.swift b/test/Sema/lifetime_dependence_functype.swift index ee704ac9292ab..871af3d61b9c7 100644 --- a/test/Sema/lifetime_dependence_functype.swift +++ b/test/Sema/lifetime_dependence_functype.swift @@ -24,12 +24,17 @@ func applyAnnotatedTransfer(ne: NE, @_lifetime(0) transfer: (NE) -> NE) -> NE { } @_lifetime(copy ne) -func applyTransfer(ne: NE, transfer: (NE) -> NE) -> NE { +func applyCorrectlyAnnotatedTransfer(ne: NE, transfer: @_lifetime(copy ne) (_ ne: NE) -> NE) -> NE { // OK + transfer(ne) +} + +@_lifetime(copy ne) +func applyTransfer(ne: NE, transfer: (NE) -> NE) -> NE { // OK, copy 0 inferred transfer(ne) } func testTransfer(nc: consuming NC) { - let transferred = applyTransfer(ne: nc.ne, transfer: transfer) // expected-error{{does not conform to expected type 'Escapable'}} e/xpected-error{{cannot convert value of type '(NE) -> @_lifetime(copy 0) NE' to expected argument type '(NE) -> NE'}} + let transferred = applyTransfer(ne: nc.ne, transfer: transfer) // OK _ = consume nc _ = transfer(transferred) @@ -45,8 +50,185 @@ func applyBorrow(nc: borrowing NC, borrow: (borrowing NC) -> NE) -> NE { } func testBorrow(nc: consuming NC) { - let borrowed = applyBorrow(nc: nc, borrow: borrow) // expected-error{{does not conform to expected type 'Escapable'}} ex/pected-error{{cannot convert value of type '(borrowing NC) -> @_lifetime(borrow 0) NE' to expected argument type '(borrowing NC) -> NE}} + let borrowed = applyBorrow(nc: nc, borrow: borrow) // OK _ = consume nc _ = transfer(borrowed) } +// Tests adapted from lifetime_attr.swift for function types. +class Klass {} +typealias InvalidAttrOnNonExistingParamType = @_lifetime(copy nonexisting) (_ ne: NE) -> NE // expected-error{{invalid parameter name specified 'nonexisting'}} + +typealias InvalidAttrOnNonExistingSelfType = @_lifetime(copy self) (_ ne: NE) -> NE // expected-error{{invalid lifetime dependence specifier on non-existent self}} + +typealias InvalidAttrOnNonExistingParamIndexType = @_lifetime(2) (_ ne: NE) -> NE // expected-error{{invalid parameter index specified '2'}} + +typealias InvalidDuplicateLifetimeDependenceType = @_lifetime(copy ne, borrow ne) (_ ne: borrowing NE) -> NE // expected-error{{duplicate lifetime dependence specifier}} + +typealias InvalidDependenceConsumeKlassType = @_lifetime(borrow x) (_ x: consuming Klass) -> NE // expected-error{{invalid use of borrow dependence with consuming ownership}} + +typealias InvalidDependenceBorrowKlassType = @_lifetime(&x) (_ x: borrowing Klass) -> NE // expected-error{{invalid use of & dependence with borrowing ownership}} + // expected-note @-1{{use '@_lifetime(borrow x)' instead}} + +typealias InvalidDependenceInoutKlassType = @_lifetime(borrow x) (_ x: inout Klass) -> NE // expected-error{{invalid use of borrow dependence with inout ownership}} + // expected-note @-1{{use '@_lifetime(&x)' instead}} + +typealias InvalidDependenceConsumeIntType = @_lifetime(borrow x) (_ x: consuming Int) -> NE // OK + +typealias InvalidDependenceBorrowIntType = @_lifetime(&x) (_ x: borrowing Int) -> NE // expected-error{{invalid use of & dependence with borrowing ownership}} + // expected-note @-1{{use '@_lifetime(borrow x)' instead}} + +typealias InvalidDependenceInoutIntType = @_lifetime(borrow x) (_ x: inout Int) -> NE // expected-error{{invalid use of borrow dependence with inout ownership}} + // expected-note @-1{{use '@_lifetime(&x)' instead}} + +typealias InvalidTargetType = + @_lifetime(result: copy source1) + @_lifetime(result: copy source2) // expected-error{{invalid duplicate target lifetime dependencies on function}} + (_ result: inout NE, _ source1: consuming NE, _ source2: consuming NE) -> () + +typealias InvalidSourceType = + @_lifetime(result: copy source) + @_lifetime(result: borrow source) // expected-error{{invalid duplicate target lifetime dependencies on function}} + (_ result: inout NE, _ source: consuming NE) -> () + +typealias ImmortalConflictType = @_lifetime(immortal) (_ immortal: Int) -> NE // expected-error{{conflict between the parameter name and 'immortal' contextual keyword}} + +typealias TestParameterDepType = @_lifetime(span: borrow holder) (_ holder: AnyObject, _ span: Span) -> () // expected-error{{lifetime-dependent parameter 'span' must be 'inout'}} + +typealias InoutLifetimeDependenceType = @_lifetime(&ne) (_ ne: inout NE) -> NE + +typealias DependOnEscapableType1 = @_lifetime(copy k) (_ k: inout Klass) -> NE // expected-error{{cannot copy the lifetime of an Escapable type}} + // expected-note@-1{{use '@_lifetime(&k)' instead}} + +typealias DependOnEscapableType2 = @_lifetime(copy k) (_ k: borrowing Klass) -> NE // expected-error{{cannot copy the lifetime of an Escapable type}} + // expected-note@-1{{use '@_lifetime(borrow k)' instead}} + +typealias DependOnEscapableType3 = @_lifetime(copy k) (_ k: consuming Klass) -> NE // expected-error{{cannot copy the lifetime of an Escapable type}} + // expected-note@-1{{use '@_lifetime(borrow k)' instead}} + +typealias GetIntType1 = @_lifetime(inValue) (_ inValue: Int) -> Int // expected-error{{invalid lifetime dependence on an Escapable result}} + +typealias GetIntType2 = @_lifetime(outValue: borrow inValue) (_ outValue: inout Int, _ inValue: Int) -> () // expected-error{{invalid lifetime dependence on an Escapable target}} + +typealias GetGenericEscapableType = @_lifetime(inValue) (_ inValue: T) -> T // expected-error{{invalid lifetime dependence on an Escapable result}} + +typealias GetGenericEscapableType2 = + @_lifetime(outValue: borrow inValue) // expected-error{{invalid lifetime dependence on an Escapable target}} + (_ outValue: inout T, _ inValue: T) -> () + +typealias GetGenericNonEscapableType2 = + @_lifetime(borrow inValue) (_ inValue: borrowing T) -> T // OK + +typealias GetGenericCorrectType = + @_lifetime(outValue: borrow inValue) (_ outValue: inout T, _ inValue: borrowing T) -> () // OK + +@_lifetime(outValue: copy inValue) // OK +func getGeneric(_ outValue: inout T, _ inValue: borrowing T) { // expected-note{{in call to function 'getGeneric'}} + outValue = inValue +} + +@_lifetime(outValue: borrow inValue) // OK +func getGeneric2(_ outValue: inout T, _ /* borrowing inferred */ inValue: T) { + outValue = inValue +} + +@_lifetime(outValueI: immortal) // OK +func getImmortalNE(_ outValueI: inout NE, _ inValueI: borrowing NE) { + outValueI = NE() +} + +@_lifetime(o: borrow i) // OK +func takeGetGenericAndArgs(f: @_lifetime(outValue: borrow inValue) + (_ outValue: inout T, _ inValue: borrowing T) -> (), o: inout T, i: T) { + f(&o, i) +} + +do { + let x = NE() + var y = NE() + takeGetGenericAndArgs(f: getGeneric, o: &y, i: x) + // expected-error@-1{{cannot convert value of type '(inout T, borrowing T) -> ()' to expected argument type '@_lifetime(outValue: borrow inValue) (_ outValue: inout NE, _ inValue: borrowing NE) -> ()'}} + // expected-error@-2{{generic parameter 'T' could not be inferred}} +} +do { + let x = NE() + var y = NE() + takeGetGenericAndArgs(f: getGeneric2, o: &y, i: x) // OK +} +do { + let x = NE() + var y = NE() + takeGetGenericAndArgs(f: getImmortalNE, o: &y, i: x) + // expected-error@-1{{cannot convert value of type '@_lifetime(0: immortal) (inout NE, borrowing NE) -> ()' to expected argument type '@_lifetime(outValue: borrow inValue) (_ outValue: inout NE, _ inValue: borrowing NE) -> ()'}} +} +do { + let _ = transfer // OK + let _: (NE) -> NE = transfer // OK + let _: @_lifetime(copy ne) (_ ne: NE) -> NE = transfer // OK +} + +// rdar://166912068 (Incorrect error when passing a local function with a non-escapable parameter) +struct NEWithSpan: ~Escapable { + var span: RawSpan + + @_lifetime(copy span) + init(span: RawSpan) { + self.span = span + } +} +func takeBody(body: (inout NEWithSpan) -> Void) {} +func checkNestedFunctions() { + func doIt(ne: inout NEWithSpan) {} + takeBody(body: doIt) +} + +// Bail-out cases where lifetime dependence checking cannot run. +struct CNE: ~Escapable { + let ne: T + @_lifetime(copy ne) + init(ne: T) { + self.ne = ne + } +} + +@_lifetime(borrow cne) +func copyCNE(cne: CNE) -> CNE { + return cne +} + +public let UnboundGenericParamFunctionType : (CNE) -> CNE = copyCNE // expected-error{{lifetime dependence checking failed due to unknown parameter type}} + // expected-error@-1{{value of type 'CNE' does not conform to specified type 'Escapable'}} +public let UnboundGenericParamFunctionTypeAnnotated : @_lifetime(borrow cne) (_ cne: CNE) -> CNE = copyCNE // expected-error{{lifetime dependence checking failed due to unknown parameter type}} + // expected-error@-1{{value of type 'CNE' does not conform to specified type 'Escapable'}} +public let UnboundGenericResultFunctionType : (CNE) -> CNE = copyCNE // expected-error{{lifetime dependence checking failed due to unknown result type}} + // expected-error@-1{{value of type 'CNE' does not conform to specified type 'Escapable'}} +public let UnboundGenericResultFunctionTypeAnnotated : @_lifetime(borrow cne) (_ cne: CNE) -> CNE = copyCNE // expected-error{{lifetime dependence checking failed due to unknown result type}} + // expected-error@-1{{value of type 'CNE' does not conform to specified type 'Escapable'}} + +public let TypeParameterParamFunctionType = copyCNE as (_) -> CNE // expected-error{{lifetime dependence checking failed due to unknown parameter type}} + // expected-error@-1{{failed to produce diagnostic for expression}} +public let TypeParameterParamFunctionTypeAnnotated = copyCNE as @_lifetime(borrow cne) (_ cne: _) -> CNE // expected-error{{lifetime dependence checking failed due to unknown parameter type}} + // expected-error@-1{{failed to produce diagnostic for expression}} +public let TypeParameterResultFunctionType = copyCNE as (CNE) -> _ // expected-error{{lifetime dependence checking failed due to unknown result type}} + // expected-error@-1{{failed to produce diagnostic for expression}} +public let TypeParameterResultFunctionTypeAnnotated = copyCNE as @_lifetime(borrow cne) (_ cne: CNE) -> _ // expected-error{{lifetime dependence checking failed due to unknown result type}} + // expected-error@-1{{failed to produce diagnostic for expression}} + +// Closure Context Dependence Tests + +// This case should pass after we add support for dependencies on the closure context. +// +// We had to move it out of +// SILOptimizer/lifetime_dependence/verify_diagnostics.swift because it causes +// an error during type checking with the current version of function type +// lifetime checking, preventing the SIL diagnostic checks that file tests from +// running. +// +// TODO: Add more diagnostic tests when implementing closure context +// dependencies, including SILOptimizer diagnostic tests such as the one this +// originated as. +func testIndirectClosureResult(f: () -> CNE) -> CNE { + // expected-error @-1{{a function with a ~Escapable result needs a parameter to depend on}} + // expected-note @-2{{'@_lifetime(immortal)' can be used to indicate that values produced by this initializer have no lifetime dependencies}} + f() +}