eclipse-archived / ceylon

The Ceylon compiler, language module, and command line tools
http://ceylon-lang.org
Apache License 2.0
395 stars 62 forks source link

wildcard capture for Java methods where variable occurs in return type #7402

Closed jvasileff closed 5 years ago

jvasileff commented 5 years ago

Limited wildcard capture was added in #6682 but doesn't help with the Java method:

import java.util.concurrent.Callable;

public class TJ {
    public static <T> Callable<T> echo(Callable<T> t) {
        return t;
    }
}

where T appears invariantly in the return. (Note that Callable above is Java's Callable.)

import java.util.concurrent { Callable }

shared void run() {
    Callable<out Object> computer = nothing;
    TJ.echo<Object>(computer);
}

// source/simple/run.ceylon:5: error: argument must be assignable to parameter 't' of 'echo' in 'TJ': 'Callable<out Object>' is not assignable to 'Callable<Object>?'
//     TJ.echo<Object>(computer);
//                     ^
// 1 error

The actual use case is to construct a covariant FutureTask from a covariant Callable:

import java.util.concurrent { Callable, FutureTask }

shared void run() {
    Callable<out Object> computer = nothing;
    FutureTask<out Object> ft = FutureTask(computer); // can't
}
gavinking commented 5 years ago

In principle, it would be possible to add in an overloaded version of the method, transforming to:

shared class TJ {
    shared overloaded static <T> Callable<T> echo(Callable<T> t);
    shared overloaded static <T> Callable<out T> echo(Callable<out T> t);
    shared overloaded static <T> Callable<in T> echo(Callable<in T> t);
}

However, that would make a lot of Java APIs look pretty ugly in the IDE.

An alternative approach might be to really implement wildcard capture, as suggested by @RossTate, but only enable it for generic types without reified type arguments (i.e. for Java types).

gavinking commented 5 years ago

Actually there's probably a relatively simple way to implement a sort of "minimal" support for wildcard capture that would handle this case. Let me try.

gavinking commented 5 years ago

Done. I'm now pretty far down the path of fully general support for wildcard capture for Java methods!

If I'm not mistaken, all I need now is to be able to capture contravariant wildcards in argument types.

gavinking commented 5 years ago

I've updated the issue description to reflect that this is not restricted to the case of invariant occurrences in the return type 😄

jvasileff commented 5 years ago

Excellent!

On Sep 13, 2018, at 6:05 PM, Gavin King notifications@github.com wrote:

I've updated the issue description to reflect that this is not restricted to the case of invariant occurrences in the return type 😄

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

gavinking commented 5 years ago

Added support for capturing contravariant wildcards in 17b6034. Enjoy!

gavinking commented 5 years ago

There's one minor remaining limitation to this stuff. If I pass a Map<in Key, out Item> to a parameter of type Map<K,I>, it's to stupid to capture the two parameter occurrences with opposite variances, K=in Key, I=out Item. It does work when the in Key and out Item occur in distinct arguments to separate parameters, however, or when they both have the same variance, for example, Map<out Key, out Item>.

gavinking commented 5 years ago

Also, perhaps I should turn it on for Java constructor invocations, as well as method calls.

gavinking commented 5 years ago

perhaps I should turn it on for Java constructor invocations, as well as method calls.

Done!

RossTate commented 5 years ago

Ah, don't allow the type Map<in Key,...> at all! I did a study on mixing declaration-site and use-site variance, and I found that people just get really confused by use-site variances that "oppose" the corresponding declaration-site variances.

And I'm guessing "minimal" support just captures the explicit in/out argument and not the constraints that the class itself imposes on the corresponding type parameter? If so, that's fine, and I found that most (though not quite all) use cases of wildcard capture are covered by that.

gavinking commented 5 years ago

Oh, sorry, @RossTate , that was confusing, my bad. I'm assuming this is the invariant type java.util.Map.

I agree that we don't need to let you override declaration-site variance with use-site variance annotations.

gavinking commented 5 years ago

And I'm guessing "minimal" support just captures the explicit in/out argument and not the constraints that the class itself imposes on the corresponding type parameter?

Right. It's really pretty "dumb" TBH. Not much new code there at all.

RossTate commented 5 years ago

You say "dumb", I say "simple" :)