Skip to content

type parameter inference behaves unexpectedly when a type parameter is used in a contravariant position #718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
smarter opened this issue Jul 9, 2015 · 7 comments

Comments

@smarter
Copy link
Member

smarter commented Jul 9, 2015

class Foo[-T]

object Test {
  def foo[A](x: Foo[A]): Int = 0
  foo(new Foo[Int])
}

scalac infers A to be Int:

fsc -Xprint:typer try/brokeninf.scala
[[syntax trees at end of                     typer]] // brokeninf.scala
package <empty> {
  class Foo[-T] extends scala.AnyRef {
    def <init>(): Foo[T] = {
      Foo.super.<init>();
      ()
    }
  };
  object Test extends scala.AnyRef {
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    };
    def foo[A](x: Foo[A]): Int = 0;
    Test.this.foo[Int](new Foo[Int]())
  }
}

But dotty infers A to be Nothing:

./bin/dotc -Xprint:frontend try/brokeninf.scala
result of try/brokeninf.scala after frontend:
package <empty> {
  class Foo[T]() extends Object() { 
    type Foo$$T
    private[this] type T =- Foo$$T
  }
  final lazy module val Test: Test$ = new Test$()
  final module class Test$() extends Object() { this: Test.type => 
    def foo[A](x: Foo[A]): Int = 0
    Test.foo[Nothing](new Foo[Int][Int]())
  }
}

This is probably not what we want, especially for function types.

@odersky
Copy link
Contributor

odersky commented Jul 9, 2015

Arguably, dotty is right here. In this case, the type parameter A is minimized. And A = Nothing fullfils the constraint

Foo[A] >: Foo[Int]

so it is picked. scalac has a somewhat weird fear of Nothing :-). If the result would be Nothing it switches bounds. But that leads to other problems. For example, the fact that untyped dotty trees are Tree[Null] instead of Tree[Nothing] is because of scalac's Nothing paranoia.

@smarter
Copy link
Member Author

smarter commented Jul 9, 2015

But the spec says:

A maximal type Ti will be chosen if the type parameter ai appears contravariantly in the type T of the expression. A minimal type Ti will be chosen in all other situations, i.e. if the variable appears covariantly, non-variantly or not at all in the type T. We call such a substitution an optimal solution of the given constraint system for the type T.

And the problem is not specific to Nothing as far as I can see, for example:

class Foo[-T]

trait X
trait Y extends X

object Test {
  def foo[A >: Y](x: Foo[A]): Int = 0
  foo(new Foo[X])
}
  • scalac will infer foo|X](new Foo[X])
  • but dotty will infer foo[Y](new Foo[X])

@odersky
Copy link
Contributor

odersky commented Jul 9, 2015

The type T in this context is Int, and the type variable does not
appear in it. So a minimal type will be chosen and that's Y. I think this
is a bug in scalac.

On Thu, Jul 9, 2015 at 9:59 AM, Guillaume Martres notifications@github.com
wrote:

But the spec
http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#local-type-inference
says:

A maximal type Ti will be chosen if the type parameter ai appears
contravariantly in the type T of the expression. A minimal type Ti will be
chosen in all other situations, i.e. if the variable appears covariantly,
non-variantly or not at all in the type T. We call such a substitution an
optimal solution of the given constraint system for the type T.

And the problem is not specific to Nothing as far as I can see, for
example:

class Foo[-T]
trait Xtrait Y extends X
object Test {

def foo[A >: Y](x: Foo[A]): Int = 0
foo(new Foo[X])

  • scalac will infer foo|X](new Foo[X])
  • but dotty will infer foo[Y](new Foo[Y])


Reply to this email directly or view it on GitHub
#718 (comment).

Martin Odersky
EPFL

@smarter
Copy link
Member Author

smarter commented Jul 9, 2015

I think this is a bug in scalac.

It seems deliberate: https://github.com/scala/scala/blob/2.11.x/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala#L188-L189 used indirectly by https://github.com/scala/scala/blob/2.11.x/src/compiler/scala/tools/nsc/typechecker/Infer.scala#L550

And the behavior of dotty seems more surprising to me than the behavior of scalac here:

import scala.reflect.ClassTag
import scala.reflect.classTag

object Test {
  def getParamType[T: ClassTag](x: T => Int) = println(classTag[T])

  def main(args: Array[String]): Unit = {
    getParamType((x: Int) => x) // dotty: Nothing, scalac: Int
  }
}

@odersky
Copy link
Contributor

odersky commented Jul 9, 2015

It's Cecilia Bigler.

http://ic.epfl.ch/computer-science/bachelor

  • Martin

On Thu, Jul 9, 2015 at 11:31 AM, Guillaume Martres <notifications@github.com

wrote:

I think this is a bug in scalac.

It seems deliberate:
https://github.com/scala/scala/blob/2.11.x/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala#L188-L189

And the behavior of dotty seems more surprising to me than the behavior of
scalac here:

import scala.reflect.ClassTagimport scala.reflect.classTag
object Test {
def getParamType[T: ClassTag](x: T => Int) = println(classTag[T])

def main(args: Array[String]): Unit = {
getParamType((x: Int) => x) // dotty: Nothing, scalac: Int
}
}


Reply to this email directly or view it on GitHub
#718 (comment).

Martin Odersky
EPFL

@DarkDimius
Copy link
Contributor

@smarter, Though dotty behavior here seems unintuitive, the types in all examples that you give are the minimal types that are enough to typecheck. I find it easier to reason about such behavior, compared to the behavior of Scalac, where treatment of Nothing, which is the most minimal type, is very special.

@smarter
Copy link
Member Author

smarter commented Oct 24, 2015

Fixed by #799.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants