This patch makes sure we don't get warnings in strict memory safe mode
when using shared references. Those types are reference counted so we
are unlikely to run into lifetime errors.
rdar://151039766
Currently, we only get warnings for using unsafe types in expressions
but not in the function signature. The tests did not use the std::string
object in the function body. As a result, we regressed and std::string
was considered unsafe.
The reason is that the annotation only mode for calculating escapability
of a type did not do what we intended. std::basic_string is
conditionally escapable if the template argument is escapable. We
considered 'char' to have unknown escapability in annotation only mode.
The annotation only mode was introduced to avoid suddenly importing
certain types as not escapable when they have pointer fields and break
backward compatibility.
The solution is to make annotation only mode to still consider char and
co as escapable types and only fall back to unknown when the inference
otherwise would have deduced non-escapable (for unannotated typed).
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)`.
After PR #79424 was merged the compiler proper is doing inference on
what C++ types should be considered unsafe. Remove the duplicated (and
slightly divergent) logic from the importer as we no longer need it and
we should have a consistent view of what is considered unsafe. The only
divergence left is the old logic that renames some methods to have
"Unsafe" in their names. In the future, we want to get rid of this
behavior (potentially under a new interop version).
SafeInterop was guarding whether we import certain foreign types as
unsafe. Since these attrbutes are only considered when an opt-in strict
language mode is on, this PR removes this feature flag. We still rely on
the presence of the AllowUnsafeAttribute flag to add the unsafe
attributes to the imported types and functions.
In strict safe mode we should consider all C++ APIs with non-escapable
parameters unsafe unless they have their lifetimes annotated. This can
be done using [[clang::lifetimebound]], [[clang::lifetime_capture_by]],
or [[clang::noescape]].
Swift imports template specializations as a standalone type (not as an
instantiation of a generic) so unsafety is not propagated from the
template arguments to the specialization. This PR propagates this
information explicitly.
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.
The std::basic_string class is escapable only if its template argument
is escapable. This change helps us consider the regular std::string type
with the non-escapable char template argument as self-contained and a
safe type to use. This prevents spurious warnings in strict memory
safety mode.
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.
The C++ span should be a non-escapable type but is imported as escapable
for backward compatibility reason. This is inherently unsafe, so make
sure std::span is imported as such. In the future, we plan to generate
safe overloads using Swift's Span and that will be the preferred way of
using the API.
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.
ClangImporter already had some logic in place to rename certain unsafe
C++ methods to make sure their name indicates unsafety. With the recent
push for auditability, we have a new @unsafe attribute so we can
automate parts of the auditing process. This patch makes sure whenever
we rename a method as "Unsafe", we also add the @unsafe attribute.
For now, this logic is used for importing fewer unannotated types as
unsafe. In the future, this logic will be used by escapability inference
for other (non-aggregate) types.
In this mode all C++ types are imported as unsafe by default. Users
explicitly marking types are escapable or not escapable can make them
imported as safe. In the future, we also want to import unannotated
functions as unsafe and add more logic to infer types that are actually
safe, like agregates of escapable types.