Skip to content

LifetimeDependence: implement strict type checking #80064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Mar 20, 2025

Conversation

atrick
Copy link
Contributor

@atrick atrick commented Mar 17, 2025

LifetimeDependence: implement strict type checking

Rework the type checker to support completely checking lifetime dependence requirements. Don't let anything through without the feature being enabled and complete annotation or inference.

First, prevent lifetime dependencies from sneaking into source that does not enable LifetimeDependence. This is essential for controlling the scope of the feature.

Fixing this is disruptive because, ever since ~Escapable was introduced we have been declaring empty non-Escapable types without enabling LifetimeDependence. Such as:

  struct Implicit_Init_Nonescapable : ~Escapable {}

Fixes: rdar://145979187 ([nonescapable] diagnose implicit non-Escapable initializers as an error unless LifetimeDependence is enabled)

Various forms of unsupported 'inout' dependencies are now also caught by the type checker.

Second, disable lifetime dependency inferrence except in unambiguous cases and some implicitly generated cases.

Fixes: rdar://131176898 ([nonescapable] missing diagnostic for incorrectly inferred inherited dependence)

This is important to avoid source compatibility problems as inference rules change. They will change as the proposal goes through review.

This fixes various latent missing dependency bugs.

Disable experimental lifetime dependence inference. Unambiguous lifetime dependency candidates will still be inferred by default, without any frontend options. Ambiguous candidates will, however, no longer be inferred unless -Xfrontend -enable_experimental_lifetime_dependence_inference is enabled.

This all has to be done without breaking existing .swiftinterface files. So backward compatibility logic is maintained.

Examples of inference rules that are no longer enabled by default:

  1. do not infer a dependency on non-Escapable 'self' for methods with more than zero parameters:
    extension NE: ~Escapable {
      /*@lifetime(self)*/ // ERROR: 'self' not inferred
      func method<Arg>(arg: Arg) -> NE { ... }
    }
  1. Never infer a 'copy' dependency kind for explicit functions
    extension NE: ~Escapable {
      @lifetime(self) // ERROR: 'copy' not inferred
      func method() -> NE { ... }

      @lifetime(self) // ERROR: 'copy' not inferred
      var property : NE { /*@lifetime(self: newValue)*/ set { ... } }
    }

@atrick
Copy link
Contributor Author

atrick commented Mar 18, 2025

@swift-ci test

@atrick
Copy link
Contributor Author

atrick commented Mar 18, 2025

@swift-ci Please smoke test compiler performance

@atrick
Copy link
Contributor Author

atrick commented Mar 18, 2025

@swift-ci benchmark

@atrick
Copy link
Contributor Author

atrick commented Mar 18, 2025

@swift-ci Please Build Toolchain macOS Platform

@atrick
Copy link
Contributor Author

atrick commented Mar 18, 2025

@swift-ci Please Test Source Compatibility Debug

@atrick
Copy link
Contributor Author

atrick commented Mar 18, 2025

The only macOS test failure was the known spurious test failing the same spurious way: Interpreter/preconcurrency_conformances.swift

@atrick
Copy link
Contributor Author

atrick commented Mar 18, 2025

@swift-ci smoke test macOS

@atrick atrick force-pushed the lifetime-inference branch from 80d88b5 to 2ca38a6 Compare March 19, 2025 16:14
atrick added 19 commits March 19, 2025 11:57
The will be supported in a future commit that adds mark_dependence_addr.
Rework the type checker's diagnostics to support completely checking lifetime
dependence requirements. Don't let anything through without the feature being
enabled and complete annotation or inference.
Rework the type checker to support completely checking lifetime dependence
requirements. Don't let anything through without the feature being enabled and
complete annotation or inference.

First, prevent lifetime dependencies from sneaking into source that does not
enable LifetimeDependence. This is essential for controlling the scope of the
feature.

Fixing this is disruptive because, ever since `~Escapable` was introduced we
have been declaring empty non-Escapable types without enabling
LifetimeDependence. Such as:

      struct Implicit_Init_Nonescapable : ~Escapable {}

Fixes: rdar://145979187 ([nonescapable] diagnose implicit non-Escapable
initializers as an error unless LifetimeDependence is enabled)

Various forms of unsupported 'inout' dependencies are now also caught by the
type checker.

Second, disable lifetime dependency inferrence except in unambiguous cases and
some implicitly generated cases.

Fixes: rdar://131176898 ([nonescapable] missing diagnostic for incorrectly inferred inherited dependence)

This is important to avoid source compatibility problems as inference rules
change. They will change as the proposal goes through review.

This fixes various latent missing dependency bugs.

Disable experimental lifetime dependence inference. Unambiguous lifetime
dependency candidates will still be inferred by default, without any frontend
options. Ambiguous candidates will, however, no longer be inferred unless
-Xfrontend -enable_experimental_lifetime_dependence_inference is enabled.

This all has to be done without breaking existing .swiftinterface files. So
backward compatibility logic is maintained.

Examples of inference rules that are no longer enabled by default:

1. do not infer a dependency on non-Escapable 'self' for methods with more than
zero parameters:

    extension NE: ~Escapable {
      /*@Lifetime(self)*/ // ERROR: 'self' not inferred
      func method<Arg>(arg: Arg) -> NE { ... }
    }

2. Never infer a 'copy' dependency kind for explicit functions

    extension NE: ~Escapable {
      @Lifetime(self) // ERROR: 'copy' not inferred
      func method() -> NE { ... }

      @Lifetime(self) // ERROR: 'copy' not inferred
      var property : NE { /*@Lifetime(self: newValue)*/ set { ... } }
    }
Test each basic case that requires LifetimeDependences,
first without enabling the LifetimeDependence feature,
then without specifying the kind of dependence.
Test the matrix of possibilities that need to be handled by the type
checker. This tracks the implementation of LifetimeDependenceChecker.
These empty initializers have @Lifetime(immortal).
These tests can't be fully enabled until this is fixed:

rdar://147194789 ([nonescapable] 'mutating get' causes a type checking error for
non-existent _read accessor)
Test for rdar://131176898 ([nonescapable] missing diagnostic for
incorrectly inferred inherited dependence)
Do not rely on the @_unsafeNonescapableResult attribute. That attribute is only
for temporarily working around bugs! And it only affects lifetime diagnostics within
the function. It has no affect on the caller's diagnostics, so it won't solve
this problem:

func macroGeneratedThunk() -> CxxSpan<Int> {
  return _unsafeRemoveLifetime(Span...)
}

We cannot simply add @_unsafeRemoveLifetime to the thunk, because SwiftSyntax
does not natively support the attribute. We don't want to add SwiftSyntax
support because this attribute will never be supported syntax!

Instead, use `_overrideLifetime` copying the `Void` type to remove a dependency:

func macroGeneratedThunk() -> CxxSpan<Int> {
  return _cxxOverrideLifetime(Span..., copying: ())
}
_unsafeNonEscapableResult no longer dsiables diagnostics at the call site. We
need to use a proper lifetime annotation for that.
@atrick atrick force-pushed the lifetime-inference branch from 590dab4 to d41c4d4 Compare March 19, 2025 18:59
@atrick
Copy link
Contributor Author

atrick commented Mar 19, 2025

@swift-ci test

@atrick atrick enabled auto-merge March 19, 2025 18:59
@atrick
Copy link
Contributor Author

atrick commented Mar 19, 2025

@swift-ci smoke test

@atrick atrick merged commit 75ba7a8 into swiftlang:main Mar 20, 2025
4 of 5 checks passed
@atrick atrick deleted the lifetime-inference branch March 20, 2025 03:14
atrick added a commit to atrick/swift that referenced this pull request Mar 20, 2025
This was fix was accidentally not include in the previous commit,
which breaks older .swiftinterface files without it:

commit 75ba7a8
Merge: befc15e d41c4d4
Author: Andrew Trick <[email protected]>
Date:   Wed Mar 19 18:22:35 2025

    Merge pull request swiftlang#80064 from atrick/lifetime-inference

    LifetimeDependence: implement strict type checking
atrick added a commit to atrick/swift that referenced this pull request Mar 20, 2025
This was fix was accidentally not include in the previous commit,
which breaks older .swiftinterface files without it:

commit 75ba7a8
Merge: befc15e d41c4d4
Author: Andrew Trick <[email protected]>
Date:   Wed Mar 19 18:22:35 2025

    Merge pull request swiftlang#80064 from atrick/lifetime-inference

    LifetimeDependence: implement strict type checking
glessard added a commit to glessard/swift that referenced this pull request Mar 20, 2025
Changes to lifetime annotations implemented in swiftlang#80064
glessard added a commit to glessard/swift that referenced this pull request Mar 22, 2025
Changes to lifetime annotations implemented in swiftlang#80064
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant