Kotlin-native: Unexpected Obj-C _ suffixing for interfaces with properties of the same name but different types

Created on 30 Aug 2019  路  14Comments  路  Source: JetBrains/kotlin-native

Let's call this test.kt:

interface WidthHeight1 {
    val height: Float
    val width: Float
}

interface WidthHeight2 {
    val height: Double
    val width: Double
}

~/.konan/kotlin-native-macos-1.3.1/bin/konanc test.kt -o test -p framework yields the following Obj-C protocols:

__attribute__((swift_name("WidthHeight1")))
@protocol TestWidthHeight1
@required
@property (readonly) float height __attribute__((swift_name("height")));
@property (readonly) float width __attribute__((swift_name("width")));
@end;

__attribute__((swift_name("WidthHeight2")))
@protocol TestWidthHeight2
@required
@property (readonly) double height_ __attribute__((swift_name("height_")));
@property (readonly) double width_ __attribute__((swift_name("width_")));
@end;

I'd expect neither to have underscore suffixes. IMO, it's on the programmer to figure out how a design like this should lead to valid code in the end. In other words, resolving this in Kotlin would be hard (having a class implement both interfaces), so it seems weird to fix the naming conflicts in Obj-C. Does this make sense?

All 14 comments

Consider a Kotlin class implementing both interfaces.
If height property has the same Objective-C name for both of resulting protocols, then the class method table should have two functions with different binary types (one returning float, other returning double) for the same selector. How would you do that?

Right I don't think you can do that in Kotlin, so it seems weird that we would add namespacing to allow you to do it in Obj-C? Basically, you've designed yourself into a hole, so I think you (me the designer) needs to design yourself out of it. Do you see what I mean?

to allow you to do it in Obj-C

This is basically not the reason, please read by answer above.

I don't think you can do that in Kotlin

I bet I can:

private class WidthHeightImpl : WidthHeight1, WidthHeight2 {
    override val height: Nothing
        get() = TODO()

    override val width: Nothing
        get() = TODO()
}

Do you see what I mean?

I don't think so. Could you clarify?

Interesting. Is that the only way you can satisfy the interfaces in this scenario? If so, doesn't that seem impractical?

Consider a Kotlin class implementing both interfaces.

I would understand the namespacing if that were the case, but in this case, them merely existing together causes the namespacing to happen. That's the frustrating part: now we have to consider the global namespace for property names (and associated types) as common as width and height.

Is that the only way you can satisfy the interfaces in this scenario?

What do you mean by "this"?

If so, doesn't that seem impractical?

The compiler can't decide that some valid source code "seem impractical" and generate incorrect machine code for it. That's not what compilers do.

I would understand the namespacing if that were the case, but in this case, them merely existing together causes the namespacing to happen.

What do propose instead? How would you generate correct Objective-C method table in this case without mangling?

Sorry what I mean is that in my scenario, those interfaces exist in the same header, but they're never used/implemented together. If they were implemented by the same kotlin class and that class was exposed, I would understand that yes you need the suffixing. In my case, no single kotlin class is implementing them, so it doesn't matter that they have properties with the same name. Does that clarify?

If they were implemented by the same kotlin class and that class was exposed

The thing is: every Kotlin class potentially requires Objective-C class generated even if it is not exposed, since the instance of this Kotlin class may leak to Objective-C world.

So making translation conditional here would make it vary depending on private implementation too, which would lead to a translation strategy even more global and unpredictable than we have now.

Ah I see. Yep okay I understand now. Thanks for your patience!

Thank you for the report!

We completely understand limitations of current approach and have some considerations on improving the situation, but the problem is complicated enough, so it would take some time.

@SvyatoslavScherbina are the name mangling PRs that I see cropping up aimed at possibly addressing this?

I don't think so.

@SvyatoslavScherbina as discussed, I think the key issue here is that it's unexpected. Can something be added so that the compiler emits a log or other warning when it has to disambiguate in the Obj-C header?

This way, we can look for that during our build and manually fail the build ourselves and ask that engineers improve their method names.

Now that I've thought about this some more, it might be better to just add a compiler mode, similar to the new API mode, that emits a warning for each collision for interops where a collision had to be resolved this way, when enabled via a flag.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tarek360 picture tarek360  路  4Comments

msink picture msink  路  4Comments

msink picture msink  路  4Comments

talanov picture talanov  路  3Comments

jonnyzzz picture jonnyzzz  路  4Comments