scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.89k stars 1.06k forks source link

Patterns with dependent result types don't get a reference to match scrutinee #20360

Open EugeneFlesselle opened 6 months ago

EugeneFlesselle commented 6 months ago

Compiler version

3.5.0-RC1

Minimized code

trait Wrap:
  type Value
  val value: Value

object Wrap:
  def unapply(w: Wrap): Some[w.Value] = Some(w.value)

object Test:
  val w: Wrap = ???

  val Some(n1) = Wrap.unapply(w)
  val _: w.Value = n1 // ok

  val Wrap(n2) = w
  val _: w.Value = n2 // Error Found: (Test.n2 : Wrap#Value)

Expectation

Expected no errors

EugeneFlesselle commented 5 months ago

In (w: w.type) match case Wrap(n) => ... we type the pattern with a selector type of Wrap, i.e. we have lost the reference to w.type. This is due to a widening in typedMatch: https://github.com/scala/scala3/blob/e2dfea3356ca2b9acac20d7cdb86976e82f98c9f/compiler/src/dotty/tools/dotc/typer/Typer.scala#L1974-L1976 adapting this fixes the issue, but causes a bunch of other tests to fail.

mbovel commented 4 months ago

We had a look during last Spree.

@EugeneFlesselle, what was the conclusion in the end? Do you have a branch somewhere with what you experimented?

EugeneFlesselle commented 4 months ago

what was the conclusion in the end?

There is no easy fix (that breaks nothing else), but it would be nice to figure it out.

Do you have a branch somewhere with what you experimented?

Not right now no, but I can push what I had if anyone needs it

EugeneFlesselle commented 3 months ago

The single modification which allows to above to compile is to modify: https://github.com/scala/scala3/blob/b981231e3bf810d3b0c50e17603209f641e9431f/compiler/src/dotty/tools/dotc/typer/Typer.scala#L2017-L2019 to do less widening of the selector type, as was already done for ConstantTypes.

Doing so breaks other things, such as exhaustivity checking since the inferred type of match all patterns are also affected.

The more general solution would support cases like:

Option(Wrap(1)) match
  case Some(w @ Wrap(x)) => x: w.Value

But this requires having a symbol for w to refine the selector from Wrap to a term ref (w: Wrap) for the typing of the body of the bind pattern Wrap(x). But, the logic for constructing the symbol of the bind heavily relies on being after the body has been typed: https://github.com/scala/scala3/blob/b981231e3bf810d3b0c50e17603209f641e9431f/compiler/src/dotty/tools/dotc/typer/Typer.scala#L2692