scala / scala3

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

`c.toHexString` is hexed #14630

Open som-snytt opened 2 years ago

som-snytt commented 2 years ago

Compiler version

3.1.1

Minimized code

scala> 'B'.toHexString
val res0: String = 42

scala> def f(c: Char) = c.toHexString
-- [E008] Not Found Error: ----------------------------------------------------------------------------------------------------------------------
1 |def f(c: Char) = c.toHexString
  |                 ^^^^^^^^^^^^^
  |                 value toHexString is not a member of Char.
  |                 An extension method was tried, but could not be fully constructed:
  |
  |                     longWrapper(c)
1 error found

Maybe it's a REPL thing.

➜  scalac -d /tmp hexed.scala
-- [E008] Not Found Error: hexed.scala:2:19 -----------------------------------------------------------------------------------------------------
2 |def f(c: Char) = c.toHexString
  |                 ^^^^^^^^^^^^^
  |                 value toHexString is not a member of Char.
  |                 An extension method was tried, but could not be fully constructed:
  |
  |                     intWrapper(c)
1 error found
➜  cat hexed.scala

def f(c: Char) = c.toHexString

Output

As shown.

Expectation

Not as shown.

Also would not expect REPL to report different wrapper was tried.

prolativ commented 2 years ago

In scala 2

def f(c: Char) = c.toHexString

compiles successfully and after typer this gets expanded to

def f(c: Char): String = scala.Predef.intWrapper(c.toInt).toHexString

no matter if in REPL or when a file is compiled. In case of

'B'.toHexString

it's

scala.Predef.intWrapper(66).toHexString

in both scala 2 and 3. So we can see that in both cases a character literal gets widened to Int. On the other hand a parameter of type Char gets implicitly converted to Int with .toInt in scala 2. To me it seems the problem is that there are two implicit conversions that return an instance of a type that has toHexString method: intWrapper and longWrapper and scala 3 compiler doesn't know which one to choose while for some reason in scala 2 intWrapper arbitrarily takes priority.

smarter commented 2 years ago

Unclear if there's anything non-hackish we can do here without being able to change the standard library.

smarter commented 2 years ago

Unless implicit conversions should consider a conversion to Int to be more specific than a conversion to Long? This is the logic that isValueSubType uses, I don't know if either Scala 2 or 3 take that into account for specificity.

prolativ commented 2 years ago

One thing that probably should be pointed out is that here scala2 converts a Char to an Int with c.toInt, which is itself a non-implicit def. The same happens for c: Int where c is a Char variable. However in scala 3 it's Char.char2int(c), which is indeed an implicit def.