What keeps a value type from being @inlinable?


(Daryle Walker) #1

I'm making a type by copying another. The original one has a single stored property that is public and let. Its initializer is internal but @inlinable. My copy has a second member, but I don't want it to be a public let. It needs to be a var, but I don't want anyone to modify it besides me. I tried a public private(set), but the compiler complained. I removed all the access levels, making it internal, but the compiler still complained. Is it possible to have a non-public or non-fully-public stored property and still make the main initializer @inlinable?


(Xiaodi Wu) #2

No, it is not.


(Daryle Walker) #3

I found a workaround:

public struct MyType<Base: Collection> {
    @usableFromInline
    internal var _newStart: Base.Index
    //...

    // Now this works
    @inlinable
    internal init(_ base: Base, i: Base.Index) {
        //...
       _newStart = //...
    }
}

The way I use the property doesn't really need it to be made public. However adding "private(set)" fails due to what @xwu just said. If I (or reviewers) need it to really be non-mutating public, I could add a public computed forwarding property. I just have to hope that no one will mutate _newStart outside my type. (Shouldn't be a problem since the type is planned for the Standard Library.)


(Slava Pestov) #4

I guess what you're after is a property with a public getter and a usableFromInline setter. I can't think you of a way to express that now.

@jrose: Can you think of any reasons we shouldn't be able to to this eventually, assuming we came up with meaningful syntax? Is this worth a bug report?


(Jordan Rose) #5

Sorry, why do we need the setter to be usableFromInline? The initializer doesn't go through the setter unless the property has an initial value. Do we just not have that distinction in Sema?


(Daryle Walker) #6

The compiler was saying a mixed stored property ("public private(set)" or "public internal(set)") wasn't allowed.

With @usableFromInline public private(set) var _newStart: Base.Index:

  • The "@usableFromInline" line was flagged with "'@usableFromInline' attribute can only be applied to internal declarations, but '_newStart' is public"
  • The assignment in the initializer was flagged with "Setter for '_newStart' is private and cannot be referenced from an '@inlinable' function"

With "@usableFromInline public internal(set) var _newStart: Base.Index", the errors were the same, except the second one used "internal" instead of "private".

With "@usableFromInline internal private(set) var _newStart: Base.Index", the first error went away, but the second (now only) one stayed.

Addendum: I want to make clear that the declaration of _newStart does not have an initial value to it. So I guess that

may be correct.


(Jordan Rose) #7

Hm. This might be because we wouldn't want adding an initial value to change anything. …no, that's only for resilient libraries.

@Slava_Pestov, do you see any problems with allowing this? Except for the setter DI thing?


(Thomas Roughton) #8

FWIW I have hundreds of public internal(set) inlinable properties that fail to compile under Swift 5. Having this be properly supported would be very useful; the alternatives are to either expose externally-immutable variables as mutable or to separately write a computed public getter for each one.