Skip to content

Commit e25dcfa

Browse files
authored
Merge pull request #82505 from atrick/inout-syntax
Fix diagnostics for missing or invalid @_lifetime annotations on inout params
2 parents 475ecca + 87f2510 commit e25dcfa

File tree

7 files changed

+61
-11
lines changed

7 files changed

+61
-11
lines changed

include/swift/AST/DiagnosticsParse.def

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

21482148
ERROR(expected_identifier_or_index_or_self_after_lifetime_dependence,
21492149
PointsToFirstBadToken,
2150-
"expected identifier, index or self in lifetime dependence specifier",
2150+
"expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier",
21512151
())
21522152

21532153
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
@@ -8324,7 +8324,7 @@ ERROR(lifetime_dependence_cannot_use_default_escapable_consuming, none,
83248324
"invalid lifetime dependence on an Escapable value with %0 ownership",
83258325
(StringRef))
83268326
ERROR(lifetime_dependence_cannot_use_parsed_borrow_inout, none,
8327-
"invalid use of borrow dependence on the same inout parameter",
8327+
"invalid use of inout dependence on the same inout parameter",
83288328
())
83298329
ERROR(lifetime_dependence_duplicate_target, none,
83308330
"invalid duplicate target lifetime dependencies on function", ())
@@ -8340,18 +8340,18 @@ ERROR(lifetime_dependence_feature_required_return, none,
83408340
ERROR(lifetime_dependence_feature_required_mutating, none,
83418341
"%0 cannot have a ~Escapable 'self'", (StringRef))
83428342
ERROR(lifetime_dependence_feature_required_inout, none,
8343-
"%0 cannot have a ~Escapable 'inout' parameter %1",
8343+
"%0 cannot have a ~Escapable 'inout' parameter '%1' in addition to other ~Escapable parameters",
83448344
// this arg list must be compatible with
83458345
// lifetime_dependence_cannot_infer_inout
8346-
(StringRef, Identifier))
8346+
(StringRef, StringRef))
83478347

83488348
ERROR(lifetime_dependence_cannot_infer_return, none,
83498349
"%0 with a ~Escapable result requires '@_lifetime(...)'", (StringRef))
83508350
ERROR(lifetime_dependence_cannot_infer_mutating, none,
83518351
"%0 with a ~Escapable 'self' requires '@_lifetime(self: ...)'", (StringRef))
83528352
ERROR(lifetime_dependence_cannot_infer_inout, none,
83538353
"%0 with a ~Escapable 'inout' parameter requires '@_lifetime(%1: ...)'",
8354-
(StringRef, Identifier))
8354+
(StringRef, StringRef))
83558355

83568356
//------------------------------------------------------------------------------
83578357
// MARK: Lifetime Dependence Inference - refinements to the requirements above
@@ -8374,6 +8374,9 @@ ERROR(lifetime_dependence_cannot_infer_kind, none,
83748374
ERROR(lifetime_dependence_cannot_infer_scope_ownership, none,
83758375
"cannot borrow the lifetime of '%0', which has consuming ownership on %1",
83768376
(StringRef, StringRef))
8377+
NOTE(lifetime_dependence_cannot_infer_inout_suggest, none,
8378+
"use '@_lifetime(%0: copy %0) to forward the inout dependency",
8379+
(StringRef))
83778380

83788381
//------------------------------------------------------------------------------
83798382
// 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
@@ -5014,6 +5014,7 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
50145014
SmallVector<LifetimeDescriptor> sources;
50155015
SourceLoc rParenLoc;
50165016
bool foundParamId = false;
5017+
bool invalidSourceDescriptor = false;
50175018
status = parseList(
50185019
tok::r_paren, lParenLoc, rParenLoc, /*AllowSepAfterLast=*/false,
50195020
diag::expected_rparen_after_lifetime_dependence, [&]() -> ParserStatus {
@@ -5030,13 +5031,18 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
50305031
auto sourceDescriptor =
50315032
parseLifetimeDescriptor(*this, lifetimeDependenceKind);
50325033
if (!sourceDescriptor) {
5034+
invalidSourceDescriptor = true;
50335035
listStatus.setIsParseError();
50345036
return listStatus;
50355037
}
50365038
sources.push_back(*sourceDescriptor);
50375039
return listStatus;
50385040
});
50395041

5042+
if (invalidSourceDescriptor) {
5043+
status.setIsParseError();
5044+
return status;
5045+
}
50405046
if (!foundParamId) {
50415047
diagnose(
50425048
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: 25 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: ...)'}}
@@ -577,3 +580,24 @@ struct NonEscapableMutableSelf: ~Escapable {
577580
@_lifetime(&self)
578581
mutating func mutatingMethodOneParamBorrow(_: NE) {}
579582
}
583+
584+
// =============================================================================
585+
// Handle common mistakes with inout parameter annotations
586+
// =============================================================================
587+
588+
// Unable to infer an 'inout' dependency. Provide valid guidance.
589+
//
590+
func f_inout_no_infer(a: inout MutNE, b: NE) {}
591+
// expected-error @-1{{a function with a ~Escapable 'inout' parameter requires '@_lifetime(a: ...)'}}
592+
// expected-note @-2{{use '@_lifetime(a: copy a) to forward the inout dependency}}
593+
594+
// Invalid keyword for the dependence kind.
595+
//
596+
@_lifetime(a: inout a) // expected-error{{expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier}}
597+
func f_inout_bad_keyword(a: inout MutableRawSpan) {}
598+
599+
// Don't allow a useless borrow dependency on an inout param--it is misleading.
600+
//
601+
@_lifetime(a: &a) // expected-error{{invalid use of inout dependence on the same inout parameter}}
602+
// expected-note @-1{{use '@_lifetime(a: copy a) to forward the inout dependency}}
603+
func f_inout_useless(a: inout MutableRawSpan) {}

0 commit comments

Comments
 (0)