@_spi with @usableFromInline: how?

i have a @frozen struct that looks like:

extension LZ77.Inflator
{
    @frozen @usableFromInline
    struct Stream
    {
        let format:LZ77.Format
        var input:In
        ...

the compiler will not allow me to define such a type because LZ77.Inflator.In is @_spi(testable):

cannot use struct 'In' here; it is SPI

but i cannot actually seem to give that type an @usableFromInline, because it conflicts with the @_spi(testable) public ACL:

extension LZ77.Inflator
{
    @_spi(testable)
    @frozen @usableFromInline public
    struct In

what can i do here?

Having @_spi declarations be usable from non-SPI @usableFromInline declarations would defeat the design goals of @_spi since the In declaration would have to be visible in the public interface of the module. The purpose of @_spi is to exclude declarations from the public interface of a module. The declarations should only be accessible to clients that have access to a copy of the module that includes SPI declarations, while other clients only have access to a copy of the module that lacks SPI declarations entirely.

It's understandable that you want to both only expose a declaration for the purposes of testing via @_spi while also using it as an implementation detail of @inlinable code, but you'll need to compromise on some aspect of that design to make it work. Either make the declaration fully public, don't use it in @inlinable code, or don't test it directly. The convention for declarations that must be public to satisfy typechecking constraints such as this, but should be treated as internal implementation details by clients of the module, is to _ prefix the declaration name, which will for example exclude it from auto-complete.

3 Likes

really in this situation, i was just trying to keep away from @testable, since @testable would prevent the tests from running in release mode, which is required for a lot of the computationally intensive tests in this gzip library.

in the end i just wound up paring down the size of the test cases until they could run acceptably in debug mode.

2 Likes

gah! i forgot that @testable not only prevents the executable target itself from running in release mode, but also prevents the entire package from being buildable with swift build -c release!

nothing that can’t be worked around with .define("DEBUG", .when(configuration: .debug)), but still, annoying.

1 Like