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

Possible typer regression in Scala 3.1.2+ #14804

Closed
WojciechMazur opened this issue Mar 29, 2022 · 5 comments
Closed

Possible typer regression in Scala 3.1.2+ #14804

WojciechMazur opened this issue Mar 29, 2022 · 5 comments

Comments

@WojciechMazur
Copy link
Contributor

WojciechMazur commented Mar 29, 2022

Compiler version

The bug is present in builds since 3.1.2-RC1 and present in current nightly build 3.2.0-RC1-bin-20220308-29073f1-NIGHTLY,

Minimized code

The following code was minimalized from https://github.com/tpolecat/doobie, it compiles with Scala 3.1.1
https://github.dev/tpolecat/doobie/blob/0ead51d4628929db46b29c446eeeef7b68ddaf30/modules/core/src/main/scala/doobie/util/write.scala#L49-L56

sealed abstract class Nullability  extends Product with Serializable
object Nullability {
  sealed abstract class NullabilityKnown extends Nullability
  case object NoNulls         extends NullabilityKnown
  case object Nullable        extends NullabilityKnown
}
import Nullability.*

abstract class Put[A]
sealed trait Elem
object Elem {
   final case class Arg[A](a: A, p: Put[A]) extends Elem
   final case class Opt[A](a: Option[A], p: Put[A]) extends Elem
 }

class Write[A]{
  def toList(a: A): List[Any] = ???
  def puts: List[(Put[_], NullabilityKnown)] = ???
  
  def toFragment(a: A) = {
    (puts zip toList(a)).map {
        case ((p: Put[a], NoNulls), a) => Elem.Arg(a.asInstanceOf[a], p)
        case ((p: Put[a], Nullable), a) => Elem.Opt(a.asInstanceOf[Option[a]], p)
    }
  }
}

Output

[error] ./test.scala:23:71: Found:    (p : Put[a])
[error] Required: Put[Any]
[error]         case ((p: Put[a], NoNulls), a) => Elem.Arg(a.asInstanceOf[a], p)
[error]                                                                       ^
[error] ./test.scala:24:80: Found:    (p : Put[a])
[error] Required: Put[Any]
[error]         case ((p: Put[a], Nullable), a) => Elem.Opt(a.asInstanceOf[Option[a]], p)```
[error]                                                                                ^

## Expectation
The snippet compiles with Scala up to 3.1.1
@WojciechMazur WojciechMazur added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Mar 29, 2022
@KacperFKorban KacperFKorban added area:typer and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Mar 29, 2022
@smarter
Copy link
Member

smarter commented Mar 29, 2022

Thanks for the report and minimization. It works if you specify the type arguments in each case explicitly:

  def toFragment(a: A) = {
    (puts zip toList(a)).map {
        case ((p: Put[a], NoNulls), a) => Elem.Arg[a](a.asInstanceOf[a], p)
        case ((p: Put[a], Nullable), a) => Elem.Opt[a](a.asInstanceOf[Option[a]], p)
    }
  }

... or if you specify the type argument of map explicitly:

  def toFragment(a: A) = {
    (puts zip toList(a)).map[Elem] {
        case ((p: Put[a], NoNulls), a) => Elem.Arg(a.asInstanceOf[a], p)
        case ((p: Put[a], Nullable), a) => Elem.Opt(a.asInstanceOf[Option[a]], p)
    }
  }

I believe this is a consequence of #14026, in particular 3ab18a9 describes situations where a similar change was needed. Basically, in places where the compiler previously inferred a type with wildcards, it might instead infer a type without wildcards. In general, it's impossible to know in advance which of these two options will work better, but most of the time avoiding wildcards is better, so unless we can come up with a better heuristic this probably won't be fixed.

@TheElectronWill
Copy link
Contributor

Is it possible to try both ways? For instance, avoids wildcards, and if it fails, try with wildcards.

@smarter
Copy link
Member

smarter commented Apr 1, 2022

Probably not. Backtracking is dangerous since it can easily lead to an exponential blow-up in time complexity if we end up backtracking multiple times. Infamously, this is (or used to be?) the case in Swift when resolving overloads of numeric operators: https://www.cocoawithlove.com/blog/2016/07/12/type-checker-issues.html

@TheElectronWill
Copy link
Contributor

I see... Thanks for the interesting read!

@SethTisue
Copy link
Member

closing since this isn't clearly actionable

@SethTisue SethTisue closed this as not planned Won't fix, can't repro, duplicate, stale Feb 17, 2023
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

5 participants