Skip to content

Commit 5a86600

Browse files
authored
Merge pull request #82472 from atrick/explicit-init
Disable surprising lifetime inference of implicit initializers
2 parents 9c83ae4 + 7abe222 commit 5a86600

File tree

4 files changed

+74
-6
lines changed

4 files changed

+74
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8377,6 +8377,9 @@ ERROR(lifetime_dependence_cannot_infer_scope_ownership, none,
83778377
NOTE(lifetime_dependence_cannot_infer_inout_suggest, none,
83788378
"use '@_lifetime(%0: copy %0) to forward the inout dependency",
83798379
(StringRef))
8380+
ERROR(lifetime_dependence_cannot_infer_implicit_init, none,
8381+
"cannot infer implicit initialization lifetime. Add an initializer with "
8382+
"'@_lifetime(...)' for each parameter the result depends on", ())
83808383

83818384
//------------------------------------------------------------------------------
83828385
// MARK: Lifetime Dependence Experimental Inference

lib/AST/LifetimeDependence.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,9 +1075,13 @@ class LifetimeDependenceChecker {
10751075
return LifetimeDependenceKind::Scope;
10761076
}
10771077

1078-
// Infer implicit initialization. The dependence kind can be inferred, similar
1079-
// to an implicit setter, because the implementation is simply an assignment
1080-
// to stored property.
1078+
// Infer implicit initialization. A non-Escapable initializer parameter can
1079+
// always be inferred, similar to an implicit setter, because the
1080+
// implementation is simply an assignment to stored property. Escapable
1081+
// parameters are ambiguous: they may either be borrowed or
1082+
// non-dependent. non-Escapable types often have incidental integer fields
1083+
// that are unrelated to lifetime. Avoid inferring any dependency on Escapable
1084+
// parameters unless it is the (unambiguously borrowed) sole parameter.
10811085
void inferImplicitInit() {
10821086
auto *afd = cast<AbstractFunctionDecl>(decl);
10831087
if (afd->getParameters()->size() == 0) {
@@ -1097,15 +1101,28 @@ class LifetimeDependenceChecker {
10971101
if (paramTypeInContext->hasError()) {
10981102
return;
10991103
}
1104+
if (!paramTypeInContext->isEscapable()) {
1105+
// An implicitly initialized non-Escapable value always copies its
1106+
// dependency.
1107+
targetDeps = std::move(targetDeps).add(paramIndex,
1108+
LifetimeDependenceKind::Inherit);
1109+
continue;
1110+
}
1111+
if (afd->getParameters()->size() > 1 && !useLazyInference()) {
1112+
diagnose(param->getLoc(),
1113+
diag::lifetime_dependence_cannot_infer_implicit_init);
1114+
return;
1115+
}
1116+
// A single Escapable parameter must be borrowed.
11001117
auto kind = inferLifetimeDependenceKind(paramTypeInContext,
11011118
param->getValueOwnership());
11021119
if (!kind) {
11031120
diagnose(returnLoc,
11041121
diag::lifetime_dependence_cannot_infer_scope_ownership,
11051122
param->getParameterName().str(), diagnosticQualifier());
1106-
return;
11071123
}
1108-
targetDeps = std::move(targetDeps).add(paramIndex, *kind);
1124+
targetDeps = std::move(targetDeps).add(paramIndex,
1125+
LifetimeDependenceKind::Scope);
11091126
}
11101127
pushDeps(std::move(targetDeps));
11111128
}

test/Sema/lifetime_attr.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func immortalConflict(_ immortal: Int) -> NE { // expected-error{{conflict betwe
7575
}
7676

7777
do {
78-
struct Test: ~Escapable {
78+
struct Test: ~Escapable { // expected-error{{cannot infer implicit initialization lifetime. Add an initializer with '@_lifetime(...)' for each parameter the result depends on}}
7979
var v1: Int
8080
var v2: NE
8181
}

test/Sema/lifetime_depend_infer.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,54 @@ struct NonEscapableMutableSelf: ~Escapable {
581581
mutating func mutatingMethodOneParamBorrow(_: NE) {}
582582
}
583583
584+
// =============================================================================
585+
// Initializers
586+
// =============================================================================
587+
588+
// Motivation: Non-escapable struct definitions often have inicidental integer fields that are unrelated to lifetime.
589+
// Without an explicit initializer, the compiler would infer these fields to be borrowed by the implicit intializer.
590+
// This inevitabely results in lifetime diagnostic errors elsewhere in the code that can't be tracked down at the use
591+
// site:
592+
//
593+
// let span = CountedSpan(span: span, i: 3) // ERROR: span depends on the lifetime of this value
594+
//
595+
struct CountedSpan: ~Escapable { // expected-error{{cannot infer implicit initialization lifetime. Add an initializer with '@_lifetime(...)' for each parameter the result depends on}}
596+
let span: Span<Int>
597+
let i: Int
598+
}
599+
600+
struct NE_Int: ~Escapable {
601+
let i: Int
602+
}
603+
604+
struct NE_C: ~Escapable { // expected-error{{cannot borrow the lifetime of 'c', which has consuming ownership on an implicit initializer}}
605+
let c: C
606+
}
607+
608+
struct NE_C_Int: ~Escapable { // expected-error{{cannot infer implicit initialization lifetime. Add an initializer with '@_lifetime(...)' for each parameter the result depends on}}
609+
let c: C
610+
let i: Int
611+
}
612+
613+
struct NE_Int_Int: ~Escapable { // expected-error{{cannot infer implicit initialization lifetime. Add an initializer with '@_lifetime(...)' for each parameter the result depends on}}
614+
let i: Int
615+
let j: Int
616+
}
617+
618+
struct NE_NE: ~Escapable {
619+
let ne: NE
620+
}
621+
622+
struct NE_NE_Int: ~Escapable { // expected-error{{cannot infer implicit initialization lifetime. Add an initializer with '@_lifetime(...)' for each parameter the result depends on}}
623+
let ne: NE
624+
let i: Int
625+
}
626+
627+
struct NE_NE_C: ~Escapable { // expected-error{{cannot infer implicit initialization lifetime. Add an initializer with '@_lifetime(...)' for each parameter the result depends on}}
628+
let ne: NE
629+
let c: C
630+
}
631+
584632
// =============================================================================
585633
// Handle common mistakes with inout parameter annotations
586634
// =============================================================================

0 commit comments

Comments
 (0)