Add support for parsing @lifetime attribute to specify lifetime dependencies on declarations

This commit is contained in:
Meghana Gupta
2024-09-01 00:36:37 -07:00
parent 854298ab3b
commit e61d87c01c
12 changed files with 200 additions and 8 deletions

View File

@@ -24,6 +24,7 @@
#include "swift/AST/DeclNameLoc.h" #include "swift/AST/DeclNameLoc.h"
#include "swift/AST/Identifier.h" #include "swift/AST/Identifier.h"
#include "swift/AST/KnownProtocols.h" #include "swift/AST/KnownProtocols.h"
#include "swift/AST/LifetimeDependence.h"
#include "swift/AST/MacroDeclaration.h" #include "swift/AST/MacroDeclaration.h"
#include "swift/AST/Ownership.h" #include "swift/AST/Ownership.h"
#include "swift/AST/PlatformKind.h" #include "swift/AST/PlatformKind.h"
@@ -2627,6 +2628,27 @@ public:
} }
}; };
class LifetimeAttr final
: public DeclAttribute,
private llvm::TrailingObjects<LifetimeAttr, LifetimeDependenceSpecifier> {
friend TrailingObjects;
unsigned NumEntries = 0;
explicit LifetimeAttr(SourceLoc atLoc, SourceRange baseRange, bool implicit,
ArrayRef<LifetimeDependenceSpecifier> entries);
public:
static LifetimeAttr *create(ASTContext &context, SourceLoc atLoc,
SourceRange baseRange, bool implicit,
ArrayRef<LifetimeDependenceSpecifier> entries);
static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DeclAttrKind::Lifetime;
}
};
/// Predicate used to filter MatchingAttributeRange. /// Predicate used to filter MatchingAttributeRange.
template <typename ATTR, bool AllowInvalid> struct ToAttributeKind { template <typename ATTR, bool AllowInvalid> struct ToAttributeKind {
ToAttributeKind() {} ToAttributeKind() {}

View File

@@ -506,6 +506,10 @@ SIMPLE_DECL_ATTR(unsafe, Unsafe,
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
160) 160)
DECL_ATTR(lifetime, Lifetime,
OnAccessor | OnConstructor | OnFunc | OnSubscript | LongAttribute | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
161)
LAST_DECL_ATTR(Unsafe) LAST_DECL_ATTR(Unsafe)
#undef DECL_ATTR_ALIAS #undef DECL_ATTR_ALIAS

View File

@@ -74,7 +74,9 @@ private:
public: public:
static LifetimeDependenceSpecifier getNamedLifetimeDependenceSpecifier( static LifetimeDependenceSpecifier getNamedLifetimeDependenceSpecifier(
SourceLoc loc, ParsedLifetimeDependenceKind kind, Identifier name) { SourceLoc loc, Identifier name,
ParsedLifetimeDependenceKind kind =
ParsedLifetimeDependenceKind::Default) {
return {loc, SpecifierKind::Named, kind, name}; return {loc, SpecifierKind::Named, kind, name};
} }
@@ -84,13 +86,15 @@ public:
} }
static LifetimeDependenceSpecifier getOrderedLifetimeDependenceSpecifier( static LifetimeDependenceSpecifier getOrderedLifetimeDependenceSpecifier(
SourceLoc loc, ParsedLifetimeDependenceKind kind, unsigned index) { SourceLoc loc, unsigned index,
ParsedLifetimeDependenceKind kind =
ParsedLifetimeDependenceKind::Default) {
return {loc, SpecifierKind::Ordered, kind, index}; return {loc, SpecifierKind::Ordered, kind, index};
} }
static LifetimeDependenceSpecifier static LifetimeDependenceSpecifier getSelfLifetimeDependenceSpecifier(
getSelfLifetimeDependenceSpecifier(SourceLoc loc, SourceLoc loc, ParsedLifetimeDependenceKind kind =
ParsedLifetimeDependenceKind kind) { ParsedLifetimeDependenceKind::Default) {
return {loc, SpecifierKind::Self, kind, {}}; return {loc, SpecifierKind::Self, kind, {}};
} }

View File

@@ -1178,6 +1178,10 @@ public:
MacroSyntax syntax, SourceLoc AtLoc, SourceLoc Loc MacroSyntax syntax, SourceLoc AtLoc, SourceLoc Loc
); );
/// Parse the @lifetime attribute.
ParserResult<LifetimeAttr> parseLifetimeAttribute(SourceLoc AtLoc,
SourceLoc Loc);
/// Parse a specific attribute. /// Parse a specific attribute.
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
SourceLoc AtEndLoc, SourceLoc AtEndLoc,

View File

@@ -2011,6 +2011,8 @@ StringRef DeclAttribute::getAttrName() const {
} else { } else {
return "_allowFeatureSuppression"; return "_allowFeatureSuppression";
} }
case DeclAttrKind::Lifetime:
return "lifetime";
} }
llvm_unreachable("bad DeclAttrKind"); llvm_unreachable("bad DeclAttrKind");
} }
@@ -3040,6 +3042,24 @@ AllowFeatureSuppressionAttr *AllowFeatureSuppressionAttr::create(
AllowFeatureSuppressionAttr(atLoc, range, implicit, inverted, features); AllowFeatureSuppressionAttr(atLoc, range, implicit, inverted, features);
} }
LifetimeAttr::LifetimeAttr(SourceLoc atLoc, SourceRange baseRange,
bool implicit,
ArrayRef<LifetimeDependenceSpecifier> entries)
: DeclAttribute(DeclAttrKind::Lifetime, atLoc, baseRange, implicit),
NumEntries(entries.size()) {
std::copy(entries.begin(), entries.end(),
getTrailingObjects<LifetimeDependenceSpecifier>());
}
LifetimeAttr *
LifetimeAttr::create(ASTContext &context, SourceLoc atLoc,
SourceRange baseRange, bool implicit,
ArrayRef<LifetimeDependenceSpecifier> entries) {
unsigned size = totalSizeToAlloc<LifetimeDependenceSpecifier>(entries.size());
void *mem = context.Allocate(size, alignof(LifetimeDependenceSpecifier));
return new (mem) LifetimeAttr(atLoc, baseRange, implicit, entries);
}
void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) { void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
if (attr) if (attr)
attr->print(out); attr->print(out);

View File

@@ -189,6 +189,8 @@ extension ASTGenVisitor {
fatalError("unimplemented") fatalError("unimplemented")
case .allowFeatureSuppression: case .allowFeatureSuppression:
return self.generateAllowFeatureSuppressionAttr(attribute: node)?.asDeclAttribute return self.generateAllowFeatureSuppressionAttr(attribute: node)?.asDeclAttribute
case .lifetime:
fatalError("unimplemented")
// Simple attributes. // Simple attributes.
case .alwaysEmitConformanceMetadata, case .alwaysEmitConformanceMetadata,

View File

@@ -2801,6 +2801,96 @@ static std::optional<Identifier> parseSingleAttrOptionImpl(
return P.Context.getIdentifier(parsedName); return P.Context.getIdentifier(parsedName);
} }
ParserResult<LifetimeAttr> Parser::parseLifetimeAttribute(SourceLoc atLoc,
SourceLoc loc) {
ParserStatus status;
SmallVector<LifetimeDependenceSpecifier> lifetimeEntries;
if (!Context.LangOpts.hasFeature(Feature::NonescapableTypes)) {
diagnose(loc, diag::requires_experimental_feature, "lifetime attribute",
false, getFeatureName(Feature::NonescapableTypes));
status.setIsParseError();
return status;
}
if (!Tok.isFollowingLParen()) {
diagnose(loc, diag::expected_lparen_after_lifetime_dependence);
status.setIsParseError();
return status;
}
// consume the l_paren
auto lParenLoc = consumeToken();
SourceLoc rParenLoc;
bool foundParamId = false;
status = parseList(
tok::r_paren, lParenLoc, rParenLoc, /*AllowSepAfterLast*/ false,
diag::expected_rparen_after_lifetime_dependence, [&]() -> ParserStatus {
ParserStatus listStatus;
foundParamId = true;
switch (Tok.getKind()) {
case tok::identifier: {
Identifier paramName;
auto paramLoc =
consumeIdentifier(paramName, /*diagnoseDollarPrefix=*/false);
if (paramName.is("immortal")) {
lifetimeEntries.push_back(
LifetimeDependenceSpecifier::
getImmortalLifetimeDependenceSpecifier(paramLoc));
} else {
lifetimeEntries.push_back(
LifetimeDependenceSpecifier::
getNamedLifetimeDependenceSpecifier(paramLoc, paramName));
}
break;
}
case tok::integer_literal: {
SourceLoc paramLoc;
unsigned paramNum;
if (parseUnsignedInteger(
paramNum, paramLoc,
diag::expected_param_index_lifetime_dependence)) {
listStatus.setIsParseError();
return listStatus;
}
lifetimeEntries.push_back(
LifetimeDependenceSpecifier::
getOrderedLifetimeDependenceSpecifier(paramLoc, paramNum));
break;
}
case tok::kw_self: {
auto paramLoc = consumeToken(tok::kw_self);
lifetimeEntries.push_back(
LifetimeDependenceSpecifier::getSelfLifetimeDependenceSpecifier(
paramLoc));
break;
}
default:
diagnose(
Tok,
diag::
expected_identifier_or_index_or_self_after_lifetime_dependence);
listStatus.setIsParseError();
return listStatus;
}
return listStatus;
});
if (!foundParamId) {
diagnose(
Tok,
diag::expected_identifier_or_index_or_self_after_lifetime_dependence);
status.setIsParseError();
return status;
}
assert(!lifetimeEntries.empty());
SourceRange range(loc, rParenLoc);
return ParserResult<LifetimeAttr>(
LifetimeAttr::create(Context, atLoc, SourceRange(loc, rParenLoc),
/* implicit */ false, lifetimeEntries));
}
/// Parses a (possibly optional) argument for an attribute containing a single, arbitrary identifier. /// Parses a (possibly optional) argument for an attribute containing a single, arbitrary identifier.
/// ///
/// \param P The parser object. /// \param P The parser object.
@@ -4062,6 +4152,13 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
Attributes.add(attr); Attributes.add(attr);
break; break;
} }
case DeclAttrKind::Lifetime: {
auto Attr = parseLifetimeAttribute(AtLoc, Loc);
Status |= Attr;
if (Attr.isNonNull())
Attributes.add(Attr.get());
break;
}
} }
if (DuplicateAttribute) { if (DuplicateAttribute) {
@@ -5147,7 +5244,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
specifierList.push_back( specifierList.push_back(
LifetimeDependenceSpecifier:: LifetimeDependenceSpecifier::
getNamedLifetimeDependenceSpecifier( getNamedLifetimeDependenceSpecifier(
paramLoc, lifetimeDependenceKind, paramName)); paramLoc, paramName, lifetimeDependenceKind));
} }
break; break;
} }
@@ -5163,7 +5260,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
specifierList.push_back( specifierList.push_back(
LifetimeDependenceSpecifier:: LifetimeDependenceSpecifier::
getOrderedLifetimeDependenceSpecifier( getOrderedLifetimeDependenceSpecifier(
paramLoc, lifetimeDependenceKind, paramNum)); paramLoc, paramNum, lifetimeDependenceKind));
break; break;
} }
case tok::kw_self: { case tok::kw_self: {

View File

@@ -366,6 +366,7 @@ public:
void visitWeakLinkedAttr(WeakLinkedAttr *attr); void visitWeakLinkedAttr(WeakLinkedAttr *attr);
void visitSILGenNameAttr(SILGenNameAttr *attr); void visitSILGenNameAttr(SILGenNameAttr *attr);
void visitUnsafeAttr(UnsafeAttr *attr); void visitUnsafeAttr(UnsafeAttr *attr);
void visitLifetimeAttr(LifetimeAttr *attr);
}; };
} // end anonymous namespace } // end anonymous namespace
@@ -7674,6 +7675,8 @@ void AttributeChecker::visitUnsafeAttr(UnsafeAttr *attr) {
diagnoseAndRemoveAttr(attr, diag::unsafe_attr_disabled); diagnoseAndRemoveAttr(attr, diag::unsafe_attr_disabled);
} }
void AttributeChecker::visitLifetimeAttr(LifetimeAttr *attr) {}
namespace { namespace {
class ClosureAttributeChecker class ClosureAttributeChecker

View File

@@ -1701,6 +1701,7 @@ namespace {
UNINTERESTING_ATTR(UnsafeNonEscapableResult) UNINTERESTING_ATTR(UnsafeNonEscapableResult)
UNINTERESTING_ATTR(StaticExclusiveOnly) UNINTERESTING_ATTR(StaticExclusiveOnly)
UNINTERESTING_ATTR(PreInverseGenerics) UNINTERESTING_ATTR(PreInverseGenerics)
UNINTERESTING_ATTR(Lifetime)
#undef UNINTERESTING_ATTR #undef UNINTERESTING_ATTR
void visitAvailableAttr(AvailableAttr *attr) { void visitAvailableAttr(AvailableAttr *attr) {

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; /// 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. /// 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. /// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 889; // public-module-name const uint16_t SWIFTMODULE_VERSION_MINOR = 890; // @lifetime attribute
/// A standard hash seed used for all string hashes in a serialized module. /// A standard hash seed used for all string hashes in a serialized module.
/// ///
@@ -2490,6 +2490,15 @@ namespace decls_block {
// trialed by introduced conformances // trialed by introduced conformances
>; >;
using LifetimeDeclAttrLayout =
BCRecordLayout<Lifetime_DECL_ATTR,
BCVBR<4>, // targetIndex
BCFixed<1>, // isImmortal
BCFixed<1>, // hasInheritLifetimeParamIndices
BCFixed<1>, // hasScopeLifetimeParamIndices
BCArray<BCFixed<1>> // concatenated param indices
>;
#undef SYNTAX_SUGAR_TYPE_LAYOUT #undef SYNTAX_SUGAR_TYPE_LAYOUT
#undef TYPE_LAYOUT #undef TYPE_LAYOUT
#undef TYPE_LAYOUT_IMPL #undef TYPE_LAYOUT_IMPL

View File

@@ -3382,6 +3382,10 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
RawLayoutDeclAttrLayout::emitRecord( RawLayoutDeclAttrLayout::emitRecord(
S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(),
typeID, countID, rawSize, rawAlign, attr->shouldMoveAsLikeType()); typeID, countID, rawSize, rawAlign, attr->shouldMoveAsLikeType());
return;
}
case DeclAttrKind::Lifetime: {
return;
} }
} }
} }

View File

@@ -0,0 +1,22 @@
// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-experimental-feature NonescapableTypes
// REQUIRES: asserts
struct NE : ~Escapable {
}
@lifetime(ne)
func derive(_ ne: NE) -> NE {
ne
}
@lifetime // expected-error{{expected '(' after lifetime dependence specifier}}
func testMissingLParenError(_ ne: NE) -> NE {
ne
}
@lifetime() // expected-error{{expected identifier, index or self in lifetime dependence specifier}}
func testMissingDependence(_ ne: NE) -> NE {
ne
}