mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
We want 'inout sending' parameters to have the semantics that not only are they
disconnected on return from the function but additionally they are guaranteed to
be in their own disconnected region on return. This implies that we must emit
errors when an 'inout sending' parameter or any element that is in the same
region as the current value within an 'inout sending' parameter is
returned. This commit contains a new diagnostic for RegionIsolation that adds
specific logic for detecting and emitting errors in these situations.
To implement this, we introduce 3 new diagnostics with each individual
diagnostic being slightly different to reflect the various ways that this error
can come up in source:
* Returning 'inout sending' directly:
```swift
func returnInOutSendingDirectly(_ x: inout sending NonSendableKlass) -> NonSendableKlass {
return x // expected-warning {{cannot return 'inout sending' parameter 'x' from global function 'returnInOutSendingDirectly'}}
// expected-note @-1 {{returning 'x' risks concurrent access since caller assumes that 'x' and the result of global function 'returnInOutSendingDirectly' can be safely sent to different isolation domains}}
}
```
* Returning a value in the same region as an 'inout sending' parameter. E.x.:
```swift
func returnInOutSendingRegionVar(_ x: inout sending NonSendableKlass) -> NonSendableKlass {
var y = x
y = x
return y // expected-warning {{cannot return 'y' from global function 'returnInOutSendingRegionVar'}}
// expected-note @-1 {{returning 'y' risks concurrent access to 'inout sending' parameter 'x' since the caller assumes that 'x' and the result of global function 'returnInOutSendingRegionVar' can be safely sent to different isolation domains}}
}
```
* Returning the result of a function or computed property that is in the same
region as the 'inout parameter'.
```swift
func returnInOutSendingViaHelper(_ x: inout sending NonSendableKlass) -> NonSendableKlass {
let y = x
return useNonSendableKlassAndReturn(y) // expected-warning {{cannot return result of global function 'useNonSendableKlassAndReturn' from global function 'returnInOutSendingViaHelper'}}
// expected-note @-1 {{returning result of global function 'useNonSendableKlassAndReturn' risks concurrent access to 'inout sending' parameter 'x' since the caller assumes that 'x' and the result of global function 'returnInOutSendingViaHelper' can be safely sent to different isolation domains}}
}
```
Additionally, I had to introduce a specific variant for each of these
diagnostics for cases where due to us being in a method, we are actually in our
caller causing the 'inout sending' parameter to be in the same region as an
actor isolated value:
* Returning 'inout sending' directly:
```swift
extension MyActor {
func returnInOutSendingDirectly(_ x: inout sending NonSendableKlass) -> NonSendableKlass {
return x // expected-warning {{cannot return 'inout sending' parameter 'x' from instance method 'returnInOutSendingDirectly'}}
// expected-note @-1 {{returning 'x' risks concurrent access since caller assumes that 'x' is not actor-isolated and the result of instance method 'returnInOutSendingDirectly' is 'self'-isolated}}
}
}
```
* Returning a value in the same region as an 'inout sending' parameter. E.x.:
```swift
extension MyActor {
func returnInOutSendingRegionLet(_ x: inout sending NonSendableKlass) -> NonSendableKlass {
let y = x
return y // expected-warning {{cannot return 'y' from instance method 'returnInOutSendingRegionLet'}}
// expected-note @-1 {{returning 'y' risks concurrent access to 'inout sending' parameter 'x' since the caller assumes that 'x' is not actor-isolated and the result of instance method 'returnInOutSendingRegionLet' is 'self'-isolated}}
}
}
```
* Returning the result of a function or computed property that is in the same region as the 'inout parameter'.
```swift
extension MyActor {
func returnInOutSendingViaHelper(_ x: inout sending NonSendableKlass) -> NonSendableKlass {
let y = x
return useNonSendableKlassAndReturn(y) // expected-warning {{cannot return result of global function 'useNonSendableKlassAndReturn' from instance method 'returnInOutSendingViaHelper'; this is an error in the Swift 6 language mode}}
// expected-note @-1 {{returning result of global function 'useNonSendableKlassAndReturn' risks concurrent access to 'inout sending' parameter 'x' since the caller assumes that 'x' is not actor-isolated and the result of instance method 'returnInOutSendingViaHelper' is 'self'-isolated}}
}
}
```
To implement this, I used two different approaches depending on whether or not
the returned value was generic or not.
* Concrete
In the case where we had a concrete value, I was able to in simple cases emit
diagnostics based off of the values returned by the return inst. In cases where
we phied together results due to multiple results in the same function, we
determine which of the incoming phied values caused the error by grabbing the
exit partition information of each of the incoming value predecessors and seeing
if an InOutSendingAtFunctionExit would emit an error.
* Generic
In the case of generic code, it is a little more interesting since the result is
a value stored in an our parameter instead of being a value directly returned by
a return inst. To work around this, I use PrunedLiveness to determine the last
values stored into the out parameter in the function to avoid having to do a
full dataflow. Then I take the exit blocks where we assign each of those values
and run the same check as we do in the direct phi case to emit the appropriate
error.
rdar://152454571
70 lines
2.7 KiB
C++
70 lines
2.7 KiB
C++
//===--- PartitionOpError.def ----------------------------*- C++ -*--------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// This file contains macros for creating PartitionOpErrors for use with
|
|
/// SendNonSendable. This just makes it easier to add these errors without
|
|
/// needing to write so much boielr plate.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef PARTITION_OP_ERROR
|
|
#define PARTITION_OP_ERROR(Name)
|
|
#endif
|
|
|
|
/// An error created if we discover a value was locally used after it
|
|
/// was already sent.
|
|
///
|
|
/// The arguments in the type are:
|
|
///
|
|
/// 1. The PartitionOp that required the element to be alive.
|
|
///
|
|
/// 2. The element in the PartitionOp that was asked to be alive.
|
|
///
|
|
/// 3. The operand of the instruction that originally sent the region. Can be
|
|
/// used to get the immediate value sent or the sending instruction.
|
|
PARTITION_OP_ERROR(LocalUseAfterSend)
|
|
|
|
/// This is called if we detect a never sendable element that was actually sent.
|
|
///
|
|
/// E.x.: passing a main actor exposed value to a sending parameter.
|
|
PARTITION_OP_ERROR(SentNeverSendable)
|
|
|
|
/// This is emitted when a never sendable value is passed into a sending
|
|
/// result. The sending result will be viewed in our caller as disconnected and
|
|
/// able to be sent.
|
|
PARTITION_OP_ERROR(AssignNeverSendableIntoSendingResult)
|
|
|
|
/// This is emitted when an inout sending parameter has been sent in a function
|
|
/// body but has not been reinitialized at the end of the function.
|
|
PARTITION_OP_ERROR(InOutSendingNotInitializedAtExit)
|
|
|
|
/// This is emitted when an inout sending parameter has been assigned a
|
|
/// non-disconnected value (e.x.: a value exposed to main actor isolated code)
|
|
/// at end of function without being reinitialized with something disconnected.
|
|
PARTITION_OP_ERROR(InOutSendingNotDisconnectedAtExit)
|
|
|
|
/// This is emitted when a concrete inout sending parameter is returned.
|
|
PARTITION_OP_ERROR(InOutSendingReturned)
|
|
|
|
/// Used to signify an "unknown code pattern" has occured while performing
|
|
/// dataflow.
|
|
///
|
|
/// DISCUSSION: Our dataflow cannot emit errors itself so this is a callback
|
|
/// to our user so that we can emit that error as we process.
|
|
PARTITION_OP_ERROR(UnknownCodePattern)
|
|
|
|
/// Used to signify that an isolation crossing function is returning a
|
|
/// non-Sendable value.
|
|
PARTITION_OP_ERROR(NonSendableIsolationCrossingResult)
|
|
|
|
#undef PARTITION_OP_ERROR
|