Skip to content

GADT constraints not propagated correctly #18740

Open
@pjmc-oliveira

Description

@pjmc-oliveira

Compiler version

(scalac -version)
Scala compiler version 3.3.0 -- Copyright 2002-2023, LAMP/EPFL

Minimized code

sealed trait PTwo

sealed trait Foo[+A]
case class One(unit: Unit) extends Foo[Nothing]
case class Two(unit: Unit) extends Foo[PTwo]

sealed trait FooK[+A]
case class OneK(unit: Unit) extends FooK[Nothing]
case class TwoK(unit: Unit) extends FooK[PTwo]


def foo[A](x: Foo[A], fun: FooK[A] => Unit): Unit =
  if x.isInstanceOf[One] then
    ???
  else x.asInstanceOf[Two] match {
    case Two(unit) =>
      val twoK = fun(TwoK(unit))
      ???
  }

Output

-- [E007] Type Mismatch Error: src/main/scala/main.scala:18:25 -----------------
17 |      val twoK = fun(TwoK(unit))
   |                     ^^^^^^^^^^
   |                     Found:    TwoK
   |                     Required: FooK[A]
   |----------------------------------------------------------------------------
   | Explanation (enabled by `-explain`)
   |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   |
   | Tree: TwoK.apply(unit)
   | I tried to show that
   |   TwoK
   | conforms to
   |   FooK[A]
   | but the comparison trace ended with `false`:
   |
   |   ==> TwoK  <:  FooK[A]
   |     ==> FooK[PTwo]  <:  FooK[A] (left is approximated)
   |       ==> PTwo  <:  A
   |         ==> PTwo  <:  Nothing in frozen constraint
   |         <== PTwo  <:  Nothing in frozen constraint = false
   |       <== PTwo  <:  A = false
   |     <== FooK[PTwo]  <:  FooK[A] (left is approximated) = false
   |   <== TwoK  <:  FooK[A] = false
   |
   | The tests were made under the empty constraint
    ----------------------------------------------------------------------------
1 error found

Expectation

  • Expected code to compile
  • Note that if we change the asInstanceOf cast from Two to Foo[A] & (Two | One) the code will compile. But if it's Two | One or Foo[A] & Two it does not compile.
  • Looking at Dotty in typer > Applications.scala > typedUnApply it only seems to add the GADT constraint mode (withMode(Mode.GadtConstraintInference)(TypeComparer.constrainPatternType(unapplyArgType, selType))) if the selector type is a subtype of the unapply arg type. Adding the GADT constraints to both sides of the seems to fix this type error, but I'm not sure if it would create any unsoundness issues.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions