scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

TypeVars can't specialize Scope declarations #10506

Open joroKr21 opened 7 years ago

joroKr21 commented 7 years ago

Below are a few examples involving type aliases and refinement types that show how among several equivalent types, some can be resolved implicitly while others cannot.

object Main {
  type With[A, B] = A with B

  class TC[A]
  object TC {
    def apply[A](implicit tc: TC[A]): TC[A] = tc
    implicit def tc[A, B]: TC[A With B] = new TC
  }

  trait Trait { def head: Int }
  TC[Trait With Serializable] // ok
  TC[Trait with Serializable] // ok

  type Struct = { def head: Int }
  TC[Struct With Serializable] // ok
  TC[Struct with Serializable] // missing (1)

  type TwSeq = Trait With Seq[Int]
  TC[TwSeq With Serializable] // ok
  TC[TwSeq with Serializable] // ok

  type SwSeq = Struct With Seq[Int]
  TC[SwSeq With Serializable] // ok
  TC[SwSeq with Serializable] // missing (2)

  TC[(Trait With Seq[Int]) with Serializable] // missing (3)
  TC[(Trait with Seq[Int]) with Serializable] // ok

  TC[(Struct With Seq[Int]) with Serializable] // missing (4)
  TC[(Struct with Seq[Int]) with Serializable] // missing (5)
}

I looked more closely into (1). -Xlog-implicits reports:

Information:(16, 5) Main.this.TC.tc is not a valid implicit value for Main.TC[Main.Struct with Serializable] because:
hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
 found   : [A, B]Main.TC[Main.With[A,B]]
    (which expands to)  [A, B]Main.TC[A with B]
 required: Main.TC[Main.Struct with Serializable]
    (which expands to)  Main.TC[AnyRef{def head: Int} with Serializable]
  TC[Struct with Serializable] // missing (1)

Further debugging reveals that at some point implicit resolution checks if the types below are compatible:

Main.TC[Main.With[?A,?B]] weak_<:< Main.TC[Main.Struct with Serializable] // false

Strangely enough though:

Main.TC[Main.With[?A,?B]] =:= Main.TC[Main.Struct with Serializable] // true

Intuitively I would expect A =:= B to imply A <:< B. But I'm not sure what role type variables play here.

Scala version: 2.12.3 Java version: Oracle 1.8.0_144

milessabin commented 7 years ago

Somewhat related to https://github.com/scala/bug/issues/9770.

joroKr21 commented 7 years ago

@milessabin I don't know about that. But here is how to reproduce inconsistencies in the subtype relation involving type variables:

import scala.reflect.runtime
val universe = runtime.universe.asInstanceOf[runtime.JavaUniverse]
import universe._

val tvar = TypeVar(symbolOf[Set[Any]].typeParams.head)
val struct = typeOf[{ def i: Int }]
val pat = refinedType(tvar :: typeOf[Serializable] :: Nil, NoSymbol)
val tpe = refinedType(struct :: typeOf[Serializable] :: Nil, NoSymbol)

println(tvar =:= struct) // true
println(tvar.constr) // _= AnyRef{def i: Int}

println(tpe =:= pat) // true
println(tpe <:< pat) // true
println(pat <:< tpe) // false
println(pat <:< struct) // false
println(tvar <:< struct) // true
println(tvar.member(TermName("i"))) // <none>
println(tvar.inst.member(TermName("i"))) // method i
joroKr21 commented 6 years ago

Further minimization:

object Test {
  type Parent
  type Scope = { val x: Int }  
  implicit def refined[A]: Parent with A = ???
  implicitly[Parent with Int] // ok
  implicitly[Parent with Scope] // missing
}

A leaky abstraction: ?A <:< Scope and Parent with ?A <:< Parent with Int, but not Parent with ?A <:< Parent with Scope