swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
66.85k stars 10.29k forks source link

[cxx-interop] plain C++ enums should be importable into Swift #64313

Open jpsim opened 1 year ago

jpsim commented 1 year ago

Motivation

Currently only C++ "class enums" can be imported into Swift. This is fine for enums that are entirely under the user's control, but in some cases those APIs come from external dependencies or can't be modified for some reason (e.g. API compatibility).

Ideally there would still be a way to import these into Swift without having to write a shim wrapper re-exposing them.

Currently if you have this C++ code:

namespace Parent {
namespace Child {
enum MyEnum { first, second };
}
}

constexpr Parent::Child::MyEnum kMyEnumFirst = Parent::Child::first;
// Alternatively: inline const Parent::Child::MyEnum kMyEnumFirst = Parent::Child::first;

As far as I know the only way to construct the first member of the Parent::Child::MyEnum enum from Swift is to use the kMyEnumFirst constant.

// This compiles:
print(kMyEnumFirst)
// This doesn't compile: error: type 'Parent.Child.MyEnum' has no member 'first'
// print(Parent.Child.MyEnum.first)
// This also doesn't compile: error: type 'Parent.Child' has no member 'first'
// print(Parent.Child.first)

If the enum is changed to a "class enum" then this works fine:

namespace Parent {
namespace Child {
enum class MyEnum { first, second };
}
}
print(Parent.Child.MyEnum.first)

Solution

Ideally it should be possible to construct the first member of the Parent::Child::MyEnum enum from Swift just like a class enum would be imported.

print(Parent.Child.MyEnum.first)

Alternatives considered

But if that isn't possible for some reason, then ideally the clang importer would generate accessors to all the enum fields in some other way:

print(Parent.Child.MyEnum.__first)
// or
print(Parent.Child.__MyEnum__first)

Additional context

Thread in Swift Open Source Slack: https://swift-open-source.slack.com/archives/C03FAHQK8F7/p1678459701676699 PRs changing plain enums to enum classes to work around this issue: https://github.com/envoyproxy/envoy/pull/26036 and https://github.com/envoyproxy/envoy/pull/26037

hyp commented 1 year ago

@zoecarver

hyp commented 1 year ago

@AnthonyLatsis can we remove the C++ enums label? It's polluting the regular C++ interop label when creating PRs and completing the labels.

AnthonyLatsis commented 1 year ago

@hyp So you reckon we should use a single label for C++ and Swift features that share the same name? Enums are pretty similar, but what about macros? Or Cxx-specific features like templates?

kambala-decapitator commented 1 month ago

I noticed that this already works in Swift toolchain from Xcode 15.4, but doesn't in 15.2. Maybe also works in 15.3, but I don't have it to test.

namespace S {
enum B {
    Z,
    X,
};
}
public enum S {

    public struct B : Equatable, RawRepresentable {

        public init(_ rawValue: UInt32)

        public init(rawValue: UInt32)

        public var rawValue: UInt32
    }

    // these are missing in 15.2
    public static var Z: S.B { get }

    public static var X: S.B { get }
}