Fix a compiler crash with '@'_lifetime(inout x), add diagnostic

This is a common mistake made more common be suggestions of existing diagnostic
that tell users not to use a 'copy' dependency.

Report a diagnostic error rather than crashing the compiler. Fix the diagnostic
output to make sense relative to the source location.

Fixes rdar://154136015 ([nonescapable] compiler assertion with @_lifetime(x: inout x))
This commit is contained in:
Andrew Trick
2025-06-25 12:26:51 -07:00
parent 05fa82b7a7
commit 080b68292d
4 changed files with 13 additions and 2 deletions

View File

@@ -2147,7 +2147,7 @@ ERROR(expected_lparen_after_lifetime_dependence, PointsToFirstBadToken,
ERROR(expected_identifier_or_index_or_self_after_lifetime_dependence, ERROR(expected_identifier_or_index_or_self_after_lifetime_dependence,
PointsToFirstBadToken, PointsToFirstBadToken,
"expected identifier, index or self in lifetime dependence specifier", "expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier",
()) ())
ERROR(expected_rparen_after_lifetime_dependence, PointsToFirstBadToken, ERROR(expected_rparen_after_lifetime_dependence, PointsToFirstBadToken,

View File

@@ -5014,6 +5014,7 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
SmallVector<LifetimeDescriptor> sources; SmallVector<LifetimeDescriptor> sources;
SourceLoc rParenLoc; SourceLoc rParenLoc;
bool foundParamId = false; bool foundParamId = false;
bool invalidSourceDescriptor = false;
status = parseList( status = parseList(
tok::r_paren, lParenLoc, rParenLoc, /*AllowSepAfterLast=*/false, tok::r_paren, lParenLoc, rParenLoc, /*AllowSepAfterLast=*/false,
diag::expected_rparen_after_lifetime_dependence, [&]() -> ParserStatus { diag::expected_rparen_after_lifetime_dependence, [&]() -> ParserStatus {
@@ -5030,6 +5031,7 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
auto sourceDescriptor = auto sourceDescriptor =
parseLifetimeDescriptor(*this, lifetimeDependenceKind); parseLifetimeDescriptor(*this, lifetimeDependenceKind);
if (!sourceDescriptor) { if (!sourceDescriptor) {
invalidSourceDescriptor = true;
listStatus.setIsParseError(); listStatus.setIsParseError();
return listStatus; return listStatus;
} }
@@ -5037,6 +5039,10 @@ ParserResult<LifetimeEntry> Parser::parseLifetimeEntry(SourceLoc loc) {
return listStatus; return listStatus;
}); });
if (invalidSourceDescriptor) {
status.setIsParseError();
return status;
}
if (!foundParamId) { if (!foundParamId) {
diagnose( diagnose(
Tok, Tok,

View File

@@ -24,7 +24,7 @@ func testMissingLParenError(_ ne: NE) -> NE { // expected-error{{cannot infer th
ne ne
} }
@_lifetime() // expected-error{{expected identifier, index or self in lifetime dependence specifier}} @_lifetime() // expected-error{{expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier}}
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)'}} 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)'}}
ne ne
} }

View File

@@ -588,3 +588,8 @@ struct NonEscapableMutableSelf: ~Escapable {
// //
func f_inout_no_infer(a: inout MutNE, b: NE) {} // expected-error{{a function with a ~Escapable 'inout' parameter requires '@_lifetime(a: ...)'}} func f_inout_no_infer(a: inout MutNE, b: NE) {} // expected-error{{a function with a ~Escapable 'inout' parameter requires '@_lifetime(a: ...)'}}
// expected-note @-1{{use '@_lifetime(a: copy a) to forward the inout dependency}} // expected-note @-1{{use '@_lifetime(a: copy a) to forward the inout dependency}}
// Invalid keyword for the dependence kind.
//
@_lifetime(a: inout a) // expected-error{{expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier}}
func f_inout_bad_keyword(a: inout MutableRawSpan) {}