diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 7bb4cb39d71..bde208c344a 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -24,6 +24,7 @@ #include "swift/AST/DeclNameLoc.h" #include "swift/AST/Identifier.h" #include "swift/AST/KnownProtocols.h" +#include "swift/AST/LifetimeDependence.h" #include "swift/AST/MacroDeclaration.h" #include "swift/AST/Ownership.h" #include "swift/AST/PlatformKind.h" @@ -2627,6 +2628,27 @@ public: } }; +class LifetimeAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + + friend TrailingObjects; + + unsigned NumEntries = 0; + + explicit LifetimeAttr(SourceLoc atLoc, SourceRange baseRange, bool implicit, + ArrayRef entries); + +public: + static LifetimeAttr *create(ASTContext &context, SourceLoc atLoc, + SourceRange baseRange, bool implicit, + ArrayRef entries); + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DeclAttrKind::Lifetime; + } +}; + /// Predicate used to filter MatchingAttributeRange. template struct ToAttributeKind { ToAttributeKind() {} diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index 5afb3e32b6f..a27579eb79b 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -506,6 +506,10 @@ SIMPLE_DECL_ATTR(unsafe, Unsafe, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, 160) +DECL_ATTR(lifetime, Lifetime, + OnAccessor | OnConstructor | OnFunc | OnSubscript | LongAttribute | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + 161) + LAST_DECL_ATTR(Unsafe) #undef DECL_ATTR_ALIAS diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index dcbba5e2a52..f6c2577dac0 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -74,7 +74,9 @@ private: public: static LifetimeDependenceSpecifier getNamedLifetimeDependenceSpecifier( - SourceLoc loc, ParsedLifetimeDependenceKind kind, Identifier name) { + SourceLoc loc, Identifier name, + ParsedLifetimeDependenceKind kind = + ParsedLifetimeDependenceKind::Default) { return {loc, SpecifierKind::Named, kind, name}; } @@ -84,13 +86,15 @@ public: } static LifetimeDependenceSpecifier getOrderedLifetimeDependenceSpecifier( - SourceLoc loc, ParsedLifetimeDependenceKind kind, unsigned index) { + SourceLoc loc, unsigned index, + ParsedLifetimeDependenceKind kind = + ParsedLifetimeDependenceKind::Default) { return {loc, SpecifierKind::Ordered, kind, index}; } - static LifetimeDependenceSpecifier - getSelfLifetimeDependenceSpecifier(SourceLoc loc, - ParsedLifetimeDependenceKind kind) { + static LifetimeDependenceSpecifier getSelfLifetimeDependenceSpecifier( + SourceLoc loc, ParsedLifetimeDependenceKind kind = + ParsedLifetimeDependenceKind::Default) { return {loc, SpecifierKind::Self, kind, {}}; } diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index e58247bf4f3..24ff2aabc77 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1178,6 +1178,10 @@ public: MacroSyntax syntax, SourceLoc AtLoc, SourceLoc Loc ); + /// Parse the @lifetime attribute. + ParserResult parseLifetimeAttribute(SourceLoc AtLoc, + SourceLoc Loc); + /// Parse a specific attribute. ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, SourceLoc AtEndLoc, diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 32ea96ef4f3..d9a4e3fa944 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -2011,6 +2011,8 @@ StringRef DeclAttribute::getAttrName() const { } else { return "_allowFeatureSuppression"; } + case DeclAttrKind::Lifetime: + return "lifetime"; } llvm_unreachable("bad DeclAttrKind"); } @@ -3040,6 +3042,24 @@ AllowFeatureSuppressionAttr *AllowFeatureSuppressionAttr::create( AllowFeatureSuppressionAttr(atLoc, range, implicit, inverted, features); } +LifetimeAttr::LifetimeAttr(SourceLoc atLoc, SourceRange baseRange, + bool implicit, + ArrayRef entries) + : DeclAttribute(DeclAttrKind::Lifetime, atLoc, baseRange, implicit), + NumEntries(entries.size()) { + std::copy(entries.begin(), entries.end(), + getTrailingObjects()); +} + +LifetimeAttr * +LifetimeAttr::create(ASTContext &context, SourceLoc atLoc, + SourceRange baseRange, bool implicit, + ArrayRef entries) { + unsigned size = totalSizeToAlloc(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) { if (attr) attr->print(out); diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 4908c57a4e8..a52d5a5df3e 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -189,6 +189,8 @@ extension ASTGenVisitor { fatalError("unimplemented") case .allowFeatureSuppression: return self.generateAllowFeatureSuppressionAttr(attribute: node)?.asDeclAttribute + case .lifetime: + fatalError("unimplemented") // Simple attributes. case .alwaysEmitConformanceMetadata, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 9536afaa00e..b0e11a71a25 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2801,6 +2801,96 @@ static std::optional parseSingleAttrOptionImpl( return P.Context.getIdentifier(parsedName); } +ParserResult Parser::parseLifetimeAttribute(SourceLoc atLoc, + SourceLoc loc) { + ParserStatus status; + SmallVector 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::create(Context, atLoc, SourceRange(loc, rParenLoc), + /* implicit */ false, lifetimeEntries)); +} + /// Parses a (possibly optional) argument for an attribute containing a single, arbitrary identifier. /// /// \param P The parser object. @@ -4062,6 +4152,13 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, Attributes.add(attr); break; } + case DeclAttrKind::Lifetime: { + auto Attr = parseLifetimeAttribute(AtLoc, Loc); + Status |= Attr; + if (Attr.isNonNull()) + Attributes.add(Attr.get()); + break; + } } if (DuplicateAttribute) { @@ -5147,7 +5244,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers( specifierList.push_back( LifetimeDependenceSpecifier:: getNamedLifetimeDependenceSpecifier( - paramLoc, lifetimeDependenceKind, paramName)); + paramLoc, paramName, lifetimeDependenceKind)); } break; } @@ -5163,7 +5260,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers( specifierList.push_back( LifetimeDependenceSpecifier:: getOrderedLifetimeDependenceSpecifier( - paramLoc, lifetimeDependenceKind, paramNum)); + paramLoc, paramNum, lifetimeDependenceKind)); break; } case tok::kw_self: { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index e5642959926..ed75a16537e 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -366,6 +366,7 @@ public: void visitWeakLinkedAttr(WeakLinkedAttr *attr); void visitSILGenNameAttr(SILGenNameAttr *attr); void visitUnsafeAttr(UnsafeAttr *attr); + void visitLifetimeAttr(LifetimeAttr *attr); }; } // end anonymous namespace @@ -7674,6 +7675,8 @@ void AttributeChecker::visitUnsafeAttr(UnsafeAttr *attr) { diagnoseAndRemoveAttr(attr, diag::unsafe_attr_disabled); } +void AttributeChecker::visitLifetimeAttr(LifetimeAttr *attr) {} + namespace { class ClosureAttributeChecker diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 964701b1b4b..de19e8a97f9 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1701,6 +1701,7 @@ namespace { UNINTERESTING_ATTR(UnsafeNonEscapableResult) UNINTERESTING_ATTR(StaticExclusiveOnly) UNINTERESTING_ATTR(PreInverseGenerics) + UNINTERESTING_ATTR(Lifetime) #undef UNINTERESTING_ATTR void visitAvailableAttr(AvailableAttr *attr) { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 048868af096..5ea5f89b6e9 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 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. /// @@ -2490,6 +2490,15 @@ namespace decls_block { // trialed by introduced conformances >; + using LifetimeDeclAttrLayout = + BCRecordLayout, // targetIndex + BCFixed<1>, // isImmortal + BCFixed<1>, // hasInheritLifetimeParamIndices + BCFixed<1>, // hasScopeLifetimeParamIndices + BCArray> // concatenated param indices + >; + #undef SYNTAX_SUGAR_TYPE_LAYOUT #undef TYPE_LAYOUT #undef TYPE_LAYOUT_IMPL diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index a891f9264a0..c31135234c2 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3382,6 +3382,10 @@ class Serializer::DeclSerializer : public DeclVisitor { RawLayoutDeclAttrLayout::emitRecord( S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(), typeID, countID, rawSize, rawAlign, attr->shouldMoveAsLikeType()); + return; + } + case DeclAttrKind::Lifetime: { + return; } } } diff --git a/test/Parse/lifetime_attr.swift b/test/Parse/lifetime_attr.swift new file mode 100644 index 00000000000..69b79762559 --- /dev/null +++ b/test/Parse/lifetime_attr.swift @@ -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 +} +