Skip to content

Commit 27d0021

Browse files
committed
Add support for @Lifetime(&arg)
1 parent 9d5ca00 commit 27d0021

File tree

7 files changed

+84
-31
lines changed

7 files changed

+84
-31
lines changed

include/swift/AST/ASTBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,7 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedParsedLifetimeDependenceKind {
10811081
BridgedParsedLifetimeDependenceKindDefault,
10821082
BridgedParsedLifetimeDependenceKindScope,
10831083
BridgedParsedLifetimeDependenceKindInherit,
1084+
BridgedParsedLifetimeDependenceKindInout
10841085
};
10851086

10861087
class BridgedLifetimeDescriptor {

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8146,6 +8146,9 @@ ERROR(lifetime_dependence_invalid_inherit_escapable_type, none,
81468146
ERROR(lifetime_dependence_cannot_use_parsed_borrow_consuming, none,
81478147
"invalid use of borrow dependence with consuming ownership",
81488148
())
8149+
ERROR(lifetime_dependence_cannot_use_default_escapable_consuming, none,
8150+
"invalid lifetime dependence on Escapable value with %0 ownership",
8151+
(StringRef))
81498152
ERROR(lifetime_dependence_cannot_use_parsed_borrow_inout, none,
81508153
"invalid use of borrow dependence on the same inout parameter",
81518154
())

include/swift/AST/LifetimeDependence.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class SILResultInfo;
3939
enum class ParsedLifetimeDependenceKind : uint8_t {
4040
Default = 0,
4141
Scope,
42-
Inherit // Only used with deserialized decls
42+
Inherit, // Only used with deserialized decls
43+
Inout
4344
};
4445

4546
enum class LifetimeDependenceKind : uint8_t { Inherit = 0, Scope };

lib/AST/Bridging/DeclAttributeBridging.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ unbridged(BridgedParsedLifetimeDependenceKind kind) {
474474
return swift::ParsedLifetimeDependenceKind::Scope;
475475
case BridgedParsedLifetimeDependenceKindInherit:
476476
return swift::ParsedLifetimeDependenceKind::Inherit;
477+
case BridgedParsedLifetimeDependenceKindInout:
478+
return swift::ParsedLifetimeDependenceKind::Inout;
477479
}
478480
llvm_unreachable("unhandled enum value");
479481
}

lib/AST/LifetimeDependence.cpp

Lines changed: 68 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,20 @@ getLifetimeDependenceFor(ArrayRef<LifetimeDependenceInfo> lifetimeDependencies,
4848
return std::nullopt;
4949
}
5050

51+
StringRef
52+
getNameForParsedLifetimeDependenceKind(ParsedLifetimeDependenceKind kind) {
53+
switch (kind) {
54+
case ParsedLifetimeDependenceKind::Scope:
55+
return "borrow";
56+
case ParsedLifetimeDependenceKind::Inherit:
57+
return "copy";
58+
case ParsedLifetimeDependenceKind::Inout:
59+
return "inout";
60+
default:
61+
return "";
62+
}
63+
}
64+
5165
std::string LifetimeDependenceInfo::getString() const {
5266
std::string lifetimeDependenceString = "@lifetime(";
5367
auto addressable = getAddressableIndices();
@@ -420,26 +434,43 @@ class LifetimeDependenceChecker {
420434
}
421435
}
422436

423-
bool isCompatibleWithOwnership(LifetimeDependenceKind kind, Type type,
437+
bool isCompatibleWithOwnership(ParsedLifetimeDependenceKind kind, Type type,
424438
ValueOwnership ownership) const {
425-
if (kind == LifetimeDependenceKind::Inherit) {
439+
if (kind == ParsedLifetimeDependenceKind::Inherit) {
426440
return true;
427441
}
428442
// Lifetime dependence always propagates through temporary BitwiseCopyable
429443
// values, even if the dependence is scoped.
430444
if (isBitwiseCopyable(type, ctx)) {
431445
return true;
432446
}
433-
assert(kind == LifetimeDependenceKind::Scope);
434447
auto loweredOwnership = ownership != ValueOwnership::Default
435448
? ownership : getLoweredOwnership(afd);
436449

437-
if (loweredOwnership == ValueOwnership::InOut ||
438-
loweredOwnership == ValueOwnership::Shared) {
450+
if (kind == ParsedLifetimeDependenceKind::Scope) {
451+
return loweredOwnership == ValueOwnership::Shared;
452+
}
453+
assert(kind == ParsedLifetimeDependenceKind::Inout);
454+
return loweredOwnership == ValueOwnership::InOut;
455+
}
456+
457+
bool isCompatibleWithOwnership(LifetimeDependenceKind kind, Type type,
458+
ValueOwnership ownership) const {
459+
if (kind == LifetimeDependenceKind::Inherit) {
439460
return true;
440461
}
441-
assert(loweredOwnership == ValueOwnership::Owned);
442-
return false;
462+
// Lifetime dependence always propagates through temporary BitwiseCopyable
463+
// values, even if the dependence is scoped.
464+
if (isBitwiseCopyable(type, ctx)) {
465+
return true;
466+
}
467+
auto loweredOwnership = ownership != ValueOwnership::Default
468+
? ownership
469+
: getLoweredOwnership(afd);
470+
471+
assert(kind == LifetimeDependenceKind::Scope);
472+
return loweredOwnership == ValueOwnership::Shared ||
473+
loweredOwnership == ValueOwnership::InOut;
443474
}
444475

445476
struct TargetDeps {
@@ -515,44 +546,51 @@ class LifetimeDependenceChecker {
515546
std::optional<LifetimeDependenceKind>
516547
getDependenceKindFromDescriptor(LifetimeDescriptor descriptor,
517548
ParamDecl *decl) {
518-
auto loc = descriptor.getLoc();
519-
520-
auto ownership = decl->getValueOwnership();
549+
auto loc = decl->getLoc();
521550
auto type = decl->getTypeInContext();
551+
auto parsedLifetimeKind = descriptor.getParsedLifetimeDependenceKind();
552+
auto ownership = decl->getValueOwnership();
553+
auto loweredOwnership = ownership != ValueOwnership::Default
554+
? ownership
555+
: getLoweredOwnership(afd);
522556

523-
LifetimeDependenceKind kind;
524-
switch (descriptor.getParsedLifetimeDependenceKind()) {
525-
case ParsedLifetimeDependenceKind::Default:
557+
if (parsedLifetimeKind == ParsedLifetimeDependenceKind::Default) {
526558
if (type->isEscapable()) {
527-
kind = LifetimeDependenceKind::Scope;
559+
if (loweredOwnership == ValueOwnership::Shared ||
560+
loweredOwnership == ValueOwnership::InOut) {
561+
return LifetimeDependenceKind::Scope;
562+
} else {
563+
diagnose(
564+
loc,
565+
diag::lifetime_dependence_cannot_use_default_escapable_consuming,
566+
getOwnershipSpelling(loweredOwnership));
567+
return std::nullopt;
568+
}
528569
} else if (useLazyInference()) {
529-
kind = LifetimeDependenceKind::Inherit;
530-
} else {
531-
diagnose(loc, diag::lifetime_dependence_cannot_infer_kind,
532-
diagnosticQualifier(), descriptor.getString());
533-
return std::nullopt;
570+
return LifetimeDependenceKind::Inherit;
534571
}
535-
break;
536-
case ParsedLifetimeDependenceKind::Scope:
537-
kind = LifetimeDependenceKind::Scope;
538-
break;
539-
case ParsedLifetimeDependenceKind::Inherit:
540-
kind = LifetimeDependenceKind::Inherit;
541-
break;
572+
diagnose(loc, diag::lifetime_dependence_cannot_infer_kind,
573+
diagnosticQualifier(), descriptor.getString());
574+
return std::nullopt;
542575
}
543-
// @lifetime(borrow x) is invalid for consuming parameters.
544-
if (!isCompatibleWithOwnership(kind, type, ownership)) {
576+
577+
// @lifetime(borrow x) is valid only for borrowing parameters.
578+
// @lifetime(inout x) is valid only for inout parameters.
579+
if (!isCompatibleWithOwnership(parsedLifetimeKind, type, ownership)) {
545580
diagnose(loc,
546581
diag::lifetime_dependence_cannot_use_parsed_borrow_consuming);
547582
return std::nullopt;
548583
}
549584
// @lifetime(copy x) is only invalid for Escapable types.
550-
if (kind == LifetimeDependenceKind::Inherit && type->isEscapable()) {
585+
if (parsedLifetimeKind == ParsedLifetimeDependenceKind::Inherit &&
586+
type->isEscapable()) {
551587
diagnose(loc, diag::lifetime_dependence_invalid_inherit_escapable_type,
552588
descriptor.getString());
553589
return std::nullopt;
554590
}
555-
return kind;
591+
return parsedLifetimeKind == ParsedLifetimeDependenceKind::Inherit
592+
? LifetimeDependenceKind::Inherit
593+
: LifetimeDependenceKind::Scope;
556594
}
557595

558596
// Finds the ParamDecl* and its index from a LifetimeDescriptor

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,9 @@ extension ASTGenVisitor {
11041104
} else if let borrowExpr = node.as(BorrowExprSyntax.self) {
11051105
lifetimeDependenceKind = .scope
11061106
descriptorExpr = borrowExpr.expression
1107+
} else if let inoutExpr = node.as(InOutExprSyntax.self) {
1108+
lifetimeDependenceKind = .inout
1109+
descriptorExpr = inoutExpr.expression
11071110
} else {
11081111
lifetimeDependenceKind = .default
11091112
descriptorExpr = node

lib/Parse/ParseDecl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5019,6 +5019,11 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
50195019
tok::kw_self)) {
50205020
return ParsedLifetimeDependenceKind::Scope;
50215021
}
5022+
if (Tok.is(tok::amp_prefix) &&
5023+
peekToken().isAny(tok::identifier, tok::integer_literal,
5024+
tok::kw_self)) {
5025+
return ParsedLifetimeDependenceKind::Inout;
5026+
}
50225027
return std::nullopt;
50235028
};
50245029

0 commit comments

Comments
 (0)