From 7ea74c0d5ef0e96e2e397340270de3145324e978 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 8 Mar 2021 18:22:09 +0100 Subject: [PATCH 1/6] Eliminate stopAfterParser flag for compileForErrors It was set so that `typeChecks` would never try typechecking, but that is not backed by the docs. --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index d9dff219565f..f538f3eea537 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -293,7 +293,7 @@ object Inliner { private enum ErrorKind: case Parser, Typer - private def compileForErrors(tree: Tree, stopAfterParser: Boolean)(using Context): List[(ErrorKind, Error)] = + private def compileForErrors(tree: Tree)(using Context): List[(ErrorKind, Error)] = assert(tree.symbol == defn.CompiletimeTesting_typeChecks || tree.symbol == defn.CompiletimeTesting_typeCheckErrors) def stripTyped(t: Tree): Tree = t match { case Typed(t2, _) => stripTyped(t2) @@ -317,7 +317,7 @@ object Inliner { val parseErrors = ctx2.reporter.allErrors.toList res ++= parseErrors.map(e => ErrorKind.Parser -> e) - if !stopAfterParser || res.isEmpty then + if res.isEmpty then ctx2.typer.typed(tree2)(using ctx2) val typerErrors = ctx2.reporter.allErrors.filterNot(parseErrors.contains) res ++= typerErrors.map(e => ErrorKind.Typer -> e) @@ -346,12 +346,12 @@ object Inliner { /** Expand call to scala.compiletime.testing.typeChecks */ def typeChecks(tree: Tree)(using Context): Tree = - val errors = compileForErrors(tree, true) + val errors = compileForErrors(tree) Literal(Constant(errors.isEmpty)).withSpan(tree.span) /** Expand call to scala.compiletime.testing.typeCheckErrors */ def typeCheckErrors(tree: Tree)(using Context): Tree = - val errors = compileForErrors(tree, false) + val errors = compileForErrors(tree) packErrors(errors) /** Expand call to scala.compiletime.codeOf */ From 5670a3f77113a638add42a8cd22c14b5029fe52d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 8 Mar 2021 18:29:56 +0100 Subject: [PATCH 2/6] Also run PostTyper and Inliner from completime.typechecks For the moment we specialize this to PostTyper and Inliner. We could run more phases (e.g. all phases up to erasure) but that would require a different infrastructure where we encapsulate the parsed string in a compilation unit. --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 2 +- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 7 ++++++- tests/run-macros/i11630.scala | 9 +++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 tests/run-macros/i11630.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 5db2afdacd8d..7b6a45ce3cda 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -67,7 +67,7 @@ class Inlining extends MacroTransform { case _ => } - protected def newTransformer(using Context): Transformer = new Transformer { + def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = new InliningTreeMap().transform(tree) } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index ca442d379ec5..96e5b2ecae3a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -70,7 +70,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase override def transformPhase(using Context): Phase = thisPhase.next - protected def newTransformer(using Context): Transformer = + def newTransformer(using Context): Transformer = new PostTyperTransformer val superAcc: SuperAccessors = new SuperAccessors(thisPhase) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index f538f3eea537..c028845c364b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -25,6 +25,7 @@ import ErrorReporting.errorTree import dotty.tools.dotc.util.{SimpleIdentityMap, SimpleIdentitySet, EqHashMap, SourceFile, SourcePosition, SrcPos} import dotty.tools.dotc.parsing.Parsers.Parser import Nullables._ +import transform.{PostTyper, Inlining} import collection.mutable import reporting.trace @@ -318,7 +319,11 @@ object Inliner { val parseErrors = ctx2.reporter.allErrors.toList res ++= parseErrors.map(e => ErrorKind.Parser -> e) if res.isEmpty then - ctx2.typer.typed(tree2)(using ctx2) + val tree3 = ctx2.typer.typed(tree2)(using ctx2) + val postTyper = ctx.base.postTyperPhase.asInstanceOf[PostTyper] + val tree4 = postTyper.newTransformer.transform(tree3)(using ctx2.withPhase(postTyper)) + val inlining = ctx.base.inliningPhase.asInstanceOf[Inlining] + inlining.newTransformer.transform(tree4)(using ctx2.withPhase(inlining)) val typerErrors = ctx2.reporter.allErrors.filterNot(parseErrors.contains) res ++= typerErrors.map(e => ErrorKind.Typer -> e) res.toList diff --git a/tests/run-macros/i11630.scala b/tests/run-macros/i11630.scala new file mode 100644 index 000000000000..81ab7911b891 --- /dev/null +++ b/tests/run-macros/i11630.scala @@ -0,0 +1,9 @@ +inline def failme1() = compiletime.error("fail") +transparent inline def failme2() = compiletime.error("fail") + +@main def Test = + assert(!compiletime.testing.typeChecks("failme1()")) + assert(!compiletime.testing.typeChecks("failme2()")) + assert(!compiletime.testing.typeChecks("a b c")) + assert(!compiletime.testing.typeChecks("true: Int")) + From a5b4af1db7efb69bf04fee3e87ea0d51ca6914a1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 8 Mar 2021 18:45:38 +0100 Subject: [PATCH 3/6] Avoid crashes when a phase is missing This happens in pickler tests, where the Inlining phase does not exist. --- .../src/dotty/tools/dotc/typer/Inliner.scala | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index c028845c364b..51cd29eea230 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -312,21 +312,26 @@ object Inliner { ConstFold(underlyingCodeArg).tpe.widenTermRefExpr match { case ConstantType(Constant(code: String)) => val source2 = SourceFile.virtual("tasty-reflect", code) - val ctx2 = ctx.fresh.setNewTyperState().setTyper(new Typer).setSource(source2) - val tree2 = new Parser(source2)(using ctx2).block() - val res = collection.mutable.ListBuffer.empty[(ErrorKind, Error)] - - val parseErrors = ctx2.reporter.allErrors.toList - res ++= parseErrors.map(e => ErrorKind.Parser -> e) - if res.isEmpty then - val tree3 = ctx2.typer.typed(tree2)(using ctx2) - val postTyper = ctx.base.postTyperPhase.asInstanceOf[PostTyper] - val tree4 = postTyper.newTransformer.transform(tree3)(using ctx2.withPhase(postTyper)) - val inlining = ctx.base.inliningPhase.asInstanceOf[Inlining] - inlining.newTransformer.transform(tree4)(using ctx2.withPhase(inlining)) - val typerErrors = ctx2.reporter.allErrors.filterNot(parseErrors.contains) - res ++= typerErrors.map(e => ErrorKind.Typer -> e) - res.toList + inContext(ctx.fresh.setNewTyperState().setTyper(new Typer).setSource(source2)) { + val tree2 = new Parser(source2).block() + val res = collection.mutable.ListBuffer.empty[(ErrorKind, Error)] + + val parseErrors = ctx.reporter.allErrors.toList + res ++= parseErrors.map(e => ErrorKind.Parser -> e) + if res.isEmpty then + val tree3 = ctx.typer.typed(tree2) + ctx.base.postTyperPhase match + case postTyper: PostTyper => + val tree4 = atPhase(postTyper) { postTyper.newTransformer.transform(tree3) } + ctx.base.inliningPhase match + case inlining: Inlining => + val tree5 = atPhase(inlining) { inlining.newTransformer.transform(tree4) } + case _ => + case _ => + val typerErrors = ctx.reporter.allErrors.filterNot(parseErrors.contains) + res ++= typerErrors.map(e => ErrorKind.Typer -> e) + res.toList + } case t => report.error(em"argument to compileError must be a statically known String but was: $codeArg", codeArg1.srcPos) Nil From 263c815ae6bb4e31b52501ac7ba639d3ef9c7fe6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 8 Mar 2021 18:59:56 +0100 Subject: [PATCH 4/6] Move test and add return type There's a weird interaction with typechecks that means we need an explicit return type here. --- tests/{run-macros => run}/i11630.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename tests/{run-macros => run}/i11630.scala (92%) diff --git a/tests/run-macros/i11630.scala b/tests/run/i11630.scala similarity index 92% rename from tests/run-macros/i11630.scala rename to tests/run/i11630.scala index 81ab7911b891..b11ec4e7635e 100644 --- a/tests/run-macros/i11630.scala +++ b/tests/run/i11630.scala @@ -1,9 +1,9 @@ inline def failme1() = compiletime.error("fail") transparent inline def failme2() = compiletime.error("fail") -@main def Test = +@main def Test: Unit = { assert(!compiletime.testing.typeChecks("failme1()")) assert(!compiletime.testing.typeChecks("failme2()")) assert(!compiletime.testing.typeChecks("a b c")) assert(!compiletime.testing.typeChecks("true: Int")) - +} From 8d164a1cc6c66a90387adb2cf99fb046820be6f5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 9 Mar 2021 11:11:45 +0100 Subject: [PATCH 5/6] Stop trying more phases after errors in typeChecks --- .../src/dotty/tools/dotc/typer/Inliner.scala | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 51cd29eea230..81b26246ff85 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -314,23 +314,19 @@ object Inliner { val source2 = SourceFile.virtual("tasty-reflect", code) inContext(ctx.fresh.setNewTyperState().setTyper(new Typer).setSource(source2)) { val tree2 = new Parser(source2).block() - val res = collection.mutable.ListBuffer.empty[(ErrorKind, Error)] - - val parseErrors = ctx.reporter.allErrors.toList - res ++= parseErrors.map(e => ErrorKind.Parser -> e) - if res.isEmpty then + if ctx.reporter.allErrors.nonEmpty then + ctx.reporter.allErrors.map((ErrorKind.Parser, _)) + else val tree3 = ctx.typer.typed(tree2) ctx.base.postTyperPhase match - case postTyper: PostTyper => + case postTyper: PostTyper if ctx.reporter.allErrors.isEmpty => val tree4 = atPhase(postTyper) { postTyper.newTransformer.transform(tree3) } ctx.base.inliningPhase match - case inlining: Inlining => - val tree5 = atPhase(inlining) { inlining.newTransformer.transform(tree4) } + case inlining: Inlining if ctx.reporter.allErrors.isEmpty => + atPhase(inlining) { inlining.newTransformer.transform(tree4) } case _ => case _ => - val typerErrors = ctx.reporter.allErrors.filterNot(parseErrors.contains) - res ++= typerErrors.map(e => ErrorKind.Typer -> e) - res.toList + ctx.reporter.allErrors.map((ErrorKind.Typer, _)) } case t => report.error(em"argument to compileError must be a statically known String but was: $codeArg", codeArg1.srcPos) From 86e8fb7174e2cfca42067e8be1ccd7dcc9250cd1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 9 Mar 2021 12:59:55 +0100 Subject: [PATCH 6/6] Fix munit expect string --- community-build/community-projects/munit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/munit b/community-build/community-projects/munit index 3c9b71d7a087..7006d9d3963c 160000 --- a/community-build/community-projects/munit +++ b/community-build/community-projects/munit @@ -1 +1 @@ -Subproject commit 3c9b71d7a087015e95411534aaaf6d92cbbbfbc3 +Subproject commit 7006d9d3963c0f1db5594e4a054a3531ee781c9a