4

I noticed that the type checker works by phases. Sometimes scalac returns only a few errors, which makes you think that are almost there, but once you fix them all – boom – next phases, you suddenly get a lot of errors that where not there before.

What are the different phases of the type checker?

Is there a way to know in which phase the type checker gave up on my code (other than recognizing the errors)?

OlivierBlanvillain
  • 7,701
  • 4
  • 32
  • 51
  • I believe that one of Odersky's slides from this [talk](https://www.youtube.com/watch?v=WxyyJyB_Ssc) shows all of the phases. – Kevin Meredith Oct 16 '15 at 14:55
  • I will definitely watch that, but note that I'm (only) interested about the phases at which the compiler stops to report errors. – OlivierBlanvillain Oct 19 '15 at 07:55
  • This is possibly a duplicate of http://stackoverflow.com/questions/4527902/what-is-the-order-of-the-scala-compiler-phases – Felix Oct 20 '15 at 08:29
  • On another note, I believe the phases in which the compiler should report errors in the user code are few because most of the phases seem to deal with intermediate code, i.e. syntax and semantic errors are probably recognized in the early few phases. – Felix Oct 20 '15 at 08:30

2 Answers2

3

As @Felix points out, this answer lists the compile phases:

$ scalac -version
Scala compiler version 2.11.6 -- Copyright 2002-2013, LAMP/EPFL
$ scalac -Xshow-phases
    phase name  id  description
    ----------  --  -----------
        parser   1  parse source into ASTs, perform simple desugaring
         namer   2  resolve names, attach symbols to named trees
packageobjects   3  load package objects
         typer   4  the meat and potatoes: type the trees
        patmat   5  translate match expressions
superaccessors   6  add super accessors in traits and nested classes
    extmethods   7  add extension methods for inline classes
       pickler   8  serialize symbol tables
     refchecks   9  reference/override checking, translate nested objects
       uncurry  10  uncurry, translate function values to anonymous classes
     tailcalls  11  replace tail calls by jumps
    specialize  12  @specialized-driven class and method specialization
 explicitouter  13  this refs to outer pointers
       erasure  14  erase types, add interfaces for traits
   posterasure  15  clean up erased inline classes
      lazyvals  16  allocate bitmaps, translate lazy vals into lazified defs
    lambdalift  17  move nested functions to top level
  constructors  18  move field definitions into constructors
       flatten  19  eliminate inner classes
         mixin  20  mixin composition
       cleanup  21  platform-specific cleanups, generate reflective calls
    delambdafy  22  remove lambdas
         icode  23  generate portable intermediate code
           jvm  24  generate JVM bytecode
      terminal  25  the last phase during a compilation run

Is there a way to know in which phase the type checker gave up on my code (other than recognizing the errors)?

If you add the -verbose flag to scalac, it will print the name of each phase and how long it took after that phase completes. You can then infer which phase failed

I don't think that scalac exposes different phases of typing, only the compile phases. The typer is listed as a single compile phase.

Community
  • 1
  • 1
Rich
  • 15,048
  • 2
  • 66
  • 119
1

The compiler option for this is -Yissue-debug. It outputs a stack trace in 2.10 when an error is issued.

The code supporting it was removed in 2.11 during the refactor of reporting, but the option is still valid. (I restored it at some point because, in fact, it's the quickest way to see what is emitting an error; but apparently that PR died on the vine and disappeared. Probably a victim of push -f.)

In 2.12, you can supply a custom reporter that does just about the same thing. They claim that they will augment the reporter API with access to context, so you could perhaps directly query the current phase, inspect trees, and so on.

Here's an example of the situation you describe:

class C { def f: Int ; def g: Int = "" ; def h = "\000" }

There are three errors, but only one is reported at a time because they are emitted in different compiler phases.

To clarify the question, various phases besides "typer" can create and typecheck trees, and additionally can enforce well-typedness even after trees are typed. (There are also other kinds of errors, such as "unable to write the output file.")

For C, the parser emits an error (under -Xfuture) for the deprecated octal syntax, the typer reports the type mismatch in g, and the grab bag refchecks phase checks the declared but undefined (empty) member f. You would normally wind up fixing one error at a time. If the parser error is emitted as a warning, then the warning would be suppressed until the errors were fixed, so that would be the last to pop up instead of the first.

Here is a sample reporter that tries to do more than output huge stack traces.

package myrep

import scala.tools.nsc.Settings
import scala.tools.nsc.reporters.ConsoleReporter

import scala.reflect.internal.util._

class DebugReporter(ss: Settings) extends ConsoleReporter(ss) {
  override def warning(pos: Position, msg: String) = debug {
    super.warning(pos, msg)
  }
  override def error(pos: Position, msg: String) = debug {
    super.error(pos, msg)
  }
  // let it ride
  override def hasErrors = false

  private def debug(body: => Unit): Unit = {
    val pkgs = Set("nsc.ast.parser", "nsc.typechecker", "nsc.transform")
    def compilerPackages(e: StackTraceElement): Boolean = pkgs exists (e.getClassName contains _)
    def classname(e: StackTraceElement): String = (e.getClassName split """\.""").last
    if (ss.Yissuedebug) echo {
      ((new Throwable).getStackTrace filter compilerPackages map classname).distinct mkString ("Issued from: ", ",", "\n")
    }
    body
  }
}

It lies about having no errors so that the compiler won't abort early.

It would be invoked this way, with the reporter class on the "tool class path":

scalacm -toolcp repdir -Xreporter myrep.DebugReporter -Yissue-debug -deprecation errs.scala

where

$ scalacm -version
Scala compiler version 2.12.0-M2 -- Copyright 2002-2013, LAMP/EPFL

Sample output:

Issued from: Scanners$UnitScanner,Scanners$Scanner,Parsers$Parser,Parsers$Parser$$anonfun$templateStat$1,Parsers$Parser$$anonfun$topStat$1,Parsers$SourceFileParser,Parsers$UnitParser,SyntaxAnalyzer,SyntaxAnalyzer$ParserPhase
errs.scala:4: warning: Octal escape literals are deprecated, use \u0000 instead.
class C { def f: Int ; def g: Int = "" ; def h = "\000" }
                                                  ^
Issued from: Contexts$ImmediateReporter,Contexts$ContextReporter,Contexts$Context,ContextErrors$ErrorUtils$,ContextErrors$TyperContextErrors$TyperErrorGen$,Typers$Typer,Analyzer$typerFactory$$anon$3
errs.scala:4: error: type mismatch;
 found   : String("")
 required: Int
class C { def f: Int ; def g: Int = "" ; def h = "\000" }
                                    ^
Issued from: RefChecks$RefCheckTransformer,Transform$Phase
errs.scala:4: error: class C needs to be abstract, since method f is not defined
class C { def f: Int ; def g: Int = "" ; def h = "\000" }
      ^
one warning found
two errors found
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • I realize no one cares, but this is where you added it back in. Still in the PR queue. Why don't you add more context while you're at it? https://github.com/som-snytt/scala/blob/issue/2991-2.12/src/compiler/scala/tools/nsc/typechecker/Contexts.scala#L577 – som-snytt Oct 26 '15 at 06:36
  • the link is broken, is there a new link? I don't think this option is used anywhere in scalac code. My proposal is to add -Ydebug-error, just like scala 3 – tribbloid Dec 03 '21 at 07:06
  • 1
    @tribbloid it became a PR for an unrelated issue https://github.com/scala/scala/pull/4440 . – som-snytt Dec 07 '21 at 22:15
  • Thanks a lot! Just realised that you are a scala committer – tribbloid Dec 08 '21 at 22:03
  • The PR is integrated, but the option "-Yissue-debug" is still not used in the compiler anywhere. In my PR: https://github.com/scala/scala/pull/9824. That option is removed completely with no repercussion. – tribbloid Dec 08 '21 at 22:05
  • My PR however backported the "-Ydebug-error" option in dotty, do you think it will be helpful? – tribbloid Dec 08 '21 at 22:06
  • @tribbloid yes I've missed stacktrace-on-error. But who is ira19921 if not a russian hacker bot? – som-snytt Dec 09 '21 at 00:58
  • No idea, is this "stacktrace-on-error" in your PR? I've searched for it in scalac source code: latest master. Russian hacker bots should prefer English tho. – tribbloid Dec 09 '21 at 23:49