Proposed changes to SE-0033 Import Objective-C Constants as Swift Types

During implementation, I’ve come up with some problems with SE-0033 <https://github.com/apple/swift-evolution/blob/master/proposals/0033-import-objc-constants.md&gt; as written, and am proposing some changes to the proposal. I’d like to present them here for early discussion, and will write up a proposal amendment shortly.

The intent of this feature is to introduce a zero-cost type that serves as a source of safety and convenience, but has the same underlying storage as the typedef, and thus we pay no bridging cost.

The biggest issue with the proposal as written is with importing a typedef as an enum. Enums do not allow us to control the underlying storage, and thus we would constantly be paying a bridging cost in its use. The benefits of using enums was access to the switch/case syntax and the communication with the user that our intent is for there to be no new cases, that is, it’s non-extensible.

I propose that we import what were “struct” wrappers as structs whose raw-value initializer is implicitly labeled, that is init(_ rawValue: Type). What were “enum” wrappers have explicit labels, e.g. init(rawValue: Type). Otherwise, behavior is much the same, but we could think about enhanced warnings for the non-extensible case.

The second issue concerns importing bridged typedefs, e.g. NSString. In that case, we want the underlying storage to be NSString, but present it as a String to the users. I propose that for those we create a stored property _rawValue : NSString, and a computed property rawValue: String.

Third is common-word name stripping. I propose that we follow the common-prefix stripping logic that already exists for enum constants, albeit in this case it’s confined to common-prefix with the type name. This is a tradeoff in how much magic and heuristics the importer does, which can occasionally backfire. It’s worth noting that swift_name allows control of the name and importing a global as a member of the newly created type, e.g. swift_name(“MyTypedefStruct.globalValue”), if prefix-stripping is insufficient.

Due to these changes, I also propose to re-evaluate the attribute name and syntax. swift_wrapper(enum) doesn’t really make much sense. I propose that we use the name swift_struct and swift_struct(extensible), or something like that. The old attributes can stay around as aliases for now, and we can deprecate them later.

SE-0033 and SE-0044 are already "Implemented in Swift 3" according to both
the proposals and the README, but not according to the bug tracker:

<https://github.com/apple/swift-evolution/blob/master/README.md&gt;
<Issues · apple/swift-issues · GitHub;

(I can't find a bug report for SE-0033).

The example in SE-0033 imports HKQuantityTypeIdentifierBodyMassIndex as the
HKQuantityTypeIdentifier.bodyMassIndex enum case, but what if you also need
to nest this inside the class as HKQuantityType.Identifier.bodyMassIndex?

(See SE-0086 hoisted types).

-- Ben

Michael Ilseman wrote:

The biggest issue with the proposal as written is with importing a typedef
as an enum. Enums do not allow us to control the underlying storage,
and thus we would constantly be paying a bridging cost in its use.
[...]
The second issue concerns importing bridged typedefs, e.g. NSString.
In that case, we want the underlying storage to be NSString,
but present it as a String to the users.

Could you import __attribute__((swift_wrapper(enum)) constants as follows:

public enum HKQuantityTypeIdentifier : String {
    case bodyMassIndex
    case bodyFatPercentage
    case height
    case bodyMass
    case leanBodyMass

    private static let _bodyMassIndex: NSString
    private static let _bodyFatPercentage: NSString
    private static let _height: NSString
    private static let _bodyMass: NSString
    private static let _leanBodyMass: NSString
}

-- Ben

Michael Ilseman wrote:

The biggest issue with the proposal as written is with importing a typedef
as an enum. Enums do not allow us to control the underlying storage,
and thus we would constantly be paying a bridging cost in its use.
[...]
The second issue concerns importing bridged typedefs, e.g. NSString.
In that case, we want the underlying storage to be NSString,
but present it as a String to the users.

(Sorry, ignore my previous reply. I thought that "bridging cost" meant
converting between String and NSString).

Instead of _ObjectiveCBridgeable, are you using `unsafeBitCast` for
toll-free bridging?

-- Ben

SE-0033 and SE-0044 are already "Implemented in Swift 3" according to both
the proposals and the README, but not according to the bug tracker:

<https://github.com/apple/swift-evolution/blob/master/README.md&gt;
<Issues · apple/swift-issues · GitHub;

(I can't find a bug report for SE-0033).

The example in SE-0033 imports HKQuantityTypeIdentifierBodyMassIndex as the
HKQuantityTypeIdentifier.bodyMassIndex enum case, but what if you also need
to nest this inside the class as HKQuantityType.Identifier.bodyMassIndex?

SE0033 is fully compatible with the other enhancements to swift_name (see SE0044), including nesting types and renaming onto types. So in this case you can do something analogous to:

typedef … HKQuantityTypeIdentifier __attribute((swift_struct(extensible))) __attribute((swift_name(“HKQuantityType.Identifier")));

To import the type as a nested type. And, the globals that come in as static members will be members of this nested type (since that’s the only type that exists, that is it’s the Swift type to correspond to HKQuantityTypeIdentifier). A good point for me to clarify is how this interacts with common prefix stripping, common prefix stripping is performed on the C/Objective-C name. If you want full control over the name and where it ends up going, you can use swift_name on the constant itself:

extern const HKQuantityTypeIdentifier HKQuantityTypeIdentifierBodyMassIndex __attribute((swift_name(“HKQuantityType.Identifier.bodyMassIndex")))

Or whatever names / types you want.

Hope this answers your question. Thanks for pointing all of these out, as it deserves mention in any proposal adjustment.

···

On Jun 6, 2016, at 3:51 PM, Ben Rimmington via swift-evolution <swift-evolution@swift.org> wrote:

(See SE-0086 hoisted types).

-- Ben

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution