Merge pull request #86842 from aidan-hall/just-func-type-lifetimes-try-print

LifetimeDependence: Support function types
This commit is contained in:
Aidan Hall
2026-02-06 10:09:25 +00:00
committed by GitHub
34 changed files with 1056 additions and 171 deletions
+7
View File
@@ -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(
+9 -2
View File
@@ -368,7 +368,9 @@ public:
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<LifetimeDependenceInfo> lifetimeDependence) {
if (!lifetimeDependence.has_value()) {
return;
@@ -380,10 +382,15 @@ public:
ArrayRef<LifetimeDependenceInfo> 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<AnyFunctionType::Param> params);
private:
virtual void anchor();
};
+24
View File
@@ -4274,6 +4274,17 @@ public:
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 @@ public:
void printImpl(ASTPrinter &printer, const PrintOptions &options) const;
};
class LifetimeTypeAttr : public SimpleTypeAttrWithArgs<TypeAttrKind::Lifetime> {
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<TypeAttrKind::OpaqueReturnTypeOf> {
Located<StringRef> MangledName;
+2
View File
@@ -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
+54 -10
View File
@@ -31,8 +31,10 @@
namespace swift {
class AbstractFunctionDecl;
class AnyFunctionType;
class FunctionTypeRepr;
class LifetimeDependentTypeRepr;
class LifetimeTypeAttr;
class SILParameterInfo;
class SILResultInfo;
@@ -190,23 +192,28 @@ public:
class LifetimeDependenceInfo {
IndexSubset *inheritLifetimeParamIndices;
IndexSubset *scopeLifetimeParamIndices;
llvm::PointerIntPair<IndexSubset *, 1, bool>
addressableParamIndicesAndImmortal;
// The outer bool is the "isFromAnnotation" bit. The inner one is the
// "isImmortal" bit.
llvm::PointerIntPair<llvm::PointerIntPair<IndexSubset *, 1, bool>, 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 @@ public:
}
}
/// 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 @@ public:
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 @@ public:
return scopeLifetimeParamIndices != nullptr;
}
bool hasAddressableParamIndices() const {
return addressableParamIndicesAndImmortal.getPointer() != nullptr;
return addressableParamIndicesAndImmortalAndFromAnnotation.getPointer()
.getPointer() != nullptr;
}
unsigned getParamIndicesLength() const {
@@ -282,7 +313,8 @@ public:
/// 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 @@ public:
&& 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<bool> &concatenatedData) const;
@@ -321,6 +359,12 @@ public:
getFromSIL(FunctionTypeRepr *funcRepr, ArrayRef<SILParameterInfo> params,
ArrayRef<SILResultInfo> results, DeclContext *dc);
/// Builds LifetimeDependenceInfo from a function type.
static std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
getFromAST(FunctionTypeRepr *funcRepr, AnyFunctionType *funcType,
ArrayRef<LifetimeTypeAttr *> lifetimeAttributes, DeclContext *dc,
GenericEnvironment *env);
bool operator==(const LifetimeDependenceInfo &other) const {
return this->isImmortal() == other.isImmortal() &&
this->getTargetIndex() == other.getTargetIndex() &&
+1 -1
View File
@@ -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:
+1
View File
@@ -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)
+4
View File
@@ -3790,6 +3790,10 @@ public:
return Bits.AnyFunctionType.HasLifetimeDependencies;
}
/// Type has lifetime dependencies derived from explicit @_lifetime
/// attributes.
bool hasExplicitLifetimeDependencies() const;
ClangTypeInfo getClangTypeInfo() const;
ClangTypeInfo getCanonicalClangTypeInfo() const;
+1
View File
@@ -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.
+1
View File
@@ -198,6 +198,7 @@ struct BridgedLifetimeDependenceInfo {
swift::IndexSubset *_Nullable conditionallyAddressableParamIndices;
SwiftUInt targetIndex;
bool immortal;
bool fromAnnotation;
BRIDGED_INLINE BridgedLifetimeDependenceInfo(swift::LifetimeDependenceInfo info);
+5 -3
View File
@@ -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<swift::LifetimeDependenceInfo>().size();
+164 -17
View File
@@ -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<AnyFunctionType::Param> 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<AnyFunctionType::Param> 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<AnyFunctionType::Param> 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<AnyFunctionType::Param> params) {
*this << getLifetimeDependenceInfoSwiftString(lifetimeDependence, params);
}
void ASTPrinter::anchor() {}
void ASTPrinter::printIndent() {
@@ -6761,6 +6863,17 @@ public:
Printer.printSimpleAttr("@Sendable") << " ";
}
// Print lifetime dependencies using Swift syntax.
if (!Options.PrintInSILBody && fnType->hasLifetimeDependencies()) {
ArrayRef<AnyFunctionType::Param> 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:
@@ -6946,8 +7059,16 @@ public:
}
}
/// Print a function type's parameter list.
///
/// * printExternalLabels: Whether to print the parameters' external labels.
///
/// * printAllLabels: Whether to print all parameter labels (external and
/// internal). This is used when a function has explicit lifetime
/// dependencies, which may refer to the parameters by their labels.
void visitAnyFunctionTypeParams(
ArrayRef<AnyFunctionType::Param> Params, bool printLabels,
ArrayRef<AnyFunctionType::Param> Params, bool printExternalLabels,
bool printAllLabels,
ArrayRef<LifetimeDependenceInfo> lifetimeDependencies) {
Printer << "(";
@@ -6961,16 +7082,23 @@ public:
Printer.printStructurePost(PrintStructureKind::FunctionParameter);
};
if ((Options.AlwaysTryPrintParameterLabels || printLabels) &&
bool hasValidInternalLabel = Param.hasInternalLabel() &&
!Param.getInternalLabel().hasDollarPrefix();
if ((Options.AlwaysTryPrintParameterLabels || printExternalLabels ||
printAllLabels) &&
Param.hasLabel()) {
// Label printing was requested and we have an external label. Print it
// and omit the internal label.
Printer.printName(Param.getLabel(),
PrintNameContext::FunctionParameterExternal);
if (printAllLabels && hasValidInternalLabel) {
Printer << ' ';
Printer.printName(Param.getInternalLabel());
}
Printer << ": ";
} else if (Options.AlwaysTryPrintParameterLabels &&
Param.hasInternalLabel() &&
!Param.getInternalLabel().hasDollarPrefix()) {
} else if ((Options.AlwaysTryPrintParameterLabels || printAllLabels) &&
hasValidInternalLabel) {
// We didn't have an external parameter label but were requested to
// always try and print parameter labels.
// If the internal label is a valid internal parameter label (does not
@@ -6982,7 +7110,9 @@ public:
Printer << ": ";
}
Printer.printLifetimeDependenceAt(lifetimeDependencies, i);
if (Options.PrintInSILBody) {
Printer.printLifetimeDependenceAt(lifetimeDependencies, i);
}
auto type = Param.getPlainType();
if (Param.isVariadic()) {
@@ -6998,6 +7128,12 @@ public:
Printer << ")";
}
static bool shouldPrintLabelsForLifetimeAttributes(AnyFunctionType const *T) {
// If the type has explicit lifetime dependencies, print the labels, because
// explicit lifetimes use them to describe their sources and targets.
return T->hasExplicitLifetimeDependencies();
}
void visitFunctionType(FunctionType *T,
NonRecursivePrintOptions nrOptions) {
Printer.callPrintStructurePre(PrintStructureKind::FunctionType);
@@ -7008,7 +7144,10 @@ public:
printFunctionExtInfo(T, nrOptions);
// If we're stripping argument labels from types, do it when printing.
visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/ false,
visitAnyFunctionTypeParams(T->getParams(),
/*printExternalLabels*/ false,
/*printAllLabels*/
shouldPrintLabelsForLifetimeAttributes(T),
T->getLifetimeDependencies());
if (T->hasExtInfo()) {
@@ -7034,7 +7173,10 @@ public:
Printer.printKeyword("sending ", Options);
}
Printer.printLifetimeDependence(T->getLifetimeDependenceForResult());
if (Options.PrintInSILBody) {
Printer.printLifetimeDependenceAt(T->getLifetimeDependencies(),
T->getParams().size());
}
Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType);
T->getResult().print(Printer, Options);
@@ -7069,7 +7211,9 @@ public:
PrintAST::defaultGenericRequirementFlags(Options));
Printer << " ";
visitAnyFunctionTypeParams(T->getParams(), /*printLabels*/ true,
visitAnyFunctionTypeParams(T->getParams(),
/*printExternalLabels*/ true, /*printAllLabels*/
shouldPrintLabelsForLifetimeAttributes(T),
T->getLifetimeDependencies());
if (T->hasExtInfo()) {
@@ -7091,8 +7235,10 @@ public:
Printer << " -> ";
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);
@@ -7197,7 +7343,7 @@ public:
}
Printer << ") -> ";
Printer.printLifetimeDependence(T->getLifetimeDependenceForResult());
Printer.printSILLifetimeDependence(T->getLifetimeDependenceForResult());
bool parenthesizeResults = mustParenthesizeResults(T);
if (parenthesizeResults)
@@ -7841,12 +7987,13 @@ void AnyFunctionType::printParams(ArrayRef<AnyFunctionType::Param> Params,
printParams(Params, Printer, PO);
}
void AnyFunctionType::printParams(ArrayRef<AnyFunctionType::Param> 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
@@ -8035,7 +8182,7 @@ void SILParameterInfo::print(
}
if (lifetimeDependence) {
Printer.printLifetimeDependence(*lifetimeDependence);
Printer.printSILLifetimeDependence(*lifetimeDependence);
}
if (options.contains(SILParameterInfo::ImplicitLeading)) {
+8
View File
@@ -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);
@@ -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,
+50
View File
@@ -233,6 +233,56 @@ static bool usesFeatureLifetimes(Decl *decl) {
return false;
}
static bool hasLifetimeDependencies(Type type) {
if (auto *aft = type->getAs<AnyFunctionType>()) {
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<AbstractFunctionDecl>(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<AnyFunctionType>()->getResult();
useFound = resultType.findIf(hasLifetimeDependencies);
return Action::StopIf(useFound);
}
// Check for lifetime dependence info on param and type decls.
if (isa<ParamDecl>(D) || isa<TypeDecl>(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<LifetimeAttr>()) {
+175 -64
View File
@@ -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 @@ public:
}
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 @@ public:
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<ParamInfo> implicitSelfParam;
std::optional<ParamInfo> implicitSelfParamInfo;
LifetimeDependenceBuilder depBuilder;
@@ -506,8 +523,8 @@ public:
}
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 @@ public:
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 @@ public:
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 @@ public:
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<ConstructorDecl>(afd)),
isImplicit(afd->isImplicit()), isInit(isa<ConstructorDecl>(afd)),
hasUnsafeNonEscapableResult(
afd->getAttrs().hasAttribute<UnsafeNonEscapableResultAttr>()) {}
afd->getAttrs().hasAttribute<UnsafeNonEscapableResultAttr>()) {}
LifetimeDependenceChecker(FunctionTypeRepr *funcRepr,
AnyFunctionType *funcType,
ArrayRef<LifetimeTypeAttr *> 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 &parameter = 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<llvm::ArrayRef<LifetimeDependenceInfo>>
currentDependencies() const {
return depBuilder.initializeDependenceInfoArray(ctx);
}
/// Perform lifetime dependence checks for a function type.
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>> 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 &paramInfo) {
return isNonEscapableSafe(paramInfo.typeInContext);
}) ||
isNonEscapableSafe(resultTy);
bool unknownTypeFound = false;
for (const auto &paramInfo : 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<llvm::ArrayRef<LifetimeDependenceInfo>> checkFuncDecl() {
assert(isa<FuncDecl>(afd) || isa<ConstructorDecl>(afd));
assert(nullptr != afd && (isa<FuncDecl>(afd) || isa<ConstructorDecl>(afd)));
assert(depBuilder.empty());
// Handle Builtins first because, even though Builtins require
@@ -592,6 +687,10 @@ public:
return currentDependencies();
}
return checkCommon();
}
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>> checkCommon() {
if (!ctx.LangOpts.hasFeature(Feature::LifetimeDependence)
&& !ctx.LangOpts.hasFeature(Feature::Lifetimes)
&& !ctx.SourceMgr.isImportMacroGeneratedLoc(returnLoc)) {
@@ -695,7 +794,7 @@ protected:
// 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 @@ protected:
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 @@ protected:
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 @@ protected:
// Inferrence helper.
bool isCompatibleWithOwnership(LifetimeDependenceKind kind,
ParamInfo const &param) const {
ParamInfo const &paramInfo) 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 @@ protected:
diag::lifetime_dependence_invalid_self_in_init);
return nullptr;
}
return &implicitSelfParam.value();
return &implicitSelfParamInfo.value();
}
}
}
@@ -1054,17 +1153,17 @@ protected:
// 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 &param) const {
auto const ownership = param.getValueOwnership();
if (ownership != ValueOwnership::Default)
return ownership;
if (isa<ConstructorDecl>(afd)) {
if (nullptr != afd && isa<ConstructorDecl>(afd)) {
return ValueOwnership::Owned;
}
if (auto *ad = dyn_cast_or_null<AccessorDecl>(afd)) {
auto const isSelfParameter = implicitSelfParam.has_value() &&
&param == &(implicitSelfParam->param);
auto const isSelfParameter = implicitSelfParamInfo.has_value() &&
&param == &(implicitSelfParamInfo->param);
if (ad->getAccessorKind() == AccessorKind::Set) {
return isSelfParameter ? ValueOwnership::InOut : ValueOwnership::Owned;
}
@@ -1224,9 +1323,10 @@ protected:
// 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 @@ protected:
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 @@ protected:
// 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 @@ protected:
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 @@ protected:
// 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 @@ protected:
// 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 @@ protected:
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 @@ protected:
}
// 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 @@ protected:
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<AccessorDecl>(afd) && !useLazyInference() &&
if (!(nullptr != afd && isa<AccessorDecl>(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 @@ protected:
}
// 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 @@ protected:
"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 @@ protected:
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<llvm::ArrayRef<LifetimeDependenceInfo>>
LifetimeDependenceInfo::getFromAST(
FunctionTypeRepr *funcRepr, AnyFunctionType *funcType,
ArrayRef<LifetimeTypeAttr *> 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<LifetimeDependenceInfo> 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<LifetimeDependenceInfo> checkSILTypeModifiers(
: nullptr,
targetIndex,
/*isImmortal*/ false,
/*isFromAnnotation*/ true,
addressableLifetimeParamIndices.any()
? IndexSubset::get(ctx, addressableLifetimeParamIndices)
: nullptr,
+8
View File
@@ -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:
+1
View File
@@ -2769,6 +2769,7 @@ void LifetimeDependenceInfoRequest::cacheResult(
}
auto *eed = cast<EnumElementDecl>(decl);
eed->LazySemanticInfo.NoLifetimeDependenceInfo = 1;
return;
}
decl->getASTContext().evaluator.cacheNonEmptyOutput(*this, std::move(result));
+17
View File
@@ -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)
+11 -6
View File
@@ -2104,7 +2104,8 @@ namespace {
}
SmallVector<LifetimeDependenceInfo, 1> lifetimeDependencies;
LifetimeDependenceInfo immortalLifetime(nullptr, nullptr, resultIndex,
/*isImmortal*/ true);
/*isImmortal*/ true,
/*isFromAnnotation*/ true);
lifetimeDependencies.push_back(immortalLifetime);
Impl.SwiftContext.evaluator.cacheOutput(
LifetimeDependenceInfoRequest{fd},
@@ -4230,7 +4231,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));
@@ -4299,7 +4300,8 @@ namespace {
SmallVector<LifetimeDependenceInfo, 1> 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(
@@ -4387,8 +4389,11 @@ namespace {
cast<clang::CXXMethodDecl>(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() ||
@@ -4403,7 +4408,7 @@ namespace {
scopedLifetimeParamIndicesForReturn)
: nullptr,
returnIdx,
/*isImmortal*/ false));
/*isImmortal*/ false, /*isFromAnnotation*/ true));
else if (auto *ctordecl = dyn_cast<clang::CXXConstructorDecl>(decl)) {
// Assume default constructed view types have no dependencies.
if (ctordecl->isDefaultConstructor() &&
+16
View File
@@ -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,
+13 -10
View File
@@ -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) {
+118 -43
View File
@@ -2468,7 +2468,7 @@ namespace {
CallerIsolatedTypeRepr *nonisolatedNonsendingAttr;
llvm::TinyPtrVector<CustomAttr*> customAttrs;
EnumMap<TypeAttrKind, TypeAttribute *> typeAttrs;
EnumMap<TypeAttrKind, llvm::TinyPtrVector<TypeAttribute *>> typeAttrs;
llvm::SmallBitVector claimedCustomAttrs;
FixedBitSet<NumTypeAttrKinds> claimedTypeAttrs;
@@ -2515,33 +2515,35 @@ namespace {
claimedCustomAttrs.set(it - customAttrs.begin());
}
TypeAttribute *getWithoutClaiming(TypeAttrKind attrKind) {
ArrayRef<TypeAttribute *> 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<TypeAttribute *> 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 <class AttrClass>
AttrClass *claim(TypeAttrSet &attrs) {
auto attr = attrs.claim(AttrClass::StaticKind);
return cast_or_null<AttrClass>(attr);
template <TypeAttrKind Kind>
auto claim(TypeAttrSet &attrs) {
auto attrVector = attrs.claim(Kind);
if constexpr (!TypeAttribute::allowMultipleAttributes(Kind)) {
return attrVector.empty() ? nullptr : attrVector.front();
} else {
return attrVector;
}
}
template <class AttrClass>
AttrClass *claim(TypeAttrSet *attrs) {
return (attrs ? claim<AttrClass>(*attrs) : nullptr);
auto claim(TypeAttrSet &attrs) {
ArrayRef<TypeAttribute *> attrVector = attrs.claim(AttrClass::StaticKind);
if constexpr (!TypeAttribute::allowMultipleAttributes(
AttrClass::StaticKind)) {
return attrVector.empty() ? nullptr
: cast_or_null<AttrClass>(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<AttrClass>(attr);
})) {
return ArrayRef<AttrClass *>(
reinterpret_cast<AttrClass *const *>(attrVector.data()),
attrVector.size());
} else {
return ArrayRef<AttrClass *>{};
}
}
}
template <class AttrClass>
AttrClass *getWithoutClaiming(TypeAttrSet &attrs) {
auto attr = attrs.getWithoutClaiming(AttrClass::StaticKind);
return cast_or_null<AttrClass>(attr);
auto claim(TypeAttrSet *attrs) {
return (attrs ? claim<AttrClass>(*attrs)
: decltype(claim<AttrClass>(*attrs)){});
}
template <class AttrClass>
std::enable_if_t<std::is_base_of_v<TypeAttribute, AttrClass>, AttrClass *>
getWithoutClaiming(TypeAttrSet *attrs) {
return (attrs ? getWithoutClaiming<AttrClass>(*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<AttrClass>(attrVector.front());
} else {
return attrVector;
}
}
template <class AttrClass>
std::enable_if_t<std::is_same_v<AttrClass, CallerIsolatedTypeRepr>,
CallerIsolatedTypeRepr *>
getWithoutClaiming(TypeAttrSet *attrs) {
auto getWithoutClaiming(TypeAttrSet *attrs) {
return (attrs ? getWithoutClaiming<AttrClass>(*attrs)
: decltype(getWithoutClaiming<AttrClass>(*attrs)){});
}
template <>
auto getWithoutClaiming<CallerIsolatedTypeRepr>(TypeAttrSet *attrs) {
return attrs ? attrs->getNonisolatedNonsendingAttr() : nullptr;
}
} // end anonymous namespace
@@ -3282,8 +3319,15 @@ void TypeAttrSet::accumulate(ArrayRef<TypeOrCustomAttr> 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<TAR_TypeTransformer>(attrs)) {
if (auto opaqueAttr = dyn_cast<OpaqueReturnTypeOfTypeAttr>(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<TAR_SILMetatype>(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<LifetimeTypeAttr>(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());
}
return fnTy;
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 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<TAR_SILCoroutine>(*attrs) : nullptr) {
assert(isa<YieldOnceTypeAttr>(coroAttr) ||
isa<YieldOnce2TypeAttr>(coroAttr) ||
isa<YieldManyTypeAttr>(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<TAR_SILCalleeConvention>(*attrs) : nullptr) {
assert(isa<CalleeOwnedTypeAttr>(calleeAttr) ||
isa<CalleeGuaranteedTypeAttr>(calleeAttr));
callee = (isa<CalleeOwnedTypeAttr>(calleeAttr)
@@ -5054,7 +5129,7 @@ bool TypeResolver::resolveSingleSILResult(
return false;
}
if (auto conventionAttr = attrs.claim(TAR_SILValueConvention)) {
if (auto conventionAttr = claim<TAR_SILValueConvention>(attrs)) {
switch (conventionAttr->getKind()) {
#define ERROR(ATTR, CONVENTION) \
case TypeAttrKind::ATTR: \
+5 -3
View File
@@ -9588,12 +9588,13 @@ ModuleFile::maybeReadLifetimeDependence() {
unsigned targetIndex;
unsigned paramIndicesLength;
bool isImmortal;
bool isFromAnnotation;
bool hasInheritLifetimeParamIndices;
bool hasScopeLifetimeParamIndices;
bool hasAddressableParamIndices;
ArrayRef<uint64_t> lifetimeDependenceData;
LifetimeDependenceLayout::readRecord(
scratch, targetIndex, paramIndicesLength, isImmortal,
scratch, targetIndex, paramIndicesLength, isImmortal, isFromAnnotation,
hasInheritLifetimeParamIndices, hasScopeLifetimeParamIndices,
hasAddressableParamIndices, lifetimeDependenceData);
@@ -9629,8 +9630,9 @@ ModuleFile::maybeReadLifetimeDependence() {
hasScopeLifetimeParamIndices
? IndexSubset::get(ctx, scopeLifetimeParamIndices)
: nullptr,
targetIndex, isImmortal,
targetIndex, isImmortal, isFromAnnotation,
hasAddressableParamIndices
? IndexSubset::get(ctx, addressableParamIndices)
: nullptr);
: nullptr,
nullptr);
}
+2 -1
View File
@@ -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
+1 -1
View File
@@ -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();
@@ -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
}
@@ -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
@@ -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 {
@@ -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) -> ()) {
}
@@ -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}}
}
@@ -237,13 +237,6 @@ func testIndirectNonForwardedResult<T>(arg1: GNE<T>, arg2: GNE<T>) -> GNE<T> {
forward(arg2) // expected-note {{this use causes the lifetime-dependent value to escape}}
}
func testIndirectClosureResult<T>(f: () -> GNE<T>) -> GNE<T> {
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
// =============================================================================
+5
View File
@@ -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
+185 -3
View File
@@ -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<Int>) -> () // 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<T> = @_lifetime(inValue) (_ inValue: T) -> T // expected-error{{invalid lifetime dependence on an Escapable result}}
typealias GetGenericEscapableType2<T> =
@_lifetime(outValue: borrow inValue) // expected-error{{invalid lifetime dependence on an Escapable target}}
(_ outValue: inout T, _ inValue: T) -> ()
typealias GetGenericNonEscapableType2<T: ~Escapable> =
@_lifetime(borrow inValue) (_ inValue: borrowing T) -> T // OK
typealias GetGenericCorrectType<T: ~Escapable> =
@_lifetime(outValue: borrow inValue) (_ outValue: inout T, _ inValue: borrowing T) -> () // OK
@_lifetime(outValue: copy inValue) // OK
func getGeneric<T : ~Escapable>(_ outValue: inout T, _ inValue: borrowing T) { // expected-note{{in call to function 'getGeneric'}}
outValue = inValue
}
@_lifetime(outValue: borrow inValue) // OK
func getGeneric2<T : ~Escapable>(_ 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<T: ~Escapable>(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<T: ~Escapable>: ~Escapable {
let ne: T
@_lifetime(copy ne)
init(ne: T) {
self.ne = ne
}
}
@_lifetime(borrow cne)
func copyCNE(cne: CNE<NE>) -> CNE<NE> {
return cne
}
public let UnboundGenericParamFunctionType : (CNE) -> CNE<NE> = copyCNE // expected-error{{lifetime dependence checking failed due to unknown parameter type}}
// expected-error@-1{{value of type 'CNE<NE>' does not conform to specified type 'Escapable'}}
public let UnboundGenericParamFunctionTypeAnnotated : @_lifetime(borrow cne) (_ cne: CNE) -> CNE<NE> = copyCNE // expected-error{{lifetime dependence checking failed due to unknown parameter type}}
// expected-error@-1{{value of type 'CNE<NE>' does not conform to specified type 'Escapable'}}
public let UnboundGenericResultFunctionType : (CNE<NE>) -> CNE = copyCNE // expected-error{{lifetime dependence checking failed due to unknown result type}}
// expected-error@-1{{value of type 'CNE<NE>' does not conform to specified type 'Escapable'}}
public let UnboundGenericResultFunctionTypeAnnotated : @_lifetime(borrow cne) (_ cne: CNE<NE>) -> CNE = copyCNE // expected-error{{lifetime dependence checking failed due to unknown result type}}
// expected-error@-1{{value of type 'CNE<NE>' does not conform to specified type 'Escapable'}}
public let TypeParameterParamFunctionType = copyCNE as (_) -> CNE<NE> // 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<NE> // 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<NE>) -> _ // 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<NE>) -> _ // 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<T>(f: () -> CNE<T>) -> CNE<T> {
// 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()
}