Skip to content

[Swiftify] don't unwrap pointers for UP! when not needed#89455

Open
hnrklssn wants to merge 12 commits into
swiftlang:mainfrom
hnrklssn:swiftify-implicitly-not-unwrapped
Open

[Swiftify] don't unwrap pointers for UP! when not needed#89455
hnrklssn wants to merge 12 commits into
swiftlang:mainfrom
hnrklssn:swiftify-implicitly-not-unwrapped

Conversation

@hnrklssn
Copy link
Copy Markdown
Member

Safe wrapper expansions included an explicit unwrap of the baseAddress
of the span parameter unless the underlying pointer parameter was
explicitly _Nullable. Since the baseAddress can only be null if the
count is 0, and the pointer is annotated with counted_by, it is not
allowed to dereference the zero-length buffer, even if the nullability
is not set (i.e. the underlying function parameter is imported with an
implicitly unwrapped optional type).

For consistency, this also adds the same early exit handling for the
return value as for nullable return values.

rdar://177555277

hnrklssn added 11 commits May 22, 2026 19:01
These mirror the existing __counted_by / __sized_by test files and
document the current behavior: __counted_by_or_null and __sized_by_or_null
are currently imported identically to their non-or_null counterparts.

A follow-up change will differentiate the two when combined with
_Nullable: __counted_by _Nullable should become a non-optional Span /
UnsafeBufferPointer, while __counted_by_or_null _Nullable should remain
an optional.
This makes it possible to differentiate OrNull variants from the base
attributes. For now they do the same thing as the base variant, but they
will diverge in a later commit. Clone the test structure of the base
variants to track regressions separately.
This passes the appropriate information from ClangImporter to
_SwiftifyImport for counted_by_or_null and sized_by_or_null. The
generated safe wrappers are still identical to the counted_by and
sized_by versions. That will be addressed in another commit.
The _SwiftifyImport macro expansion used a closure for the return value
to create an expression from an if statement. This is because the
constructor for Span family types requires a non-null pointer. To safely
unwrap the optional pointer we handle the null case separately. This
restructures the macro such that it can return the null case in an early
exit if statement before the main return statement. This has no
functional change for the generated code.
Safe wrappers only need lifetimes for ~Escapable types, not
UnsafeBufferPointers. This has no effect on the generated code.
counted_by and sized_by can only be null if the count parameter is 0.
Since Span and UnsafeBufferPointer can also have a null pointer with
count 0, there is no need for an extra layer of Optional here in the
safe wrapper. counted_by_or_null and sized_by_or_null still map to
Optional since they are always allowed to be null, regardless of count.
Differentiating between the "nil" and "empty span" cases will be
important in a later commit when we implement counted_by_or_null's bound
checking semantics.
A pointer annotated `__counted_by_or_null(len)` is allowed to be null
even when `len` is non-zero (unlike `__counted_by`). For a basic
function with a single parameter this doesn't make much of a difference
for safe wrappers, but when multiple pointers sharing the same count it
does. Previously we would simply extract the length from the first
parameter using `p1?.count ?? 0`. However this would result in a bounds
check failure if `p2` was a non-null non-empty buffer. Instead we now
check all the buffers sharing a count against the first non-null buffer.
If there are any non-nullable buffers to choose from we simply take the
count from one of those. If all are nullable the count is extracted with
a chained expression of all the buffers: `p1?.count ?? p2?.count ?? ...
?? 0`, and then the individual buffer counts are compared to that.

Return pointers already correctly return nil if the underlying function
returned a null pointer.

rdar://177835043
CI runs with pre-Swift-6.1 compiler, so it does not support trailing
commas.
Safe wrapper expansions included an explicit unwrap of the baseAddress
of the span parameter unless the underlying pointer parameter was
explicitly `_Nullable`. Since the baseAddress can only be null if the
count is 0, and the pointer is annotated with counted_by, it is not
allowed to dereference the zero-length buffer, even if the nullability
is not set (i.e. the underlying function parameter is imported with an
implicitly unwrapped optional type).

For consistency, this also adds the same early exit handling for the
return value as for nullable return values.

rdar://177555277
@hnrklssn
Copy link
Copy Markdown
Member Author

@swift-ci please smoke test

@hnrklssn
Copy link
Copy Markdown
Member Author

This PR is based on top of #89395. Only the last commit belongs to this PR.

@hnrklssn hnrklssn requested review from artemcm and tshortli as code owners May 27, 2026 16:25
@hnrklssn
Copy link
Copy Markdown
Member Author

@swift-ci please smoke test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant