Skip to content

Commit 92fd571

Browse files
authored
Merge pull request #82511 from atrick/62-inout-syntax
[6.2] Fix diagnostics for missing or invalid @_lifetime annotations on inout params
2 parents 5373f52 + 0b0dbd9 commit 92fd571

File tree

7 files changed

+62
-11
lines changed

7 files changed

+62
-11
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2140,7 +2140,7 @@ ERROR(expected_lparen_after_lifetime_dependence, PointsToFirstBadToken,
21402140

21412141
ERROR(expected_identifier_or_index_or_self_after_lifetime_dependence,
21422142
PointsToFirstBadToken,
2143-
"expected identifier, index or self in lifetime dependence specifier",
2143+
"expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier",
21442144
())
21452145

21462146
ERROR(expected_rparen_after_lifetime_dependence, PointsToFirstBadToken,

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8244,7 +8244,7 @@ ERROR(lifetime_dependence_cannot_use_default_escapable_consuming, none,
82448244
"invalid lifetime dependence on an Escapable value with %0 ownership",
82458245
(StringRef))
82468246
ERROR(lifetime_dependence_cannot_use_parsed_borrow_inout, none,
8247-
"invalid use of borrow dependence on the same inout parameter",
8247+
"invalid use of inout dependence on the same inout parameter",
82488248
())
82498249
ERROR(lifetime_dependence_duplicate_target, none,
82508250
"invalid duplicate target lifetime dependencies on function", ())
@@ -8260,18 +8260,18 @@ ERROR(lifetime_dependence_feature_required_return, none,
82608260
ERROR(lifetime_dependence_feature_required_mutating, none,
82618261
"%0 cannot have a ~Escapable 'self'", (StringRef))
82628262
ERROR(lifetime_dependence_feature_required_inout, none,
8263-
"%0 cannot have a ~Escapable 'inout' parameter %1",
8263+
"%0 cannot have a ~Escapable 'inout' parameter '%1' in addition to other ~Escapable parameters",
82648264
// this arg list must be compatible with
82658265
// lifetime_dependence_cannot_infer_inout
8266-
(StringRef, Identifier))
8266+
(StringRef, StringRef))
82678267

82688268
ERROR(lifetime_dependence_cannot_infer_return, none,
82698269
"%0 with a ~Escapable result requires '@_lifetime(...)'", (StringRef))
82708270
ERROR(lifetime_dependence_cannot_infer_mutating, none,
82718271
"%0 with a ~Escapable 'self' requires '@_lifetime(self: ...)'", (StringRef))
82728272
ERROR(lifetime_dependence_cannot_infer_inout, none,
82738273
"%0 with a ~Escapable 'inout' parameter requires '@_lifetime(%1: ...)'",
8274-
(StringRef, Identifier))
8274+
(StringRef, StringRef))
82758275

82768276
//------------------------------------------------------------------------------
82778277
// MARK: Lifetime Dependence Inference - refinements to the requirements above
@@ -8297,6 +8297,9 @@ ERROR(lifetime_dependence_cannot_infer_scope_ownership, none,
82978297
ERROR(lifetime_dependence_cannot_infer_implicit_init, none,
82988298
"cannot infer implicit initialization lifetime. Add an initializer with "
82998299
"'@_lifetime(...)' for each parameter the result depends on", ())
8300+
NOTE(lifetime_dependence_cannot_infer_inout_suggest, none,
8301+
"use '@_lifetime(%0: copy %0) to forward the inout dependency",
8302+
(StringRef))
83008303

83018304
//------------------------------------------------------------------------------
83028305
// MARK: Lifetime Dependence Experimental Inference

lib/AST/LifetimeDependence.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,13 @@ class LifetimeDependenceChecker {
554554
})) {
555555
ctx.Diags.diagnose(param->getLoc(), diagID,
556556
{StringRef(diagnosticQualifier()),
557-
param->getName()});
557+
param->getName().str()});
558+
if (diagID == diag::lifetime_dependence_cannot_infer_inout.ID) {
559+
ctx.Diags.diagnose(
560+
param->getLoc(),
561+
diag::lifetime_dependence_cannot_infer_inout_suggest,
562+
param->getName().str());
563+
}
558564
}
559565
}
560566
}
@@ -904,16 +910,21 @@ class LifetimeDependenceChecker {
904910
if (!paramDeclAndIndex.has_value()) {
905911
return std::nullopt;
906912
}
907-
auto lifetimeKind =
908-
getDependenceKindFromDescriptor(source, paramDeclAndIndex->first);
913+
auto *param = paramDeclAndIndex->first;
914+
unsigned sourceIndex = paramDeclAndIndex->second;
915+
auto lifetimeKind = getDependenceKindFromDescriptor(source, param);
909916
if (!lifetimeKind.has_value()) {
910917
return std::nullopt;
911918
}
912-
unsigned sourceIndex = paramDeclAndIndex->second;
913919
if (lifetimeKind == LifetimeDependenceKind::Scope
920+
&& param->isInOut()
914921
&& sourceIndex == targetIndex) {
915922
diagnose(source.getLoc(),
916923
diag::lifetime_dependence_cannot_use_parsed_borrow_inout);
924+
ctx.Diags.diagnose(source.getLoc(),
925+
diag::lifetime_dependence_cannot_infer_inout_suggest,
926+
param->getName().str());
927+
917928
return std::nullopt;
918929
}
919930
bool hasError =

lib/Parse/ParseDecl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4989,6 +4989,7 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
49894989
SmallVector<LifetimeDescriptor> sources;
49904990
SourceLoc rParenLoc;
49914991
bool foundParamId = false;
4992+
bool invalidSourceDescriptor = false;
49924993
status = parseList(
49934994
tok::r_paren, lParenLoc, rParenLoc, /*AllowSepAfterLast=*/false,
49944995
diag::expected_rparen_after_lifetime_dependence, [&]() -> ParserStatus {
@@ -5005,13 +5006,18 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
50055006
auto sourceDescriptor =
50065007
parseLifetimeDescriptor(*this, lifetimeDependenceKind);
50075008
if (!sourceDescriptor) {
5009+
invalidSourceDescriptor = true;
50085010
listStatus.setIsParseError();
50095011
return listStatus;
50105012
}
50115013
sources.push_back(*sourceDescriptor);
50125014
return listStatus;
50135015
});
50145016

5017+
if (invalidSourceDescriptor) {
5018+
status.setIsParseError();
5019+
return status;
5020+
}
50155021
if (!foundParamId) {
50165022
diagnose(
50175023
Tok,

test/Parse/lifetime_attr.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func testMissingLParenError(_ ne: NE) -> NE { // expected-error{{cannot infer th
2424
ne
2525
}
2626

27-
@_lifetime() // expected-error{{expected identifier, index or self in lifetime dependence specifier}}
27+
@_lifetime() // expected-error{{expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier}}
2828
func testMissingDependence(_ ne: NE) -> NE { // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
2929
ne
3030
}

test/Sema/lifetime_attr_nofeature.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ struct NE : ~Escapable { // expected-error{{an implicit initializer cannot retur
88
func derive(_ ne: NE) -> NE { // expected-error{{a function cannot return a ~Escapable result}}
99
ne
1010
}
11+
12+
func f_inout_infer(a: inout MutableRawSpan) {}
13+
14+
func f_inout_no_infer(a: inout MutableRawSpan, b: RawSpan) {}
15+
// expected-error @-1{{a function cannot have a ~Escapable 'inout' parameter 'a' in addition to other ~Escapable parameters}}
16+

test/Sema/lifetime_depend_infer.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ struct NEImmortal: ~Escapable {
1515
init() {}
1616
}
1717

18+
struct MutNE: ~Copyable & ~Escapable {}
19+
1820
// =============================================================================
1921
// Handle non-Escapable results with 'self'
2022
// =============================================================================
@@ -563,7 +565,8 @@ struct NonEscapableMutableSelf: ~Escapable {
563565
@_lifetime(self: copy self) // OK
564566
mutating func mutatingMethodNoParamCopy() {}
565567
566-
@_lifetime(self: &self) // expected-error{{invalid use of borrow dependence on the same inout parameter}}
568+
@_lifetime(self: &self) // expected-error{{invalid use of inout dependence on the same inout parameter}}
569+
// expected-note @-1{{use '@_lifetime(self: copy self) to forward the inout dependency}}
567570
mutating func mutatingMethodNoParamBorrow() {}
568571
569572
mutating func mutatingMethodOneParam(_: NE) {} // expected-error{{a mutating method with a ~Escapable 'self' requires '@_lifetime(self: ...)'}}
@@ -613,3 +616,25 @@ struct NE_NE_C: ~Escapable { // expected-error{{cannot infer implicit initializa
613616
let ne: NE
614617
let c: C
615618
}
619+
620+
// =============================================================================
621+
// Handle common mistakes with inout parameter annotations
622+
// =============================================================================
623+
624+
// Unable to infer an 'inout' dependency. Provide valid guidance.
625+
//
626+
func f_inout_no_infer(a: inout MutNE, b: NE) {}
627+
// expected-error @-1{{a function with a ~Escapable 'inout' parameter requires '@_lifetime(a: ...)'}}
628+
// expected-note @-2{{use '@_lifetime(a: copy a) to forward the inout dependency}}
629+
630+
// Invalid keyword for the dependence kind.
631+
//
632+
@_lifetime(a: inout a) // expected-error{{expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier}}
633+
func f_inout_bad_keyword(a: inout MutableRawSpan) {}
634+
635+
// Don't allow a useless borrow dependency on an inout param--it is misleading.
636+
//
637+
@_lifetime(a: &a) // expected-error{{invalid use of inout dependence on the same inout parameter}}
638+
// expected-note @-1{{use '@_lifetime(a: copy a) to forward the inout dependency}}
639+
func f_inout_useless(a: inout MutableRawSpan) {}
640+

0 commit comments

Comments
 (0)