duchess-rs / duchess

Silky smooth Java-Rust interop
https://duchess-rs.github.io/duchess/
Apache License 2.0
138 stars 17 forks source link

erasure and wildcards #57

Open nikomatsakis opened 1 year ago

nikomatsakis commented 1 year ago

Our current handling for ? and erasure is not optimal -- see #41 and #55 for an example of the problems we encounter. This issue is for brainstorming a better solution.

Problem 1: returning ?

The getClass method returns Class<?>. We can't model this with 100% fidelity for Rust users. Mapping it to Class<Object> is somewhat reasonable, since the role of the type parameter in this case is primarily covariant -- it's not necessarily suitable as a general purpose solution, consider methods like

class Foo<T> {
    void takes(T t) { /* given Foo<?>, you shouldn't be able to call this with any object */ }
}

It also allows you to invoke Java methods like this one, which ideally wouldn't be allowed...

void sameClass<T>(Class<T> c1, Class<T> c2)

...but this may not be achievable. I think I'm willing to live with that,.

In both cases, the worst that happens is we generate class cast exceptions in Java, not UB.

Problem 2: returning ? extends T or ? super U

Worth pointing out that sometimes there can be bounds.

Problem 3: modeling erased Java types

Scala sometimes intentionally translates to erased Java types. These I think are largely equivalent to ?, except that Java is more lenient and makes "angelic" type assumptions.

nikomatsakis commented 1 year ago

cc @fpoli -- does this cover the details, or are there further complications I'm ignoring?

fpoli commented 1 year ago

One additional question: Should duchess generate runtime type checks before the JNI calls that make use of the imprecise type parameters?

Since the JVM only verifies bytecode for type safety, not the JNI calls, I suspect that the imprecision of duchess in the type parameters might lead to cases where the JVM receives the wrong type and thus dereferences some bad pointer at runtime. I'll see if I can build an example.

nikomatsakis commented 1 year ago

I've been giving this some thought. My current proposal is that we add some special types:

and an additional trait java::SpecificClass, which is implemented by all Java objects, but not the three types above. We would add this to any type parameter that appears more than once in a signature.

Then we can translate signatures to use those types and I think we actually get the correct typing rules.

This doesn't account for ? extends A & B, we'd have to do something like java::Extends<(A, B)>, but we can do that (up to some fixed arity).

It's probably also possible to do ?extends Foo super Bar? Also possible to account for.