From 7fe6ea4c69140ab0fe7a7f1cf5c4243335aa703c Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Wed, 3 Sep 2025 16:18:27 +0200 Subject: [PATCH 1/2] Bring back previous typerState after suspending a compilation unit This is to avaoid unfulfilled constraints when going through -Ycheck later. --- compiler/src/dotty/tools/dotc/core/Phases.scala | 2 ++ tests/pos/i21176-b/Macro.scala | 9 +++++++++ tests/pos/i21176-b/Main.scala | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 tests/pos/i21176-b/Macro.scala create mode 100644 tests/pos/i21176-b/Main.scala diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index ee9ee4006919..c851e1a9e567 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -375,6 +375,7 @@ object Phases { val doCheckJava = skipIfJava && !isAfterLastJavaPhase for unit <- units do ctx.profiler.onUnit(this, unit): given unitCtx: Context = runCtx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports + val previousTyperState = unitCtx.typerState.snapshot() if ctx.run.enterUnit(unit) then try if doCheckJava && unit.typedAsJava then @@ -384,6 +385,7 @@ object Phases { buf += unitCtx.compilationUnit catch case _: CompilationUnit.SuspendException => // this unit will be run again in `Run#compileSuspendedUnits` + unitCtx.typerState.resetTo(previousTyperState) case ex: Throwable if !ctx.run.enrichedErrorMessage => println(ctx.run.enrichErrorMessage(s"unhandled exception while running $phaseName on $unit")) throw ex diff --git a/tests/pos/i21176-b/Macro.scala b/tests/pos/i21176-b/Macro.scala new file mode 100644 index 000000000000..34e63d610bd5 --- /dev/null +++ b/tests/pos/i21176-b/Macro.scala @@ -0,0 +1,9 @@ +import scala.quoted.* + +class Macro: + inline def nameTuple[NameTuple_T]: (String, List[NameTuple_T]) = Macro.tuple[NameTuple_T](Macro.named) + +object Macro: + def namedMacro(using q: Quotes): Expr[String] = Expr("test") + inline def named: String = ${Macro.namedMacro} + def tuple[Tuple_T](name: String): (String, List[Tuple_T]) = (name, List.empty[Tuple_T]) diff --git a/tests/pos/i21176-b/Main.scala b/tests/pos/i21176-b/Main.scala new file mode 100644 index 000000000000..561f43b32dda --- /dev/null +++ b/tests/pos/i21176-b/Main.scala @@ -0,0 +1,2 @@ +class Test extends Macro: + val abc = nameTuple[Int] From 4fdb6a3caf50d82ad3ac00267fb4ca3067d5d0c8 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Wed, 3 Sep 2025 17:07:05 +0200 Subject: [PATCH 2/2] Do not update Symbol defTrees when retyping after Inlining The scaladoc for the defTree method in Symbol states: "The tree defining the symbol at pickler time ...", but that was never really true. Previously, since the setDefTree method was used in Typer, in any retyping procedure those methods would be called again, with the main example being erasure, where we would lose parts of type information from those trees. Usually this was not an issue, since they weren't used after Erasure. However, the suspend mechanism used when compiling macros with calls to them can cause the macro tree to go through erasure, have their defTrees updated there, and then used for earlier phases, with the problematic phase here being the init-checker, which uses defTree calls extensively. In the issue minimisation, init-checker was expecting a MethodType (due to the missing TypeApply there), and instead got a PolyType, causing a crash. Coincidentally in the past we would sometimes also get issues about .tree method in Quotes returning unexpected stuff due to the same issue, so this should also help there. --- compiler/src/dotty/tools/dotc/core/Contexts.scala | 1 + compiler/src/dotty/tools/dotc/core/Phases.scala | 2 ++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 5 +++-- tests/pos-macros/i22584/Macro.scala | 2 +- tests/pos/i21176-a/Macro.scala | 12 ++++++++++++ tests/pos/i21176-a/Main.scala | 7 +++++++ 6 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i21176-a/Macro.scala create mode 100644 tests/pos/i21176-a/Main.scala diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 5a0e03330ef2..0d1a579de036 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -367,6 +367,7 @@ object Contexts { /** Is current phase after TyperPhase? */ final def isAfterTyper = base.isAfterTyper(phase) + final def isAfterInlining = base.isAfterInlining(phase) final def isTyper = base.isTyper(phase) /** Is this a context for the members of a class definition? */ diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index c851e1a9e567..ba9ae4b8d96b 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -305,6 +305,8 @@ object Phases { } final def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id + final def isAfterInlining(phase: Phase): Boolean = + inliningPhase != NoPhase && phase.id > inliningPhase.id final def isTyper(phase: Phase): Boolean = phase.id == typerPhase.id } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 5e919a660579..1a82eed2de69 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2974,7 +2974,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typedExpr(_, tpt1.tpe.widenExpr) val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym) postProcessInfo(vdef1, sym) - vdef1.setDefTree + if (!ctx.isAfterInlining) vdef1.setDefTree migrate(ImplicitToGiven.valDef(vdef1)) @@ -3097,7 +3097,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (!sym.isOneOf(Synthetic | InlineProxy | Param) && sym.info.finalResultType.isRepeatedParam) report.error(em"Cannot return repeated parameter type ${sym.info.finalResultType}", sym.srcPos) mdef.ensureHasSym(sym) - mdef.setDefTree + if (!ctx.isAfterInlining) mdef.setDefTree + else mdef def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = ctx.profiler.onTypedDef(sym) { val TypeDef(name, rhs) = tdef diff --git a/tests/pos-macros/i22584/Macro.scala b/tests/pos-macros/i22584/Macro.scala index f0b9c7409520..46d4d39565de 100644 --- a/tests/pos-macros/i22584/Macro.scala +++ b/tests/pos-macros/i22584/Macro.scala @@ -22,7 +22,7 @@ object Macros { } } - val expected = "List(DefDef(boolean,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Boolean)],Select(This(Ident(MyClass1)),boolean)), DefDef(finalVal,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)],Select(This(Ident(MyClass1)),finalVal)), DefDef(int,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Int)],Select(This(Ident(MyClass1)),int)), DefDef(string,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)],Select(This(Ident(MyClass1)),string)))" + val expected = "List(ValDef(boolean,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),class Boolean)],EmptyTree), ValDef(finalVal,Ident(String),Literal(Constant(result))), ValDef(int,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),class Int)],EmptyTree), ValDef(string,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class scala)),object Predef),type String)],EmptyTree))" assert(caseFieldValOrDefDefs.toString == expected) '{ () } diff --git a/tests/pos/i21176-a/Macro.scala b/tests/pos/i21176-a/Macro.scala new file mode 100644 index 000000000000..bd937f6292ac --- /dev/null +++ b/tests/pos/i21176-a/Macro.scala @@ -0,0 +1,12 @@ +//> using options -Wsafe-init +import scala.quoted.* + +class Macro: + def tuple[T](name: String): (String, List[T]) = (name, List[T]()) + inline def nameTuple[T]: (String, List[T]) = tuple(Macro.named) + +object Macro: + def namedMacro(using q: Quotes): Expr[String] = + Expr("test") + + inline def named: String = ${Macro.namedMacro} diff --git a/tests/pos/i21176-a/Main.scala b/tests/pos/i21176-a/Main.scala new file mode 100644 index 000000000000..3af179c4b698 --- /dev/null +++ b/tests/pos/i21176-a/Main.scala @@ -0,0 +1,7 @@ +//> using options -Wsafe-init +class Test extends Macro: + val abc = nameTuple[Int] + +@main +def run(): Unit = + println(new Test().abc)