scala / scala3

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

method with singleton type mismatches same singleton type #1702

Closed rethab closed 7 years ago

rethab commented 7 years ago

As the below example shows, a method that takes a singleton type Int(1) cannot be applied with 1.

scala> def id(x: 1): 1 = x
id: (x: Int(1))Int(1)
scala> id(1)
-- [E007] Type Mismatch Error: <console> -----------------------------------
6 |id(1)
  |   ^
  |   found:    Int(1)
  |   required: Int(1)
  |
liufengyun commented 7 years ago

Thanks @rethab , report confirmed.

Note that following code type checks without problem -- seems related to REPL:

object Foo {
  def id(x: 1): 1 = x
  def f = id(1)
  id(1)
}
rethab commented 7 years ago

I have analyzed this a little.

What I have noticed is that in Typer.scala:1841 where the types are compared

else if (tree.tpe <:< pt) {

it returns false when my code from the first example is run in the REPL, but it returns true if the example from @liufengyun is run (ie. w/o REPL).

Actually, in both cases both sides are equal (ConstantType(Constant(4))) however in the REPL, the two don't refer to the same object in memory whereas otherwise they do. It seems that the TypeComparer doesn't return true unless they point to the same object in memory.

My assumption is that the types are usually cached, but not between runs in the REPL. This is supported by the following snippet, which works in the REPL:

scala> def id(x: 4): 4 = x; id(4)
def id(x: 4.type): Int(4)

What I am unsure about is whether all types are always supposed to be cached and therefore the REPL needs to ensure that it re-uses types between runs (ie. several evaluated lines) or the TypeComparer should return true in topLevelSubType if they are the same but don't point to the same object.

smarter commented 7 years ago

Thanks for the analysis! TypeComparer should not rely on type caching. To me it looks the subtyping checks done by firstTry, secondTry, etc are missing cases for ConstantType

smarter commented 7 years ago

I would try adding handling of tp1 being a ConstantType and tp2 being a ConstantType to firstTry. If that works, you can do some more testing to make sure things work as expected when you have a type alias of a constant type, an abstract type upper-bounded by a constant type, a type parameter which is a constant type, etc. You can test all of this using the REPL and add the output as tests in tests/repl/

rethab commented 7 years ago

Ok, thanks for the hints. I'll first have to invest some time to understand how the whole thing works down there.

Are there no unit tests for the single components such as the TypeComparer? I can surely test this from the REPL, but the issue isn't really with the REPL -- as you are saying.

smarter commented 7 years ago

Ok, thanks for the hints. I'll first have to invest some time to understand how the whole thing works down there.

Here are some tips: you can run the compiler or the repl with -explaintypes to see a small subtyping tests trace when a type mismatch error happens. You can see every subtyping check by setting val subtyping to be equal to new Printer instead of noPrinter in https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/config/Printers.scala and by running the compiler or the repl with -Ylog:all (this will log all phases, you can also just use -Ylog:frontend since the frontend phase is when we do the initial typechecking). Although this may be a lot of information and it might be easier to try to do the fix without thinking about everything that is going on ;).

Are there no unit tests for the single components such as the TypeComparer?

Not for TypeComparer, we don't have muxh unit tests at that level.

smarter commented 7 years ago

(Also have a look at http://dotty.epfl.ch/docs/contributing/workflow.html if you haven't already)

rethab commented 7 years ago

Thanks a lot! I'm actually mostly using the debugger to step through and try to make sense out of it :)