Skip to content

Match types combined with GADT pattern match not reducing as expected #10510

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

Closed
atennapel opened this issue Nov 26, 2020 · 8 comments
Closed
Assignees

Comments

@atennapel
Copy link

atennapel commented Nov 26, 2020

Minimized code

enum Bool {
  case True
  case False
}

import Bool._

enum SBool[B <: Bool] {
  case STrue extends SBool[True.type]
  case SFalse extends SBool[False.type]
}

import SBool._

type Not[B <: Bool] = B match {
  case True.type => False.type
  case False.type => True.type  
}

def not[B <: Bool](b: SBool[B]): SBool[Not[B]] = b match {
  case STrue => SFalse
  case SFalse => STrue
}

Output

Found:    (SBool.STrue : SBool[(Bool.True : Bool)])
Required: SBool[Not[B]]

where:    B is a type in method not which is an alias of (Bool.False : Bool)

Expectation

I would expect the not function to pass typechecking, but it doesn't. Interestingly if I add a implicitly[Not[B] =:= False.type] in the first branch of not it will not complain, so it does know that B is actually True.type in that branch and it will reduce Not correctly if I ask for it.

It looks like the types are not reduced enough when checking the type of the branches against the return type of the function (not).

@atennapel atennapel changed the title Match types combined with GADT not reducing as expected Match types combined with GADT pattern match not reducing as expected Nov 26, 2020
@bishabosha
Copy link
Member

bishabosha commented Nov 26, 2020

minimised to

enum Bool {
  case True
  case False
}

import Bool._

type Not[B <: Bool] = B match {
  case True.type => False.type
  case False.type => True.type  
}

val f: Not[False.type] = True
13 |val f: Not[False.type] = True
   |                         ^^^^
   |                         Found:    (Bool.True : Bool)
   |                         Required: Not[(Bool.False : Bool)]

@bishabosha
Copy link
Member

in this case, it seems that the singleton type of enum values is treated differently to the singleton type of objects, e.g. this works:

sealed trait Bool
object Bool {
  case object True extends Bool
  case object False extends Bool
}

import Bool._

type Not[B <: Bool] = B match {
  case True.type => False.type
  case False.type => True.type  
}

val f: Not[False.type] = True

@bishabosha bishabosha changed the title Match types combined with GADT pattern match not reducing as expected Enum Values in match types do not reduce Nov 26, 2020
@atennapel
Copy link
Author

Writing Bool in that way still does not make my example typecheck.

@bishabosha
Copy link
Member

bishabosha commented Nov 26, 2020

Writing Bool in that way still does not make my example typecheck.

yes, what I found seems to be another issue :/

@bishabosha bishabosha changed the title Enum Values in match types do not reduce Match types combined with GADT pattern match not reducing as expected Nov 26, 2020
@OlivierBlanvillain
Copy link
Contributor

Can this issue be minimized without enums? Match types on x.type where x is a val cannot be reduced by the compiler given that case _: x.type in a match expression desugars to a call to ==, which we know nothing about at compile time.

See my comments in #10511

@atennapel
Copy link
Author

Could you suggest how I can change the example in my initial post to fix the issue? You are saying that

type Not[B <: Bool] = B match {
  case True.type => False.type
  case False.type => True.type  
}

will not work well because I used an enum to create Bool, but if I use case object or case class then I can still not get this to work. Would love to know how to get this example working.

OlivierBlanvillain added a commit to dotty-staging/dotty that referenced this issue Dec 9, 2020
@OlivierBlanvillain
Copy link
Contributor

OlivierBlanvillain commented Dec 9, 2020

Yes, I meant using traits and classes instead of enums to try to pin point the issue, like this:

sealed trait Bool
case object True extends Bool
case object False extends Bool

sealed trait SBool[B <: Bool]
case object STrue extends SBool[True.type]
case object SFalse extends SBool[False.type]

type Not[B <: Bool] <: Bool = B match {
  case True.type => False.type
  case False.type => True.type
}

def not[B <: Bool](b: SBool[B]): SBool[Not[B]] = b match {
  case STrue => SFalse
  case SFalse => STrue
}

That minimization seems to be a duplicate #6687.

@atennapel
Copy link
Author

Ooh I see now, your comment was about minimization of the example. Yes that might aid in debugging it.

OlivierBlanvillain added a commit to dotty-staging/dotty that referenced this issue Dec 10, 2020
OlivierBlanvillain added a commit to dotty-staging/dotty that referenced this issue Dec 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants