Commit Graph

15 Commits

Author SHA1 Message Date
Andrew Trick
0b0dbd9ee0 Diagnostic note for invalid @_lifetime annotations on inout params
Users commonly try to write a lifetime dependency on an 'inout' parameters as:

    @_lifetime(a: &a)
    func f_inout_useless(a: inout MutableRawSpan) {}

This is useless. Guide them toward what they really wanted:

    @_lifetime(a: copy a)

Fixes rdar://151618856 (@lifetime(..) gives inconsistent error messages)

(cherry picked from commit 87f2510a27)
2025-06-26 12:58:40 -07:00
Andrew Trick
102e5ecee1 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))

(cherry picked from commit 080b68292d)
2025-06-26 12:58:40 -07:00
Andrew Trick
fcbcc8c1e4 Fix misleading Lifetime diagnostics for inout parameters
Correctly diagnose this as:
"invalid use of inout dependence on the same inout parameter

    @_lifetime(a: &a)
    func f_inout_useless(a: inout MutableRawSpan) {}

Correctly diagnose this as:
"lifetime-dependent parameter must be 'inout'":

    @_lifetime(a: borrow a)
    func f_inout_useless(a: borrowing MutableRawSpan) {}

(cherry picked from commit 05fa82b7a7)
2025-06-26 12:58:40 -07:00
Andrew Trick
e409752e00 Lifetime diagnostics: clarify @_lifetime usage for inout parameters
This comes up often when passing a MutableSpan as an 'inout' argument.  The
vague diagnostic was causing developers to attempt incorrect @_lifetime
annotations. Be clear about why the annotation is needed and which annotation
should be used.

(cherry picked from commit df0b81c88d)
2025-06-26 12:58:39 -07:00
Andrew Trick
78e8b5af50 Disable surprising lifetime inference of implicit initializers
Non-escapable struct definitions often have inicidental integer fields that are
unrelated to lifetime. Without an explicit initializer, the compiler would infer
these fields to be borrowed by the implicit intializer.

    struct CountedSpan: ~Escapable {
      let span: Span<Int>
      let i: Int

      /* infer: @lifetime(copy span, borrow i) init(...) */
    }

This was done because
- we always want to infer lifetimes of synthesized code if possible
- inferring a borrow dependence is always conservative

But this was the wrong decision because it inevitabely results in lifetime
diagnostic errors elsewhere in the code that can't be tracked down at the use
site:

    let span = CountedSpan(span: span, i: 3) // ERROR: span depends on the lifetime of this value

Instead, force the author of the data type to specify whether the type actually
depends on trivial fields or not. Such as:

    struct CountedSpan: ~Escapable {
      let span: Span<Int>
      let i: Int

      @lifetime(copy span) init(...) { ... }
    }

This fix enables stricter diagnostics, so we need it in 6.2.

Fixes rdar://152130977 ([nonescapable] confusing diagnostic message when a
synthesized initializer generates dependence on an Int parameter)

(cherry picked from commit 8789a686fed869e3cd7bc4e748a443e71df464e1)
2025-06-24 18:09:09 -07:00
Andrew Trick
b15d308053 LifetimeDependence type check: infer trivial _read accessor
This fixes a small oversight in the type checker's LifetimeDependence
inference. Allow inference on _read accessors even when 'self' is a trivial
type. This is needed because the compiler synthesizes a _read accessor even when
the user defines a getter (this is probably a mistake, but it's easire to just
fix inference at this point). There is no workaround because it defining both a
getter and '_read' is illegal!

    extension UnsafeMutableRawBufferPointer {
      var mutableBytes: MutableRawSpan {
        @_lifetime(borrow self)
        get {
          unsafe MutableRawSpan(_unsafeBytes: self)
        }
      }
    }

Fixes rdar://153346478 (Can't compile the
UnsafeMutableRawBufferPointer.mutableBytes property)

(cherry picked from commit 125a0862a9)
2025-06-16 10:50:57 -07:00
Andrew Trick
4f337f0b9c Update tests after disallowing @_lifetime(borrow) for inout
(cherry picked from commit f6c7524353)
2025-06-12 16:31:56 -07:00
Meghana Gupta
8d3f38623d [NFC] Update tests and diagnostics 2025-06-11 13:15:22 -07:00
Meghana Gupta
ea35d41f2a [6.2] Fix diagnostic messages for some cases of invalid lifetime dependencies
The diagnostic messages were misleading and suggested an invalid alternative.

rdar://151810577
2025-06-02 16:04:13 -07:00
Andrew Trick
1bd73dafb6 Allow passing MutableSpan 'inout' without an experimental feature.
This adds a new lifetime inference rule, loosening the requirement for @lifetime
annotations even when the experimental LifetimeDependence mode is
enabled. Additionally, it enables this new inference rule even when the
experimental mode is disabled. All other inference rules continue to require the
experimental feature. The rule is:

If a function or method has a single inout non-Escapable parameter other than
'self' and has no other non-Escapable parameters including 'self', then infer a
single @lifetime(copy) dependency on the inout parameter from its own incoming
value.

This supports the common case in which the user of a non-Escapable type,
such as MutableSpan, wants to modify the span's contents without modifying
the span value itself. It should be possible to use MutableSpan this way
without requiring any knowledge of lifetime annotations. The tradeoff is
that it makes authoring non-Escapable types less safe. For example, a
MutableSpan method could update the underlying unsafe pointer and forget to
declare a dependence on the incoming pointer.

Disallowing other non-Escapable parameters rules out the easy mistake of
programmers attempting to trivially reassign the inout parameter. There's
is no way to rule out the possibility that they derive another
non-Escapable value from an Escapable parameteter. So users can still write
the following:

    func reassign(s: inout MutableSpan<Int>, a: [Int]) {
      s = a.mutableSpan
    }

The 'reassign' declaration will type check, but it's implementation will
diagnose a lifetime error on 's'.

Fixes rdar://150557314 ([nonescapable] Declaration of inout MutableSpan
parameter requires LifetimeDependence experimental feature)

(cherry picked from commit dbcba013434aeaa042e9f5cc9ec0829d762b74e0)
2025-05-21 00:14:31 -07:00
Andrew Trick
baa02e0529 LifetimeDependence: fix a type checker crash on implicit init
When the type checker diagnoses an error on an implicit initializer,
return immediately before handling its parameter to avoid an assert.

(cherry picked from commit e9ac803e94)
2025-04-27 22:38:34 -07:00
Meghana Gupta
c73187aa64 [NFC] Update tests 2025-04-09 10:19:04 -07:00
Andrew Trick
462b327ee3 Cleanup RUN lines for lifetime inference type checking tests 2025-03-19 22:22:21 -07:00
Andrew Trick
64a48d08e1 Update tests for strict @lifetime type checking 2025-03-19 11:59:04 -07:00
Andrew Trick
3a383cd9f9 Create a test suite for LifetimeDependence type checking & inferrence
Test the matrix of possibilities that need to be handled by the type
checker. This tracks the implementation of LifetimeDependenceChecker.
2025-03-19 11:59:04 -07:00