Commit Graph

13 Commits

Author SHA1 Message Date
Gabor Horvath
402ad33463 [StrictMemorySafety] Check the safety of return types of calls
Previously, we skipped checking the return type of a function for safety
as we expected to warn at the use of the returned value:

  let x = returnsUnsafe()
  usesUnsafe(x) // warn here

Unfortunately, this resulted in missing some unsafe constructs that can
introduce memory safety issues when the use of the return value had a
different shape resulting in false negatives for cases like:

  return returnsUnsafe()

or

  usesUnsafe(returnsUnsafe())

This PR changes the analysis to always take return types of function
calls into account.

rdar://157237301
2025-08-05 12:16:44 +01:00
Doug Gregor
d5476aeda0 Improve diagnostic for unsafe call arguments
Use the argument type rather than the (potentially generic) parameter type.
2025-05-21 13:46:17 +01:00
Doug Gregor
ee9487b86f [Strict memory safety] Provide argument-specific diagnostics for calls
Similar to what we do for 'throws' checking, perform argument-specific
checking for unsafe call arguments. This provides more detailed failures:

```
example.swift:18:3: warning: expression uses unsafe constructs but is not
marked with 'unsafe' [#StrictMemorySafety]
16 |   x.f(a: 0, b: 17, c: nil)
17 |
18 |   x.f(a: 0, b: 17, c: &i)
   |   |                   `- note: argument 'c' in call to instance
method 'f' has unsafe type 'UnsafePointer<Int>?'
   |   `- warning: expression uses unsafe constructs but is not marked
with 'unsafe' [#StrictMemorySafety]
19 |   unsafeF()
20 | }
```

It also means that we won't complain for `nil` or `Optional.none`
arguments passed to unsafe types, which eliminates some false
positives, and won't complain about unsafe result types when there is
a call---because we'd still get complaints later about the
actually-unsafe bit, which is using those results.

Fixes rdar://149629670.
2025-04-25 21:54:19 -07:00
Doug Gregor
b7b5a2a19d [SE-0458] Enable unsafe expressions / attributes / for..in effects by default
With the acceptance of SE-0458, allow the use of unsafe expressions, the
@safe and @unsafe attributes, and the `unsafe` effect on the for..in loop
in all Swift code.

Introduce the `-strict-memory-safety` flag detailed in the proposal to
enable strict memory safety checking. This enables a new class of
feature, an optional feature (that is *not* upcoming or experimental),
and which can be detected via `hasFeature(StrictMemorySafety)`.
2025-02-26 12:30:07 -08:00
Doug Gregor
2de9f4a8f5 [Strict safety] Stop complaining about unsafe signatures
Since we infer unsafety from a use of a declaration that involves unsafe types
in its signature, there isn't a reason to require @unsafe on declaration to
restate it. This matches recent revisions of SE-0458.
2025-02-15 22:02:22 -08:00
Doug Gregor
b3f2f00588 Suggest both @unsafe and @safe Fix-Its for unsafe types in signature 2025-01-23 07:47:19 -08:00
Doug Gregor
8bb5bbedbc Implement an unsafe expression to cover uses of unsafe constructs
Introduce an `unsafe` expression akin to `try` and `await` that notes
that there are unsafe constructs in the expression to the right-hand
side. Extend the effects checker to also check for unsafety along with
throwing and async operations. This will result in diagnostics like
the following:

    10 |   func sum() -> Int {
    11 |     withUnsafeBufferPointer { buffer in
    12 |       let value = buffer[0]
       |                   |     `- note: reference to unsafe subscript 'subscript(_:)'
       |                   |- warning: expression uses unsafe constructs but is not marked with 'unsafe'
       |                   `- note: reference to parameter 'buffer' involves unsafe type 'UnsafeBufferPointer<Int>'
    13 |       tryWithP(X())
    14 |       return fastAdd(buffer.baseAddress, buffer.count)

These will come with a Fix-It that inserts `unsafe` into the proper
place. There's also a warning that appears when `unsafe` doesn't cover
any unsafe code, making it easier to clean up extraneous `unsafe`.

This approach requires that `@unsafe` be present on any declaration
that involves unsafe constructs within its signature. Outside of the
signature, the `unsafe` expression is used to identify unsafe code.
2025-01-10 10:39:14 -08:00
Doug Gregor
29f23bb66a Improve diagnostics for uses of unsafe declarations in functions
Instead of producing a warning for each use of an unsafe entity,
collect all of the uses of unsafe constructs within a given function
and batch them together in a single diagnostic at the function level
that tells you what you can do (add `@unsafe` or `@safe(unchecked)`,
depending on whether all unsafe uses were in the definition), plus
notes identifying every unsafe use within that declaration. The new
diagnostic renderer nicely collects together in a single snippet, so
it's easier to reason about.

Here's an example from the embedded runtime that previously would have
been 6 separate warnings, each with 1-2 notes:

```
swift/stdlib/public/core/EmbeddedRuntime.swift:397:13: warning: global function 'swift_retainCount' involves unsafe code; use '@safe(unchecked)' to assert that the code is memory-safe
395 |
396 | @_cdecl("swift_retainCount")
397 | public func swift_retainCount(object: Builtin.RawPointer) -> Int {
    |             `- warning: global function 'swift_retainCount' involves unsafe code; use '@safe(unchecked)' to assert that the code is memory-safe
398 |   if !isValidPointerForNativeRetain(object: object) { return 0 }
399 |   let o = UnsafeMutablePointer<HeapObject>(object)
    |           |                              `- note: call to unsafe initializer 'init(_:)'
    |           `- note: reference to unsafe generic struct 'UnsafeMutablePointer'
400 |   let refcount = refcountPointer(for: o)
    |                  |                    `- note: reference to let 'o' involves unsafe type 'UnsafeMutablePointer<HeapObject>'
    |                  `- note: call to global function 'refcountPointer(for:)' involves unsafe type 'UnsafeMutablePointer<Int>'
401 |   return loadAcquire(refcount) & HeapObject.refcountMask
    |          |           `- note: reference to let 'refcount' involves unsafe type 'UnsafeMutablePointer<Int>'
    |          `- note: call to global function 'loadAcquire' involves unsafe type 'UnsafeMutablePointer<Int>'
402 | }
403 |
```

Note that we have lost a little bit of information, because we no
longer produce "unsafe declaration was here" notes pointing back at
things like `UnsafeMutablePointer` or `recountPointer(for:)`. However,
strict memory safety tends to be noisy to turn on, so it's worth
losing a little bit of easily-recovered information to gain some
brevity.
2024-12-20 07:34:51 -08:00
Doug Gregor
bd4a477cbd Suggest @safe(unchecked) when only the body of a declaration uses unsafe constructs
Also clean up some diagnostic text for the @unsafe/@safe(unchecked) suggestions.
2024-12-14 08:40:28 -08:00
Doug Gregor
268d5ccbde Suppress strict safety diagnostics in @unsafe declarations
When a declaration is `@unsafe`, don't emit strict safety diagnostics
for uses of unsafe entities, constructs, or types within it. This
allows one to account for all unsafe behavior in a module using strict
memory safety by marking the appropriate declarations `@unsafe`.

Enhance the strict-safety diagnostics to suggest the addition of
`@unsafe` where it is needed to suppress them, with a Fix-It. Ensure
that all such diagnostics can be suppressed via `@unsafe` so it's
possible to get to the above state.

Also includes a drive-by bug fix where we weren't diagnosing unsafe
methods overriding safe ones in some cases.

Fixes rdar://139467327.
2024-12-12 21:22:41 -08:00
Daniel Rodríguez Troitiño
ba68faaed5 [test] Mark tests that use experimental/upcoming features as such
Find all the usages of `--enable-experimental-feature` or
`--enable-upcoming-feature` in the tests and replace some of the
`REQUIRES: asserts` to use `REQUIRES: swift-feature-Foo` instead, which
should correctly apply to depending on the asserts/noasserts mode of the
toolchain for each feature.

Remove some comments that talked about enabling asserts since they don't
apply anymore (but I might had miss some).

All this was done with an automated script, so some formatting weirdness
might happen, but I hope I fixed most of those.

There might be some tests that were `REQUIRES: asserts` that might run
in `noasserts` toolchains now. This will normally be because their
feature went from experimental to upcoming/base and the tests were not
updated.
2024-11-02 11:46:46 -07:00
Doug Gregor
571859f294 Move "unsafe" diagnostics from errors to warnings
Warnings fit better with the approach we're going for, and can be
escalated to errors by `-warnings-as-errors` for clients that need it.
2024-08-19 14:34:00 -07:00
Doug Gregor
8378562e12 Adopt @unsafe throughout the standard library
Annotate all of the `Unsafe*` types and `unsafe` functions in the standard
library (including concurrency, synchronization, etc.) as `@unsafe`. Add a
few tests to ensure that we detect uses of these types in clients that
have disabled unsafe code.
2024-08-19 14:33:09 -07:00