Dotty: Cant use compiletime.ops.any.== with String when type of a value

Created on 27 Feb 2020  路  17Comments  路  Source: lampepfl/dotty

minimized code

scala> import compiletime.ops.any._

scala> summon["" == "" =:= true]                                                                                        
val res1: true =:= true = generalized constraint

scala> val t: "" == "" = true
1 |val t: "" == "" = true
  |       ^
  |       Type argument ("" : String) does not conform to upper bound AnyVal 
1 |val t: "" == "" = true
  |             ^
  |       Type argument ("" : String) does not conform to upper bound AnyVal 

expectation

EDIT: the second example behaves according to its definition of type ==[X <: AnyVal, Y <: AnyVal] <: Boolean. However it should behave like the first. The type signature should be changed so that t can be typed

bug

Most helpful comment

Yep, no problem. I'll make a PR later tonight.

All 17 comments

Comparing string literals is a legitimate thing to do, so can't this be fixed by changing the signature of ==to type ==[X <: Singleton, Y <: Singleton] ?

what would you say for this case, is the left side of =:= meant to be reduced first?:

scala> import compiletime.ops.int._

scala> class Foo
// defined class Foo

scala> summon[Foo * Foo =:= 0]
1 |summon[Foo * Foo =:= 0]
  |                       ^
  |Cannot prove that Foo * Foo =:= (0 : Int)..
  |I found:
  |
  |    <:<.refl[Nothing]
  |
  |But method refl in object <:< does not match type Foo * Foo =:= (0 : Int).

Honestly no idea, but that's a separate problem from getting == to work with string which is what https://github.com/lampepfl/dotty/issues/8721 was about, and it was closed in favor of this issue.

updated the context

Singleton might also be overly restrictive, as it then doesn't allow for String == String to be modeled. But it may allow for (String & Singleton) == (String & Singleton), so perhaps that's equivalent?

If the parameter type bounds change to Singleton, it could make sense to change the result type of the operation to Singleton as well (e.g. Boolean & Singleton for ==).

Yeah, so we could also just have no bounds at all if that's simpler.

It's better to have no bounds at all in this context, or to change == from any package to string and int, etc. with the proper bounds. So we will have int.== and string.==.

It is my intention to update the singleton-ops library to dotty, so it will take of combining generic == for all the various operation types using match type.

Moving them to type-specific subpackages means that we lose the ability to do 1 == "1". I don't know how useful that is in the first place though, as you're guaranteed that constants of different types are not equal, so perhaps that's a trade-off worth making.

I actually thought String would be a subtype of AnyVal when I wrote those bounds. So no type bounds is also fine by me. It would fit the package name of ops.any, and the semantics are preserved when you apply a compiletime op with something else than a primitive singleton type (i.e. the type is not folded in those cases).

Moving them to type-specific subpackages means that we lose the ability to do 1 == "1"

I don't think that is useful, but again in singleton-ops I will be able to do

type ==[L, R] <: Boolean = (L, R) match {
  case (Int, Int) => int.==[L, R]
  case (String, String) => string.==[L, R]
  case (Int, String) => string.==[string.ToString[L], R]
}

@MaximeKjaer Can you take care of making a PR for this ?

Yep, no problem. I'll make a PR later tonight.

I have implemented most of a fix, which may be useful:

https://github.com/noelwelsh/dotty/tree/bugfix-singleton-ops-8392

The commit comment details what I have done. I think the most important part of my change is what I have done to the tests. As far as I can tell from the Dotty documentation and my own experiments the existing tests were not actually performing any testing. Which is to say the test would pass regardless of the type tests succeeding or failing.

@noelwelsh

The tests were in the neg directory without a check
file so it was irrelevant if they compiled or not

Dotty's testing infrastructure looks for // error marker at the end of lines in neg tests and use that to verify which lines produced an error.

I see. That would explain why my tests are failing.

It seems to me that there is something slightly broken about how errors are handled in compiletime ops. Here is the example from #8271 that fails because the bounds of == are AnyVal:

import scala.compiletime.ops.any._
val eq3: "1" == "1" = true
// -- [E057] Type Mismatch Error: tests/neg/singleton-ops-any.scala:2:9 -----------
// 2 |val eq3: "1" == "1" = true
//   |         ^
//   |       Type argument ("1" : String) does not conform to upper bound AnyVal
// -- [E057] Type Mismatch Error: tests/neg/singleton-ops-any.scala:2:16 ----------
// 2 |val eq3: "1" == "1" = true
//   |                ^
//   |       Type argument ("1" : String) does not conform to upper bound AnyVal
// 2 errors found

However, it seems that the type-level constant fold still is taking place, as assigning false to eq3 raises an error:

import scala.compiletime.ops.any._
val eq3: "1" == "1" = false
// -- [E007] Type Mismatch Error: tests/neg/singleton-ops-any.scala:2:22 ----------
// 2 |val eq3: "1" == "1" = false
//   |                      ^^^^^
//   |                      Found:    (false : Boolean)
//   |                      Required: (true : Boolean)
// 1 error found

In fact, if there is another type mismatch error in the file, the fact that == is applied with arguments that don't match the bounds isn't even reported:

import scala.compiletime.ops.any._

val eq2: 1 == 2 = true
val eq3: "1" == "1" = true
// -- [E007] Type Mismatch Error: tests/neg/singleton-ops-any.scala:4:20 ----------
// 4 |  val eq2: 1 == 2 = true
//   |                    ^^^^
//   |                    Found:    (true : Boolean)
//   |                    Required: (false : Boolean)
// 1 error found

With no type bounds on the parameters of == this should in essence be "fixed". But it does seem like that fix is hiding a bug somewhere else.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andreaTP picture andreaTP  路  3Comments

deusaquilus picture deusaquilus  路  3Comments

Blaisorblade picture Blaisorblade  路  3Comments

NightMachinary picture NightMachinary  路  3Comments

travisbrown picture travisbrown  路  3Comments