Skip to content
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

Type inference does not dealias type constructors enough to infer type arguments #8709

Open
scabug opened this issue Jul 6, 2014 · 6 comments
Labels
dealias compiler isn't dealiasing when it should, or vice versa infer
Milestone

Comments

@scabug
Copy link

scabug commented Jul 6, 2014

Scalac refuses to infer the type arguments of or below, in the definition of bippy. If however I inline the =?>: alias, it manages. I thought it was a regression, but it's there on every Scala I happen to have around (in 2.9.2, 2.10.3, 2.11.0 and 2.11.1).

object Bug {
  type =?>:[A, B] = PartialFunction[A, B]

  val ??? : Nothing = throw new Exception
  //Does not work.
  def or[T, U](f: T =?>: U)(g: T => U): T => U =
  //Works.
  //def or[T, U](f: PartialFunction[T, U])(g: T => U): T => U =
    //x => f applyOrElse (x, g) //Works in everything but 2.9
    ???

  def bar: String =?>: String = ???
  def foo: String => String = ???
  def bippy: String => String =
    or(bar orElse bar)(foo)
}

In 2.11.0:

<console>:21: error: type mismatch;
 found   : PartialFunction[String,String]
 required: Bug.=?>:[T,String]
    (which expands to)  PartialFunction[T,String]
           or(bar orElse bar)(foo)
                  ^

In 2.9.2:

<console>:21: error: type mismatch;
 found   : PartialFunction[String,String]
 required: Bug.=?>:[T,String]
           or(bar orElse bar)(foo)
                  ^

Random/fun facts:

What's more fun is that when or and its clients are in different files: introducing the breakage in the interface won't change the SBT interface "hash", so the client will not be recompiled right away, only when you're least expecting it (say, you clean).

Original instance here:
inc-lc/ilc-scala@bea1580#diff-c23523827d5e9b210d097946ecae4a46L6

Error witnessed also on Travis below
https://travis-ci.org/inc-lc/ilc-scala/jobs/29252425

I thought it was a duplicate, but the only similar issue (at least superficially) seems #3777.

@scabug
Copy link
Author

scabug commented Jul 6, 2014

Imported From: https://issues.scala-lang.org/browse/SI-8709?orig=1
Reporter: @Blaisorblade
Affected Versions: 2.9.2, 2.10.3, 2.11.1

@scabug
Copy link
Author

scabug commented Jul 6, 2014

@Blaisorblade said:
With -Ytyper-debug (look for bippy):

$ scalac -Ytyper-debug Bug.scala
|-- <empty> EXPRmode-POLYmode-QUALmode (site: package <root>)
|    \-> <empty>.type
|-- object Bug BYVALmode-EXPRmode (site: package <empty>)
|    |-- Nothing TYPEmode (site: value ???  in Bug)
|    |    \-> Nothing
|    |-- Nothing TYPEmode (site: value ???  in Bug)
|    |    \-> Nothing
|    |-- super EXPRmode-POLYmode-QUALmode (silent: <init> in Bug)
|    |    |-- this EXPRmode (silent: <init> in Bug)
|    |    |    \-> Bug.type
|    |    \-> Bug.type
|    |-- $eq$qmark$greater$colon[A, B]PartialFunction[A, B] BYVALmode-EXPRmode (site: object Bug)
|    |    |-- PartialFunction[A, B] TYPEmode (site: type =?>: in Bug)
|    |    |    |-- A TYPEmode (site: type =?>: in Bug)
|    |    |    |    \-> A
|    |    |    |-- B TYPEmode (site: type =?>: in Bug)
|    |    |    |    \-> B
|    |    |    \-> PartialFunction[A,B]
|    |    |-- PartialFunction[A, B] TYPEmode (site: type =?>: in Bug)
|    |    |    \-> PartialFunction[A,B]
|    |    \-> [type $eq$qmark$greater$colon] Bug.=?>:[A,B]
|    |-- Nothing BYVALmode-EXPRmode (site: object Bug)
|    |    |-- Nothing TYPEmode (site: value ???  in Bug)
|    |    |    \-> Nothing
|    |    |-- throw new Exception() : pt=Nothing BYVALmode-EXPRmode (site: value ???  in Bug)
|    |    |    |-- new Exception() : pt=Throwable BYVALmode-EXPRmode (site: value ???  in Bug)
|    |    |    |    |-- new Exception BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value ???  in Bug)
|    |    |    |    |    |-- new Exception EXPRmode-POLYmode-QUALmode (silent: value ???  in Bug)
|    |    |    |    |    |    |-- Exception FUNmode-TYPEmode (silent: value ???  in Bug)
|    |    |    |    |    |    |    |-- scala.`package` EXPRmode-POLYmode-QUALmode (silent: value ???  in Bug)
|    |    |    |    |    |    |    |    \-> scala.type
|    |    |    |    |    |    |    \-> Exception
|    |    |    |    |    |    \-> Exception
|    |    |    |    |    \-> (x$1: Throwable)Exception <and> (x$1: String, x$2: Throwable)Exception <and> (x$1: String)Exception <and> ()Exception
|    |    |    |    \-> Exception
|    |    |    \-> Nothing
|    |    \-> [val $qmark$qmark$qmark ] Nothing
|    |-- def $qmark$qmark$qmark BYVALmode-EXPRmode (site: object Bug)
|    |    |-- Nothing : pt=Nothing EXPRmode (site: value ??? in Bug)
|    |    |    |-- Bug.type EXPRmode-POLYmode-QUALmode (site: value ??? in Bug)
|    |    |    |    \-> Bug.type
|    |    |    \-> Bug.???.type (with underlying type Nothing)
|    |    \-> [def $qmark$qmark$qmark] => Nothing
|    |-- def or[T, U] BYVALmode-EXPRmode (site: object Bug)
|    |    |-- _root_.scala.Function1[T, U] TYPEmode (site: method or in Bug)
|    |    |    |-- T TYPEmode (site: method or in Bug)
|    |    |    |    \-> T
|    |    |    |-- U TYPEmode (site: method or in Bug)
|    |    |    |    \-> U
|    |    |    \-> T => U
|    |    |-- $eq$qmark$greater$colon[T, U] TYPEmode (site: value f in Bug)
|    |    |    |-- T TYPEmode (site: value f in Bug)
|    |    |    |    \-> T
|    |    |    |-- U TYPEmode (site: value f in Bug)
|    |    |    |    \-> U
|    |    |    \-> Bug.=?>:[T,U]
|    |    |-- _root_.scala.Function1[T, U] TYPEmode (site: value g in Bug)
|    |    |    |-- T TYPEmode (site: value g in Bug)
|    |    |    |    \-> T
|    |    |    |-- U TYPEmode (site: value g in Bug)
|    |    |    |    \-> U
|    |    |    \-> T => U
|    |    |-- $eq$qmark$greater$colon[T, U] TYPEmode (site: value f in Bug)
|    |    |    \-> Bug.=?>:[T,U]
|    |    |-- _root_.scala.Function1[T, U] TYPEmode (site: value g in Bug)
|    |    |    \-> T => U
|    |    |-- _root_.scala.Function1[T, U] TYPEmode (site: method or in Bug)
|    |    |    \-> T => U
|    |    |-- $qmark$qmark$qmark : pt=T => U EXPRmode (site: method or in Bug)
|    |    |    \-> Nothing
|    |    \-> [def or] [T, U](f: Bug.=?>:[T,U])(g: T => U)T => U
|    |-- def bar BYVALmode-EXPRmode (site: object Bug)
|    |    |-- $eq$qmark$greater$colon[String, String] TYPEmode (site: method bar in Bug)
|    |    |    |-- String TYPEmode (site: method bar in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    |-- String TYPEmode (site: method bar in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    \-> Bug.=?>:[String,String]
|    |    |-- $eq$qmark$greater$colon[String, String] TYPEmode (site: method bar in Bug)
|    |    |    |-- String TYPEmode (site: method bar in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    |-- String TYPEmode (site: method bar in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    \-> Bug.=?>:[String,String]
|    |    |-- $qmark$qmark$qmark : pt=Bug.=?>:[String,String] EXPRmode (site: method bar in Bug)
|    |    |    \-> Nothing
|    |    \-> [def bar] => Bug.=?>:[String,String]
|    |-- def foo BYVALmode-EXPRmode (site: object Bug)
|    |    |-- _root_.scala.Function1[String, String] TYPEmode (site: method foo in Bug)
|    |    |    |-- String TYPEmode (site: method foo in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    |-- String TYPEmode (site: method foo in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    \-> String => String
|    |    |-- _root_.scala.Function1[String, String] TYPEmode (site: method foo in Bug)
|    |    |    |-- String TYPEmode (site: method foo in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    |-- String TYPEmode (site: method foo in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    \-> String => String
|    |    |-- $qmark$qmark$qmark : pt=String => String EXPRmode (site: method foo in Bug)
|    |    |    \-> Nothing
|    |    \-> [def foo] => String => String
|    |-- def bippy BYVALmode-EXPRmode (site: object Bug)
|    |    |-- _root_.scala.Function1[String, String] TYPEmode (site: method bippy in Bug)
|    |    |    |-- String TYPEmode (site: method bippy in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    |-- String TYPEmode (site: method bippy in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    \-> String => String
|    |    |-- _root_.scala.Function1[String, String] TYPEmode (site: method bippy in Bug)
|    |    |    |-- String TYPEmode (site: method bippy in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    |-- String TYPEmode (site: method bippy in Bug)
|    |    |    |    [adapt] String is now a TypeTree(String)
|    |    |    |    \-> String
|    |    |    \-> String => String
|    |    |-- or(bar.orElse(bar))(foo) : pt=String => String EXPRmode (site: method bippy in Bug)
|    |    |    |-- or(bar.orElse(bar)) BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method bippy in Bug)
|    |    |    |    |-- or BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method bippy in Bug)
|    |    |    |    |    [adapt] [T, U](f: Bug.=?>:[T,U])(g: T => U)T => U adapted to [T, U](f: Bug.=?>:[T,U])(g: T => U)T => U
|    |    |    |    |    \-> (f: Bug.=?>:[T,U])(g: T => U)T => U
|    |    |    |    |-- bar.orElse(bar) : pt=Bug.=?>:[?,?] BYVALmode-EXPRmode-POLYmode (silent: method bippy in Bug)
|    |    |    |    |    |-- bar.orElse BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method bippy in Bug)
|    |    |    |    |    |    |-- bar EXPRmode-POLYmode-QUALmode (silent: method bippy in Bug)
|    |    |    |    |    |    |    \-> Bug.=?>:[String,String]
|    |    |    |    |    |    [adapt] [A1 <: A, B1 >: B](that: PartialFunction[A1,B1])PartialFu... adapted to [A1 <: A, B1 >: B](that: PartialFunction[A1,B1])PartialFu...
|    |    |    |    |    |    \-> (that: PartialFunction[A1,B1])PartialFunction[A1,B1]
|    |    |    |    |    |-- bar : pt=PartialFunction[?,?] BYVALmode-EXPRmode-POLYmode (silent: method bippy in Bug)
|    |    |    |    |    |    \-> Bug.=?>:[String,String]
|    |    |    |    |    solving for (A1: ?A1, B1: ?B1)
|    |    |    |    |    \-> PartialFunction[String,String]
|    |    |    |    solving for (T: ?T, U: ?U)
|    |    |    |    \-> (g: T => String)T => String
Bug.scala:15: error: type mismatch;
 found   : PartialFunction[String,String]
 required: Bug.=?>:[T,String]
    (which expands to)  PartialFunction[T,String]
    or(bar orElse bar)(foo)
           ^
|    |    |    |-- foo : pt=<error> EXPRmode (site solving: type T: method bippy in Bug)
|    |    |    |    solving for (T: ?T)
|    |    |    |    \-> String => String
|    |    |    \-> <error>
|    |    \-> [def bippy] => String => String
|    \-> [object Bug] Bug.type
one error found

@scabug
Copy link
Author

scabug commented Jul 6, 2014

@retronym said:
The type alias behaves the same as its RHS if you declare the same variance:

type =?>:[-A, +B] = PartialFunction[A, B]

With this in mind, can you dig a bit further to show that there is a bug here?

@scabug
Copy link
Author

scabug commented Jul 6, 2014

@Blaisorblade said:
Thanks for the quick answer, but I do find it quite surprising.
Why shouldn't Scalac, in this case, resolve the alias statically and go on? Also, what's T doing in the output?

In fairness, I forgot to mention that

or[String, String](bar orElse bar)(foo)

typechecks correctly, as does or(bar)(foo). So this is, at least, a limitation of type inference. Does that help? I added at the end a couple variations.

Also, I just checked that dotty (38b00c5e7a2d3ea43391b5210925aaca13c79b36) does accept the example.

Moreover, given that the alias can be statically resolved, I don't see how the two possible declarations of or are actually different — and SBT agrees and does not recompile client code. The spec just says "A type alias type t = T defines t to be an alias name for the type T" (4.3), and "If t is defined by a type alias type t = T, then t is equivalent to T." (3.5.1, Type equivalence).

Given that type inference is not really specified, whether this is a Scalac bug is of course up to you. And how hard it is to change this (without breaking other things) is a different question. However, if you want to close this, you should convince SBT and dotty to fix their behavior :-)

object Bug {
  type =?>:[A, B] = PartialFunction[A, B]

  val ??? : Nothing = throw new Exception
  //Does not work.
  def or[T, U](f: T =?>: U)(g: T => U): T => U =
  //Works.
  //def or[T, U](f: PartialFunction[T, U])(g: T => U): T => U =
    //x => f applyOrElse (x, g)
    ???

  def bar: String =?>: String = ???
  def foo: String => String = ???
  def bippy: String => String =
    or(bar orElse bar)(foo) //Fails
  def bippyWorking =
    or(bar)(foo)
  def theArg = bar orElse bar //Inferred type: PartialFunction[String,String]
  def bippy2 = or(theArg)(foo) //Also fails
  def theArg2: String =?>: String = bar orElse bar
  def bippy3 = or(theArg2)(foo) //Succeeds.
}

@Blaisorblade
Copy link

Triaged, unclear it's a bug, unlikely to progress in Scala 2.

@milessabin
Copy link

I disagree ... reopening :-)

@milessabin milessabin reopened this Mar 3, 2018
@SethTisue SethTisue added this to the Backlog milestone Nov 11, 2020
@SethTisue SethTisue added the dealias compiler isn't dealiasing when it should, or vice versa label Nov 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dealias compiler isn't dealiasing when it should, or vice versa infer
Projects
None yet
Development

No branches or pull requests

4 participants