Commit Graph

990 Commits

Author SHA1 Message Date
Erik Eckstein
bc244d1122 SIL: move the FunctionBuilderTy template argument from TypeSubstCloner to remapParentFunction 2025-09-04 08:15:44 +02:00
Michael Gottesman
391ad21583 [rbi] Make all PartitionOpErrors noncopyable types.
I am going to be adding support to passes for emitting IsolationHistory behind a
flag. As part of this, we need to store the state of the partition that created
the error when the error is emitted. A partition stores heap memory so it makes
sense to make these types noncopyable types so we just move the heap memory
rather than copying it all over the place.
2025-08-29 15:18:55 -07:00
Michael Gottesman
6d20c74c3e Merge pull request #83903 from gottesmm/rdar152454571
[rbi] Teach RegionIsolation how to properly error when 'inout sending' params are returned.
2025-08-29 14:56:58 -07:00
Erik Eckstein
87e7250750 Optimizer: fix an ownership violation when duplicating loops
If a guaranteed value is used in a dead-end exit block and the enclosing value is _not_ destroyed in this block, we end up missing the enclosing value as phi-argument after duplicating the loop.
TODO: once we have complete lifetimes we can remove this check again.

rdar://159125605
2025-08-28 18:40:54 +02:00
Michael Gottesman
8745ab00de [rbi] Teach RegionIsolation how to properly error when 'inout sending' params are returned.
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
2025-08-25 14:57:44 -07:00
nate-chandler
d1637af144 Merge pull request #83789 from nate-chandler/nfc/20250818/1
[NFC] Renamed three SIL utilities.
2025-08-20 13:25:04 -07:00
Nate Chandler
5da38cb04b [NFC] OSSACanonicalizeGuaranteed: Renamed. 2025-08-18 09:45:27 -07:00
Nate Chandler
8f1d6616af [NFC] OSSACanonicalizeOwned: Renamed. 2025-08-18 09:45:21 -07:00
Nate Chandler
aa85694237 [NFC] OSSACompleteLifetime: Renamed. 2025-08-18 09:45:19 -07:00
Nate Chandler
1fe65b0e3d [NFC] Deleted two unused functions. 2025-08-14 16:15:43 -07:00
Michael Gottesman
18b1fbce7a [sns] Refactor duplicate code into a helper. NFC.
The specific code path is the code used to emit errors if we assign or merge
into a sending result. I just left the code in tree in the short term to prevent
cherry-picking issues and since there wasn't a strong reason to do it at the
time. Now that we have more freedom, lets clean up this code!
2025-08-13 17:02:49 -07:00
Anthony Latsis
bbd84429a0 Merge pull request #83400 from swiftlang/jepa-main
Address `llvm::PointerUnion::{is,get}` deprecations
2025-07-30 17:19:58 +01:00
Anthony Latsis
fec049e5e4 Address llvm::PointerUnion::{is,get} deprecations
These were deprecated in
https://github.com/llvm/llvm-project/pull/122623.
2025-07-29 18:37:48 +01:00
Andrew Trick
ab8091964b [NFC] add SinkAddressProjections::dump() 2025-07-29 00:10:59 -07:00
Michael Gottesman
73e34f020b [rbi] Teach SendNonSendable how to more aggressively suppress sending errors around obfuscated Sendable functions
Specifically the type checker to work around interface types not having
isolation introduces casts into the AST that enrich the AST with isolation
information. Part of that information is Sendable. This means that we can
sometimes lose due to conversions that a function is actually Sendable. To work
around this, we today suppress those errors when they are emitted (post 6.2, we
should just change their classification as being Sendable... but I don't want to
make that change now).

This change just makes the pattern matching for these conversions handle more
cases so that transfernonsendable_closureliterals_isolationinference.swift now
passes.
2025-07-10 11:15:00 -07:00
Doug Gregor
1e87656e40 [Region analysis] Simplify logic for dynamic casts by making them less special
Extend and use SILIsolationInfo::getConformanceIsolation() for the dynamic
cast instructions.
2025-07-08 22:36:37 -07:00
Doug Gregor
0aa54a6f11 [Region isolation] Re-simplify handling of init_existential* instructions
Centralize the logic for figuring out the conformances for the various
init_existential* instructions in a SILIsolationInfo static method, and
always go through that when handling "assign" semantics. This way, we
can use CONSTANT_TRANSLATION again for these instructions, or a simpler
decision process between Assign and LookThrough.

The actually undoes a small change made earlier when we stopped looking
through `init_existential_value` instructions. Now we do when there are
no isolated conformances.
2025-07-08 22:36:37 -07:00
Doug Gregor
2f3ef58f16 [Region isolation] Factor SILIsolationInfo creation into static methods
Better match the style of SILIsolationInfo by moving the code for determining
SILIsolationInfo from conformances or dynamic casts to existentials into
static `getXYZ` methods on SILIsolationInfo.

Other than adding an assertion regarding disconnected regions, no
intended functionality change.
2025-07-08 22:36:37 -07:00
Doug Gregor
02c34bb830 [SE-0470] Track the potential introduction of isolated conformances in regions
When we introduce isolation due to a (potential) isolated conformance,
keep track of the protocol to which the conformance could be
introduced. Use this information for two reasons:

1. Downgrade the error to a warning in Swift < 7, because we are newly
diagnosing these
2. Add a note indicating where the isolated conformance could be introduced.
2025-07-08 11:18:30 -07:00
Michael Gottesman
677b897ff6 Merge pull request #82555 from gottesmm/pr-b4de2f5d58d852cd3e9e606101e1b0ff42ce6092
[nonisolated-nonsending] Make the AST not consider nonisolated(nonsending) to be an actor isolation crossing point.
2025-07-03 05:22:57 -07:00
Michael Gottesman
010fa39f31 [rbi] Use interned StringRefs for diagnostics instead of SmallString<64>.
This makes the code easier to write and also prevents any lifetime issues from a
diagnostic outliving the SmallString due to diagnostic transactions.
2025-07-02 16:50:24 -07:00
Michael Gottesman
14634b6847 [rbi] Begin tracking if a function argument is from a nonisolated(nonsending) parameter and adjust printing as appropriate.
Specifically in terms of printing, if NonisolatedNonsendingByDefault is enabled,
we print out things as nonisolated/task-isolated and @concurrent/@concurrent
task-isolated. If said feature is disabled, we print out things as
nonisolated(nonsending)/nonisolated(nonsending) task-isolated and
nonisolated/task-isolated. This ensures in the default case, diagnostics do not
change and we always print out things to match the expected meaning of
nonisolated depending on the mode.

I also updated the tests as appropriate/added some more tests/added to the
SendNonSendable education notes information about this.
2025-07-02 13:03:13 -07:00
Michael Gottesman
4433ab8d81 [rbi] Thread through a SILFunction into print routines so we can access LangOpts.Features so we can change how we print based off of NonisolatedNonsendingByDefault.
We do not actually use this information yet though... This is just to ease
review.
2025-07-02 12:13:52 -07:00
Michael Gottesman
4ce4fc4f95 [rbi] Wrap use ActorIsolation::printForDiagnostics with our own SILIsolationInfo::printActorIsolationForDiagnostics.
I am doing this so that I can change how we emit the diagnostics just for
SendNonSendable depending on if NonisolatedNonsendingByDefault is enabled
without touching the rest of the compiler.

This does not actually change any of the actual output though.
2025-07-02 12:13:50 -07:00
Erik Eckstein
606f7693d4 MandatoryPerformanceOptimizations: don't de-virtualize a generic class method call to specialized method
This results in wrong argument/return calling conventions.
First, the method call must be specialized. Only then the call can be de-virtualized.
Usually, it's done in this order anyway, because the `class_method` instruction is located before the `apply`.
But when inlining functions, the order (in the worklist) can be the other way round.

Fixes a compiler crash.
rdar://154631438
2025-07-01 09:44:47 +02:00
Jamie
1f3f830fc7 [SILOptimizer]: slow OSSA lifetime canonicalization mitigation
OSSA lifetime canonicalization can take a very long time in certain
cases in which there are large basic blocks. to mitigate this, add logic
to skip walking the liveness boundary for extending liveness to dead
ends when there aren't any dead ends in the function.

Updates `DeadEndBlocks` with a new `isEmpty` method and cache to
determine if there are any dead-end blocks in a given function.
2025-06-18 17:52:14 -05:00
Anthony
c9b17383c8 Grammatical corrections for compound modifiers 2025-04-24 09:21:32 +02:00
nate-chandler
f424639fee Merge pull request #80990 from nate-chandler/rdar147586673
[OSSACanOwned] Don't dead-end extend if consumed.
2025-04-22 17:56:24 -07:00
Nate Chandler
40bcc74b47 [OSSACanOwned] Don't dead-end extend if consumed.
When the utility is used by the ConsumeOperatorCopyableValuesChecker,
the checker guarantees that the lifetime can end at the consumes, that
there are no uses after those consumes.  In that circumstance, the
utility maintains liveness to those consumes and as far as possible
without introducing a copy everywhere else.

The lack of complete lifetimes has forced the utility to extend liveness
of values to dead-ends.  That extension, however, is in tension with the
use that the checker is putting the utility to.  If there is a dead-end
after a consume, liveness must not be maintained to that dead-end.

rdar://147586673
2025-04-21 18:09:30 -07:00
Nate Chandler
6ff0c07fe6 [NFC] OSSACanOwned: Extracted predicate. 2025-04-21 18:09:30 -07:00
Nate Chandler
02618f562d [NFC] OSSACanOwned: Renamed field. 2025-04-21 18:09:30 -07:00
Michael Gottesman
205038b1b0 [rbi] Simplify some logic that got confused so that passing an actor isolated value to a callee that is isolated ot the same actor is not considered a send.
The logic here got confused over time. This simplifies the logic and ensures
that we do not send a value if it is in the same isolation domain as the callee.

The one interesting side effect of this is that in a few tests, due to the logic
being confused, we were emitting use-after-send errors for global actor isolated
values that were passed to a function that was global actor isolated to the same
actor and then used later locally. The error was sending 'X'-isolated a to
'X'-isolated function causes race against nonisolated local uses. In truth, this
error is misleading and the only error that we should be emitting in such a case
is the error about moving an isolated value into a non-isolated context (which
we already emit).

rdar://132932382
2025-04-18 09:58:10 -07:00
Michael Gottesman
0ece31e4f6 [sil-isolation-info] When determining isolation of a function arg, use its VarDecl.
Otherwise, we can be inconsistent with isolations returned by other parts of the
code. Previously we were just treating it always as self + nom decl, which is
clearly wrong if a type is not self (e.x.: if it is an isolated parameter).

rdar://135459885
2025-04-17 18:12:30 -07:00
Michael Gottesman
ecb745ea18 Merge pull request #80745 from gottesmm/rdar149019222
[rbi] Teach RBI how to handle non-Sendable bases of Sendable values
2025-04-10 19:57:01 -07:00
Michael Gottesman
23b6937cbc [rbi] Make it so that we correctly do not error on uses of Sendable values that are projected from non-Sendable bases.
Specifically, we only do this if the base is a let or if it is a var but not
captured by reference.

rdar://149019222
2025-04-10 14:53:56 -07:00
Michael Gottesman
a045c9880a [rbi] Add the ability to add flags to PartitionOp.
I am doing this so I can mark requires as being on a mutable non-Sendable base
from a Sendable value.

I also took this as an opportunity to compress the size of PartitionOp to be 24
bytes instead of 40 bytes.
2025-04-10 14:53:56 -07:00
Michael Gottesman
c846c2279e [rbi] Refactor getUnderlyingTrackedValue so that for addresses we return both a value and a base in certain situations.
Previously, when we saw any Sendable type and attempted to look up an underlying
tracked value, we just bailed. This caused an issue in situations like the
following where we need to emit an error:

```swift
func test() {
  var x = 5
  Task.detached { x += 1 }
  print(x)
}
```

The problem with the above example is that despite value in x being Sendable,
'x' is actually in a non-Sendable box. We are passing that non-Sendable box into
the detached task by reference causing a race against the read from the
non-Sendable box later in the function. In SE-0414, this is explicitly banned in
the section called "Accessing Sendable fields of non-Sendable types after weak
transferring". In this example, the box is the non-Sendable type and the value
stored in the box is the Sendable field.

To properly represent this, we need to change how the underlying object part of
our layering returns underlying objects and vends TrackableValues to the actual
analysis for addresses. NOTE: We leave the current behavior alone for SIL
objects.

By doing this, in situations like the above, despite have a Sendable value (the
integer), we are able to ensure that we require that the non-Sendable box
containing the integer is not used after we have sent it into the other Task
despite us not actually using the box directly.

Below I describe the representation change in more detail and describe the
various cases here. In this commit, I only change the representation and do not
actually use the new base information. I do that in the next commit to make this
change easier for others to read and review. I made sure that change was NFC by
leaving RegionAnalysis.cpp:727 returning an optional.none if the value found was
a Sendable value.

----

The way we modify the representation is that we instead of just returning a
single TrackedValue return a pair of tracked values, one for the base and one
for the "value". We return this pair in what is labeled a
"TrackableValueLookupResult":

```c++
struct TrackableValueLookupResult {
  TrackableValue value;

  std::optional<TrackableValue> base;

  TrackableValueLookupResult(TrackableValue value)
    : value(value), base() {}
  TrackableValueLookupResult(TrackableValue value, TrackableValue base)
    : value(value), base(base) {}
};
```

In the case where we are accessing a projection path out of a non-Sendable type
that contains all non-Sendable fields, we do not do anything different than we
did previously. We just walk up from use->def until we find the access path base
which we use as the representative of the leaf of the chain and return
TrackableValueLookupResult(access path base).

In the case where we are accessing a Sendable leaf type projected from a
non-Sendable base, we store the leaf type as our value and return the actual
non-Sendable base in TrackableValueLookupResult. Importantly this ensures that
even though our Sendable value will be ignored by the rest of the analysis, the
rest of the analysis will ensure that our base is required if our base is a var
that had been escaped into a closure by reference.

In the case where we are accessing a non-Sendable leaf type projected from a
Sendable type (which we may have continued to be projected subsequently out of
additional Sendable types or a non-Sendable type), we make the last type on the
projection path before the Sendable type, the value of the leaf type. We return
the eventual access path base as our underlying value base. The logic here is
that since we are dealing with access paths, our access path can only consist of
projections into a recursive value type (e.x.: struct/tuple/enum... never a
class). The minute that we hit a pointer or a class, we will no longer be along
the access path since we will be traversing a non-contiguous piece of
memory (consider a class vs the class's storage) and the traversal from use->def
will stop. Thus, we know that there are only two ways we can get a field in that
value type to be Sendable and have a non-Sendable field:

1. The struct can be @unchecked Sendable. In such a case, we want to treat the
leaf field as part of its own disconnected region.

2. The struct can be global actor isolated. In such a case, we want to treat the
leaf field as part of the global actor's region rather than whatever actor.

The reason why we return the eventual access path base as our tracked value base
is that we want to ensure that if the var value had been escaped by reference,
we can require that the var not be sent since we are going to attempt to access
state from the var in order to get the global actor guarded struct that we are
going to attempt to extract our non-Sendable leaf value out of.
2025-04-10 10:01:18 -07:00
Andrew Trick
935b5e7ea2 Fix GenericSpecializer for addressable parameters.
Addressable parameters must remain indirect.

Incidentally also fixes an obvious latent bug in which all specialization was
disabled if any metatypes could not be specialized.

Fixes rdar://145687827 (Crash of inline-stored Span properties with optimizations)
2025-04-07 14:42:10 -07:00
Andrew Trick
6898e33edd [NFC] fix a dropUnusedArguments parameter name 2025-04-07 14:42:10 -07:00
Artem Chikin
d484ec7c1f [Compile Time Values] Implement a mandatory SIL pass to verify '@const' values 2025-03-27 14:33:35 -07:00
Pavel Yaskevich
41c88f864a [SILOptimizer] Turn "is self-recursive" check into analysis
The body of a function has to be re-analyzed for every call
site of the function, which is very expensive and if the
body is not changed would produce the same result.

This takes about ~10% from swift-syntax overall build time
in release configuration.
2025-03-24 00:25:13 -07:00
Meghana Gupta
30a364d3f0 Merge pull request #80031 from meg-gupta/closurelifetimefixup
Fix ClosureLifetimeFixup to insert destroys at leaking blocks
2025-03-18 10:40:31 -07:00
Meghana Gupta
c528afccf2 Fix ClosureLifetimeFixup to insert destroys at leaking blocks 2025-03-14 15:48:44 -07:00
Erik Eckstein
d225c47d25 AST: rename OpenArchetypeType -> ExistentialArchetypeType
NFC
2025-03-11 20:21:46 +01:00
Nate Chandler
f1bc3b5205 [OSSACanOwned] Dead-end extend base lifetime.
Regardless of consumes of copies, if the original lexical value is not
consumed on a dead-end path, it must remain live to that dead-end.

rdar://145226757
2025-02-27 07:20:27 -08:00
Nate Chandler
5ffe092fa5 [OSSACanOwned] Extend all lifetimes to dead-ends.
Uses of borrows may occur between the borrow and a dead-end regardless
of lexicality, so always extend lifetimes to dead-ends.
2025-02-27 07:20:25 -08:00
Nate Chandler
22fdeff83c [NFC] OSSACanOwned: Record function as member. 2025-02-27 07:19:48 -08:00
Michael Gottesman
44052e5c28 [rbi] Respect nonisolated(unsafe) when assigning or merging into a sending out parameter.
rdar://143714959
2025-02-25 08:39:17 -08:00
Michael Gottesman
b286373631 [rbi] Make a confusing comment clearer.
From talking with @dgregor, it became clear that this comment was easily
interpreted as saying that AssignFresh always introduced a disconnected value...
which is not the case. Instead, AssignFresh just introduces a new value that
could have any form of isolation. The actual isolation of the value is assigned
via tryToTrackValue and eventually SILIsolationInfo::get().
2025-02-24 10:09:32 -08:00
Meghana Gupta
4561658799 Avoid creating unoptimizable copies in CSE
CSE uses OSSA rauw which creates copies and copies that are created to optimize
across borrow scopes are unoptimizable. This PR avoids this situation for now.
2025-02-19 11:20:43 -08:00