scala / bug

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

Refining a return type in a sub-trait loses type information #10725

Open szeiger opened 6 years ago

szeiger commented 6 years ago

In this following code the overridden declaration of fact in Iterable makes type inference fail for g and h The version in f where the additional type information is deliberately dropped, compiles successfully.

object IterableTest {
  trait IterableOps[+CC[_]] {
    def fact: IterableFactory[CC]
  }

  trait Iterable[+A] extends IterableOps[Iterable] {
    override def fact: IterableFactory[Iterable]
  }

  trait IterableFactory[+CC[_]]

  def f[CC[_] <: Iterable[_] with IterableOps[CC]](from: CC[_]): IterableFactory[CC] = (from:                  IterableOps[CC]).fact
  def g[CC[_] <: Iterable[_] with IterableOps[CC]](from: CC[_]): IterableFactory[CC] = (from: Iterable[_] with IterableOps[CC]).fact
  def h[CC[_] <: Iterable[_] with IterableOps[CC]](from: CC[_]): IterableFactory[CC] = (from                                  ).fact
}

This fails to compile (tested with Scala 2.12.2, 2.12.4 and 2.13.0-M3) with:

IterableTest.scala:13: error: type mismatch;
 found   : IterableTest.IterableFactory[IterableTest.Iterable]
 required: IterableTest.IterableFactory[CC]
  def g[CC[_] <: Iterable[_] with IterableOps[CC]](from: CC[_]): IterableFactory[CC] = (from: Iterable[_] with IterableOps[CC]).fact
                                                                                                                                ^
IterableTest.scala:14: error: type mismatch;
 found   : IterableTest.IterableFactory[IterableTest.Iterable]
 required: IterableTest.IterableFactory[CC]
  def h[CC[_] <: Iterable[_] with IterableOps[CC]](from: CC[_]): IterableFactory[CC] = (from                                  ).fact

It works in Dotty (current master branch).

szeiger commented 6 years ago

Gitter discussion about this issue starting at https://gitter.im/scala/contributors?at=5a86e43b76ced47639edd6de

Blaisorblade commented 6 years ago

The issue is that the type of from.fact is the one of Iterable.fact and not of IterableOps.fact. This might be according to the spec. Relevant quotes:

From https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#designators:

If r is a stable identifier of type T, the selection r.x refers statically to a term member m of r that is identified in T by the name x. The type of a designator is the type T of the entity it refers to [which I think, from context, must be m] The selection's result is then the member of r that is either defined by m or defined by a definition overriding m.

To figure out members of Iterable[_] with IterableOps[CC] let's look at https://www.scala-lang.org/files/archive/spec/2.11/03-types.html#compound-types:

If a declaration or definition overrides a declaration or definition in one of the component types T1,…,Tn, the usual rules for overriding apply

Finally, the rules for overriding say that:

A member M of class C that matches a non-private member M′ of a base class of C is said to override that member. In this case the binding of the overriding member M must subsume the binding of the overridden member M′.

I'd say that Iterable[_].fact does not subsume IterableOps[CC].fact at the location of the compound type Iterable[_] with IterableOps[CC]. OTOH, the spec talks about the binding of Iterable[_].fact, which does subsume the binding of IterableOps[CC].fact.

It appears the use of the subsumption relation must be refined using asSeenFrom to account for type arguments (https://www.scala-lang.org/files/archive/spec/2.11/03-types.html#base-types-and-member-definitions), and that the spec language might be insufficiently precise here.

EDIT: https://gitter.im/scala/contributors?at=5a87389b76ced47639f07bcf