eclipse-archived / ceylon

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

Type inference bug #4408

Closed CeylonMigrationBot closed 8 years ago

CeylonMigrationBot commented 9 years ago

[@renatoathaydes] In the following code, the wrong type is inferred in the last call, and this does not compile:

class X<out Y>(shared Y y) {}
X<To> transform<From, To>(X<From> x, To(From) convert)
        => X(convert(x.y));

X<String> x1 = X("10");
X<Integer?> x2 = transform(x1, parseInteger); // Error

The error is on parseInteger:

Argument must be assignable to parameter convert of transform: Integer?(String, Integer=) is not assignable to Integer?(String|Integer)

But there is no way that From could be an Integer in the above code.

If the type parameters are explicitly given, it compiles:

X<Integer?> x2 = transform<String, Integer?>(x1, parseInteger); // OK

[Migrated from ceylon/ceylon-spec#1302] [Closed at 2015-05-24 23:26:22]

CeylonMigrationBot commented 9 years ago

[@gavinking] Alright, so the winner of this issue was @jvasileff. His strategy seems robust. I need to write a spec for it. But anyway it was a team effort. Thanks @RossTate.

CeylonMigrationBot commented 9 years ago

[@RossTate] So what's plan for clarifying the behavior for Reference? Always infer left to right?

CeylonMigrationBot commented 9 years ago

[@gavinking] @RossTate It infers Anything in both cases. No, it's nothing to to with LTR ordering, it's just that it applies the constraints as a separate step.

CeylonMigrationBot commented 9 years ago

[@gavinking] Excuse me, that's not the correct explanation. The real reason is that it ignores inferred args for other type parameters when applying the constraint. But actually I was going to look into changing that. It was already on my todo list.

CeylonMigrationBot commented 9 years ago

[@gavinking] There's a TODO at ExpressionVisitor.java:4261.

CeylonMigrationBot commented 9 years ago

[@RossTate] ?

  1. Input becomes intersection of its upper bounds, which in this case is Output
  2. Output becomes union of its lower bounds, which in this case is eitherString(if you realize that the parameter now has typeOutput) orNothing` (if you don't realize that)

or

  1. Output becomes union of its lower bounds, which in this case is Input
  2. Input becomes intersection of its upper bounds, which in this case is Anything because it has no upper bounds by this point

So you get either Reference<String, String> or Reference<String, Nothing> (which is broken) or Reference<Anything, Anything> depending on in which order you infer. What am I misunderstanding here?

CeylonMigrationBot commented 9 years ago

[@gavinking] @RossTate Well, it infers Reference<Anything,String> because Input in contravariant, so the occurrence of String in a covariant location of the parameter list does not constraint it. On the other hand, this covariant occurrence of Input, which has Output as a supertype, does push Output upward, because Output is covariant. Thus far it's all quite sound.

What I need to look into, like I said above, is then coming back and substituting Output=String when I apply the upper bound on Input to the inferred value for Input. Right now the upper bound gets thrown away.

CeylonMigrationBot commented 9 years ago

[@RossTate] Anything is not a subtype of String, so isn't that unsound.

CeylonMigrationBot commented 9 years ago

[@gavinking] Huh? The inferred types are rejected of course.

CeylonMigrationBot commented 9 years ago

[@RossTate] Ah, so you're opting to error even if there are possible solutions. I misunderstood that.

CeylonMigrationBot commented 9 years ago

[@gavinking] I have updated the spec to cover the outcome of this discussion. I still need to circle back around to the issue of upper bounds and type inference, which is on my todo list.

Thanks everyone!