Skip to content

fix #13994: initialise inline ctx in lateEnter #14050

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 8 additions & 16 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Types._
import Scopes._
import Names.Name
import Denotations.Denotation
import typer.Typer
import typer.{Typer, PrepareInlineable}
import typer.ImportInfo._
import Decorators._
import io.{AbstractFile, PlainFile, VirtualFile}
Expand All @@ -23,8 +23,6 @@ import rewrites.Rewrites

import profile.Profiler
import printing.XprintMode
import parsing.Parsers.Parser
import parsing.JavaParsers.JavaParser
import typer.ImplicitRunInfo
import config.Feature
import StdNames.nme
Expand Down Expand Up @@ -302,19 +300,13 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
.setCompilationUnit(unit)
.withRootImports

def process()(using Context) = {
unit.untpdTree =
if (unit.isJava) new JavaParser(unit.source).parse()
else new Parser(unit.source).parse()
ctx.typer.lateEnter(unit.untpdTree)
def processUnit() = {
unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree)
val phase = new transform.SetRootTree()
phase.run
}
if (typeCheck)
if (compiling) finalizeActions += (() => processUnit()) else processUnit()
}
def process()(using Context) =
ctx.typer.lateEnterUnit(doTypeCheck =>
if typeCheck then
if compiling then finalizeActions += doTypeCheck
else doTypeCheck()
)

process()(using unitCtx)
}

Expand Down
47 changes: 39 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import tpd.tpes
import Variances.alwaysInvariant
import config.{Config, Feature}
import config.Printers.typr
import parsing.JavaParsers.JavaParser
import parsing.Parsers.Parser
import Annotations._
import Inferencing._
import transform.ValueClasses._
Expand Down Expand Up @@ -708,15 +710,44 @@ class Namer { typer: Typer =>
ctxWithStats
}

/** Index symbols in `tree` while asserting the `lateCompile` flag.
* This will cause any old top-level symbol with the same fully qualified
* name as a newly created symbol to be replaced.
/** Parse the source and index symbols in the compilation unit's untpdTree
* while asserting the `lateCompile` flag. This will cause any old
* top-level symbol with the same fully qualified name as a newly created
* symbol to be replaced.
*
* Will call the callback with an implementation of type checking
* That will set the tpdTree and root tree for the compilation unit.
*/
def lateEnter(tree: Tree)(using Context): Context = {
val saved = lateCompile
lateCompile = true
try index(tree :: Nil) finally lateCompile = saved
}
def lateEnterUnit(typeCheckCB: (() => Unit) => Unit)(using Context) =
val unit = ctx.compilationUnit

/** Index symbols in unit.untpdTree with lateCompile flag = true */
def lateEnter()(using Context): Context =
val saved = lateCompile
lateCompile = true
try index(unit.untpdTree :: Nil) finally lateCompile = saved

/** Set the tpdTree and root tree of the compilation unit */
def lateTypeCheck()(using Context) =
unit.tpdTree = typer.typedExpr(unit.untpdTree)
val phase = new transform.SetRootTree()
phase.run

unit.untpdTree =
if (unit.isJava) new JavaParser(unit.source).parse()
else new Parser(unit.source).parse()

atPhase(Phases.typerPhase) {
inContext(PrepareInlineable.initContext(ctx)) {
// inline body annotations are set in namer, capturing the current context
// we need to prepare the context for inlining.
lateEnter()
typeCheckCB { () =>
lateTypeCheck()
}
}
}
end lateEnterUnit

/** The type bound on wildcard imports of an import list, with special values
* Nothing if no wildcard imports of this kind exist
Expand Down
20 changes: 20 additions & 0 deletions project/scripts/bootstrapCmdTests
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,23 @@ clear_out "$OUT"
./bin/scalac -d "$OUT/out.jar" tests/pos/i12973.scala
echo "Bug12973().check" | TERM=dumb ./bin/scala -cp "$OUT/out.jar" > "$tmp" 2>&1
grep -qe "Bug12973 is fixed" "$tmp"

echo "capturing scala version from dist/target/pack/VERSION"
cwd=$(pwd)
IFS=':=' read -ra versionProps < "$cwd/dist/target/pack/VERSION" # temporarily set IFS to ':=' to split versionProps
[ ${#versionProps[@]} -eq 3 ] && \
[ ${versionProps[0]} = "version" ] && \
[ -n ${versionProps[2]} ] || die "Expected non-empty 'version' property in $cwd/dist/target/pack/VERSION"
scala_version=${versionProps[2]}

echo "testing -sourcepath with inlining"
# Here we will test that an inline method symbol loaded from the sourcepath (-sourcepath compiler option)
# will have its `defTree` correctly set when its method body is required for inlining.
# So far I have not found a way to replicate issue https://github.com/lampepfl/dotty/issues/13994
# with sbt scripted tests, if a way is found, move this test there.
cwd=$(pwd)
sbt_test_command="++${scala_version}!;clean;prepareSources;compile;copyChanges;compile"
(cd "$cwd/tests/cmdTest-sbt-tests/sourcepath-with-inline" && "$SBT" "$sbt_test_command")
rm -rf "$cwd/tests/cmdTest-sbt-tests/sourcepath-with-inline/target"
rm -rf "$cwd/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/target"
rm -f "$cwd/tests/cmdTest-sbt-tests/sourcepath-with-inline/src/main/scala/a/zz.scala"
9 changes: 8 additions & 1 deletion project/scripts/cmdTestsCommon.inc.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
set -eux

SBT="./project/scripts/sbt" # if run on CI
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" >& /dev/null && pwd)/../.."

SBT="$ROOT/project/scripts/sbt" # if run on CI
# SBT="sbt" # if run locally

SOURCE="tests/pos/HelloWorld.scala"
Expand All @@ -12,6 +14,11 @@ OUT=$(mktemp -d)
OUT1=$(mktemp -d)
tmp=$(mktemp)

die () {
echo >&2 "$@"
exit 1
}

clear_out()
{
local out="$1"
Expand Down
7 changes: 7 additions & 0 deletions tests/cmdTest-sbt-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Readme

Do not use this directory for testing sbt projects in general, add a test case to `dotty/sbt-test`

This directory is for sbt tests that can not be reproduced with sbt scripted tests.

Adding a test here will reduce the performance of running all tests.
17 changes: 17 additions & 0 deletions tests/cmdTest-sbt-tests/sourcepath-with-inline/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import java.util.Properties

val prepareSources = taskKey[Unit]("Copy changes to the src directory")
val copyChanges = taskKey[Unit]("Copy changes to the src directory")

val srcDir = settingKey[File]("The directory to copy changes to")
val changesDir = settingKey[File]("The directory to copy changes from")

srcDir := (ThisBuild / baseDirectory).value / "src" / "main" / "scala"
changesDir := (ThisBuild / baseDirectory).value / "changes"

prepareSources := IO.copyFile(changesDir.value / "zz.original.scala", srcDir.value / "a" / "zz.scala")
copyChanges := IO.copyFile(changesDir.value / "zz.new.scala", srcDir.value / "a" / "zz.scala")

(Compile / scalacOptions) ++= Seq(
"-sourcepath", (Compile / sourceDirectories).value.map(_.getAbsolutePath).distinct.mkString(java.io.File.pathSeparator),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package a

object Foo: // note that `Foo` is defined in `zz.scala`
class Local
inline def foo(using Local): Nothing =
???
???
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package a

object Foo: // note that `Foo` is defined in `zz.scala`
class Local
inline def foo(using Local): Nothing =
???
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.5.5
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package a

object Bar:
given Foo.Local()
def Bar = Foo.foo