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

Diverging implicit expansion in Scala 2.13 #11768

Open
lihaoyi opened this issue Oct 10, 2019 · 10 comments

Comments

@lihaoyi
Copy link

@lihaoyi lihaoyi commented Oct 10, 2019

The following code compiles in Scala 2.12.10, fails with a diverging implicit error in Scala 2.13.0:

object A {
  trait B[T]
  implicit def c[T <: Singleton]: B[T] = ???
  implicit def d[T1: B, T2: B]: B[Tuple2[T1, T2]] = ???
  implicit def e[T: B]: B[Option[T]] = ???

  implicit def f[C[_] <: Iterable[_], T](implicit r: B[T]): B[C[T]] = ???
}
object G{
  class H[T: A.B, V: A.B](t: Option[(V, T)]){
    implicitly[A.B[Option[(V, T)]]]
  }
  def h[T: A.B, V: A.B](t: Option[(V, T)]){
    implicitly[A.B[Option[(V, T)]]]
  }
}

class H is what fails to compile, even though def h compiles just fine.

As far as I can tell this is a minimal repro. Removing def f also is sufficient to make everything (including class H) compile. Converting the Tuple2 to a Seq also makes things compile.

This is a regression and causes spurious compile errors when trying to use uPickle with Scala 2.13

@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Oct 10, 2019

It also fails on 2.12 if we enable -Ypartial-unification.
I'm not sure yet why it causes divergence but at some point the implicit search matches:
f[[+T](V, T), T], i.e. it unifies C[_] with (V, T).

@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Oct 10, 2019

More minimization:

  trait B[T]
  object B extends {
    implicit def d[T1: B, T2: B]: B[(T1, T2)] = ???
  }

  object G {
    class H[T : B, V : B] {
      implicitly[B[((V, T), T)]]
    }
  }

Why would being inside a class matter?

@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Oct 10, 2019

Why would being inside a class matter?

Here: https://github.com/scala/scala/blob/19b05850dc835e0e504d635def4de5ab9781da13/src/compiler/scala/tools/nsc/typechecker/Implicits.scala#L515-L521

Inside a method, T and V are skolems (not type parameters).
But inside a class they are type parameters.
So when we have open implicits for B[V] and B[(V, T)] and we want to check if one dominates the other they are stripped to B[?] and B[(?, ?)], which are =:= by the nature of wildcards and we conclude that one dominates and hence there is divergence.

We need to summon help here. I don't know why the class constructor type parameters are not skolemized or why we need to strip them in dominates.

@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Oct 10, 2019

@jbracker

This comment has been minimized.

Copy link

@jbracker jbracker commented Oct 10, 2019

I just ran into this issue as well:

object Example {
   trait Foo[A]
   object Foo {
      implicit def fooPair[A: Foo, B: Foo]: Foo[(A, B)] = ???
   }
   class Test[A: Foo, B: Foo, C: Foo, D: Foo] {
      implicitly[Foo[A]] // Works
      implicitly[Foo[(A, B)]] // Works
      implicitly[Foo[(A, (B, C))]] // Fails
      implicitly[Foo[(A, (B, (C, D)))]] // Fails
   }
}

From a user perspective this is very confusing. I'd consider this a bug.
Using Scala 2.12.3 with -Ypartial-unification.

@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Oct 10, 2019

It's definitely a bug, but weird that we notice it just now. Feels like I have been using -Ypartial-unification since forever.

@Jasper-M

This comment has been minimized.

Copy link
Member

@Jasper-M Jasper-M commented Oct 10, 2019

@jbracker Your example has always failed to compile though. Also without -Ypartial-unification.

@jbracker

This comment has been minimized.

Copy link

@jbracker jbracker commented Oct 11, 2019

@Jasper-M But it should not fail, right? Or am I missing something?

@Jasper-M

This comment has been minimized.

Copy link
Member

@Jasper-M Jasper-M commented Oct 11, 2019

But it should not fail, right? Or am I missing something?

Ideally not, but I suspect it's a known limitation of the divergence checking algorithm.

Though the strange thing is, it works as-is in Dotty, it works if you use shapeless.Lazy in Scala 2, but it still doesn't work with by-name implicits in 2.13...

@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Oct 11, 2019

Well it works if we use def Test instead of class Test so it's the same problem as the OP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.