We weren't showing the unsafe uses when we determine that a for..in
loop is unsafe. Do so, which generally means complaining about `next()`
being unsafe. Fixes rdar://151237127
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.
(cherry picked from commit ee9487b86f)
Print diagnostic groups as part of the LLVM printer in the same manner as the
Swift one does, always. Make `-print-diagnostic-groups` an inert option, since we
always print diagnostic group names with the `[#GroupName]` syntax.
As part of this, we no longer render the diagnostic group name as part
of the diagnostic *text*, instead leaving it up to the diagnostic
renderer to handle the category appropriately. Update all of the tests
that were depending on `-print-diagnostic-groups` putting it into the
text to instead use the `{{documentation-file=<file name>}}`
diagnostic verification syntax.
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)`.
Memory unsafety in the iteration part of the for-in loop (i.e., the part
that works on the iterator) can be covered by the "unsafe" effect on
the for..in loop, before the pattern.
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.
Warnings about unsafe uses due to an @unsafe IteratorProtocol conformance
(for the implicit call to next()) could not be silenced. Follow the same
path we did for the Sequence conformance (and makeIterator() call) by
associating it with the `unsafe` on the sequence argument.
This isn't the only solution here, but it's a reasonable one.
Check for unsafe conformances for type erasure and opaque type
erasure.
This also uncovered an issue where we were making every conformance of
an unsafe type to an unsafe protocol @unsafe implicitly, even though
that's not really what we want.
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.
Protocol conformances have a handful attributes that can apply to them
directly, including @unchecked (for Sendable), @preconcurrency, and
@retroactive. Generalize this into an option set that we carry around,
so it's a bit easier to add them, as well as reworking the
serialization logic to deal with an arbitrary number of such options.
Use this generality to add support for @unsafe conformances, which are
needed when unsafe witnesses are used to conform to safe requirements.
Implement general support for @unsafe conformances, including
producing a single diagnostic per missing @unsafe that provides a
Fix-It and collects together all of the unsafe witnesses as notes.
Drive the strict-safety diagnostics for a particular declaration from
primary type checking for declarations, so any memory-safety-related
diagnostics will only be emitted for the primary files. This also
brings them together as notes under a single warning for each
declaration.
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.
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.