The first step is to reproduce the issue you're trying to fix. Say you want to reproduce locally issue #7710, you would first copy the code from the "Minimised Code" section of the issue to a file named e.g.
local/i7710.scala, and then try to compile it from the sbt console opened in the dotty root directory:
$ sbt sbt:scala3> scalac -d local/out local/i7710.scala
-dflag specifies a directory
local/outwhere generated code will be output.
You can then verify that the local reproduction has the same behaviour as originally reported in the issue. If so, then you can start to try and fix it. Otherwise, perhaps the issue is out of date, or is missing information about how to accurately reproduce the issue.
Let's take a deeper look at this
scalac command we just used. As we have seen you can compile a test file either from sbt:
$ sbt > scalac <OPTIONS> <FILE>
in the same way that you could from terminal:
$ scalac <OPTIONS> <FILE>
Here are some useful debugging
-Xprint:all: prints the
ASTafter each specified phase. Phase names can be found by examining the
dotty.tools.dotc.transform.*classes for their
-Xprint:erasure. You can discover all phases in the
ctx.log("")logging for the specified phase.
-Ycheck:allverifies the consistency of
ASTnodes between phases, in particular checks that types do not change. Some phases currently can't be
Ychecked, therefore in the tests we run:
- the last frontier of debugging (before actual debugging) is the range of logging capabilities that can be enabled through the
dotty.tools.dotc.config.Printersobject. Change any of the desired printer from
defaultand this will give you the full logging capability of the compiler.
You may also want to further inspect the types of a piece of code to verify the AST. Check out the section on How to Inspect Values for a detailed guide on this.
Sometimes you will need more complex commands to reproduce an issue, and it is useful to script these, which can be done with dotty-issue-workspace. It allows to bundle sbt commands for issue reproduction in one file and then run them from the Dotty project's sbt console.
Follow the steps in the README to install the plugin.
In your Issue Workspace directory (as defined in the plugin's README file, "Getting Started" section, step 2), create a subdirectory for the issue:
Create a file for the reproduction:
cd i7710; touch Test.scala. In that file, insert the code from the issue.
In the same directory, create a file
launch.isswith the following content:
$ (rm -rv out || true) && mkdir out # clean up compiler output, create `out` dir. scala3/scalac -d $here/out $here/Test.scala
- The first line,
$ (rm -rv out || true) && mkdir outspecifies a shell command (it starts with
$), in this case to ensure that there is a fresh
outdirectory to hold compiler output.
- The next line,
scala3/scalac -d $here/out $here/Test.scalaspecifies an sbt command, which will compile
Test.scalaand place any output into
$hereis a special variable that will be replaced by the path of the parent directory of
launch.isswhen executing the commands.
- The first line,
Now, from a terminal you can run the issue from sbt in the dotty directory (See here for a reminder if you have not cloned the repo.):
$ sbt sbt:scala3> issue i7710
This will execute all the commands in the
i7710/launch.issfile one by one. If you've set up
dotty-issue-workspaceas described in its README, the
issuetask will know where to find the folder by its name.
You can use script arguments inside
launch.iss to reduce the number of steps when working with issues.
Say you have an issue
foo, with two alternative files that are very similar:
original.scala, which reproduces the issue, and
alt.scala, which does not, and you want to compile them selectively?
You can achieve this via the following
$ (rm -rv out || true) && mkdir out # clean up compiler output, create `out` dir. scala3/scalac -d $here/out $here/$1.scala # compile the first argument following `issue foo <arg>`
It is similar to the previous example, except now you will compile a file
$1.scala, referring to the first argument passed after the issue name. The command invoked would look like
issue foo original to compile
issue foo alt for
In general, you can refer to arguments passed to the
issue <issue_name> command using the dollar notation:
$1 for the first argument,
$2 for the second and so on.
launch.iss file, one command can be spread across multiple lines. For example, if your command has multiple arguments, you can put each argument on a new line.
Multiline commands can even have comments in-between lines. This is useful if you want to try variants of a command with optional arguments (such as configuration). You can put the optional arguments on separate lines, and then decide when they are passed to the command by placing
# in front to convert it to a comment (i.e. the argument will not be passed). This saves typing the same arguments each time you want to use them.
launch.iss file is an example of how you can use multiline commands as a template for solving issues that run compiled code. It demonstrates configuring the
scala3/scalac command using compiler flags, which are commented out. Put your favourite flags there for quick usage.
$ (rm -rv out || true) && mkdir out # clean up compiler output, create `out` dir. scalac # Invoke the compiler task defined by the Dotty sbt project -d $here/out # All the artefacts go to the `out` folder created earlier # -Xprint:typer # Useful debug flags, commented out and ready for quick usage. Should you need one, you can quickly access it by uncommenting it. # -Ydebug-error # -Yprint-debug # -Yprint-debug-owners # -Yshow-tree-ids # -Ydebug-tree-with-id 340 # -Ycheck:all $here/$1.scala # Invoke the compiler on the file passed as the second argument to the `issue` command. E.g. `issue foo Hello` will compile `Hello.scala` assuming the issue folder name is `foo`. scala -classpath $here/out Test # Run main method of `Test` generated by the compiler run.
In this section, you have seen how to reproduce an issue locally, and next you will see how to try and detect its root cause.