From b5a31512de1352be73df52612e13d422b19f5eff Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Mon, 24 Feb 2025 15:49:49 +0100 Subject: [PATCH 1/3] Fix stale symbol crash on classes with package owner --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 2 +- tests/pos-macros/i20449/Macro.scala | 3 +++ tests/pos-macros/i20449/Main.scala | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/i20449/Macro.scala create mode 100644 tests/pos-macros/i20449/Main.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index bc4e1a332ff6..010e5114dab1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -712,7 +712,7 @@ class Namer { typer: Typer => enterSymbol(classConstructorCompanion(classSym.asClass)) else for moduleSym <- companionVals do - if moduleSym.is(Module) && !moduleSym.isDefinedInCurrentRun then + if moduleSym.lastKnownDenotation.is(Module) && !moduleSym.isDefinedInCurrentRun then val companion = if needsConstructorProxies(classSym) then classConstructorCompanion(classSym.asClass) diff --git a/tests/pos-macros/i20449/Macro.scala b/tests/pos-macros/i20449/Macro.scala new file mode 100644 index 000000000000..56c9a625e894 --- /dev/null +++ b/tests/pos-macros/i20449/Macro.scala @@ -0,0 +1,3 @@ +import scala.quoted.* +transparent inline def getTypeInfo[T]() = ${ getTypeInfoImpl[T] } +def getTypeInfoImpl[T: Type](using ctx: Quotes): Expr[Unit] = '{ () } diff --git a/tests/pos-macros/i20449/Main.scala b/tests/pos-macros/i20449/Main.scala new file mode 100644 index 000000000000..7c1a71c5598e --- /dev/null +++ b/tests/pos-macros/i20449/Main.scala @@ -0,0 +1,6 @@ + +class Wrapper1[A] +val a = { + getTypeInfo[Any]() + val wrapper2 = Wrapper1[Any]() +} \ No newline at end of file From 09f13d58e169c957b4bb0c19722f1730dc208f63 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Mon, 24 Feb 2025 15:52:09 +0100 Subject: [PATCH 2/3] Fix opaque type implementations being exposed when in type params --- .../dotty/tools/dotc/inlines/Inliner.scala | 12 ++++- tests/pos-macros/i20449/Main.scala | 2 +- tests/run-macros/i20449.check | 50 +++++++++++++++++++ tests/run-macros/i20449/Macro_1.scala | 29 +++++++++++ tests/run-macros/i20449/Main_2.scala | 41 +++++++++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 tests/run-macros/i20449.check create mode 100644 tests/run-macros/i20449/Macro_1.scala create mode 100644 tests/run-macros/i20449/Main_2.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 27d95d055f40..4e125f7dd649 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -399,7 +399,17 @@ class Inliner(val call: tpd.Tree)(using Context): * type aliases, add proxy definitions to `opaqueProxies` that expose these aliases. */ private def addOpaqueProxies(tp: Type, span: Span, forThisProxy: Boolean)(using Context): Unit = - tp.foreachPart { + val foreachTpPart = + (p: Type => Unit) => + if forThisProxy then + // Performs operations on all parts of this type, outside of the applied type arguments + new ForeachAccumulator(p, StopAt.None) { + override def apply(x: Unit, tp: Type) = tp match + case AppliedType(tycon, _) => super.apply(x, tycon) + case other => super.apply(x, other) + }.apply((), tp) + else tp.foreachPart(p) + foreachTpPart { case ref: TermRef => for cls <- ref.widen.baseClasses do if cls.containsOpaques diff --git a/tests/pos-macros/i20449/Main.scala b/tests/pos-macros/i20449/Main.scala index 7c1a71c5598e..c6f5c0b05b69 100644 --- a/tests/pos-macros/i20449/Main.scala +++ b/tests/pos-macros/i20449/Main.scala @@ -3,4 +3,4 @@ class Wrapper1[A] val a = { getTypeInfo[Any]() val wrapper2 = Wrapper1[Any]() -} \ No newline at end of file +} diff --git a/tests/run-macros/i20449.check b/tests/run-macros/i20449.check new file mode 100644 index 000000000000..289b3f95c488 --- /dev/null +++ b/tests/run-macros/i20449.check @@ -0,0 +1,50 @@ +------ UserName.T - Directly ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ UserName.T - Directly ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ ForeignWrapper1[UserName.T] ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ ForeignWrapper2[UserName.T] ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ ForeignWrapper1[UserName] ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ ForeignWrapper2[UserName] ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ Wrapper1[UserName.T] ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ Wrapper2[UserName.T] ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ Wrapper1[UserName] ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ Wrapper2[UserName] ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + diff --git a/tests/run-macros/i20449/Macro_1.scala b/tests/run-macros/i20449/Macro_1.scala new file mode 100644 index 000000000000..74b1dce3c4d6 --- /dev/null +++ b/tests/run-macros/i20449/Macro_1.scala @@ -0,0 +1,29 @@ +import scala.quoted.* + +class ForeignWrapper1[-A] { + inline def getTypeInfo(inline source: String): String = + ${ getTypeInfoImpl[A]('source) } + def createWrapper2 = ForeignWrapper2(this) +} + +class ForeignWrapper2[-A](val self: ForeignWrapper1[A]) { + inline def getTypeInfo(inline source: String): String = + ${getTypeInfoImpl[A]('source)} +} + +transparent inline def getTypeInfo[T](inline source: String) = + ${ getTypeInfoImpl[T]('source) } + +def getTypeInfoImpl[T: Type](source: Expr[String])(using ctx: Quotes) : Expr[String] = { + import ctx.reflect.* + + val tpe = TypeRepr.of[T] + val str = + s"""|------ ${source.valueOrAbort} ------- + |Original: ${tpe.show} + |Dealias: ${tpe.dealias.show} + |Dealias dealias: ${tpe.dealias.dealias.show} + """.stripMargin + + Expr(str) +} diff --git a/tests/run-macros/i20449/Main_2.scala b/tests/run-macros/i20449/Main_2.scala new file mode 100644 index 000000000000..3d2991489c1b --- /dev/null +++ b/tests/run-macros/i20449/Main_2.scala @@ -0,0 +1,41 @@ +object UserName { + opaque type T = String + + def apply(s: String): T = s +} + +type UserName = UserName.T + +class Wrapper1[-A] { + inline def getTypeInfo(inline source: String): String = + ${ getTypeInfoImpl[A]('source) } + def createWrapper2 = Wrapper2(this) +} + +class Wrapper2[-A](val self: Wrapper1[A]) { + inline def getTypeInfo(inline source: String): String = + ${getTypeInfoImpl[A]('source)} +} + + +@main def Test() = { + println(getTypeInfo[UserName.T]("UserName.T - Directly")) + println(getTypeInfo[UserName]("UserName.T - Directly")) + + val foreignWrapper = ForeignWrapper1[UserName.T]() + println(foreignWrapper.getTypeInfo("ForeignWrapper1[UserName.T]")) + println(foreignWrapper.createWrapper2.getTypeInfo("ForeignWrapper2[UserName.T]")) + + val foreignWrapper2 = ForeignWrapper1[UserName]() + println(foreignWrapper2.getTypeInfo("ForeignWrapper1[UserName]")) + println(foreignWrapper2.createWrapper2.getTypeInfo("ForeignWrapper2[UserName]")) + + val wrapper = Wrapper1[UserName.T]() + println(wrapper.getTypeInfo("Wrapper1[UserName.T]")) + println(wrapper.createWrapper2.getTypeInfo("Wrapper2[UserName.T]")) + + val wrapper2 = Wrapper1[UserName]() + println(wrapper2.getTypeInfo("Wrapper1[UserName]")) + println(wrapper2.createWrapper2.getTypeInfo("Wrapper2[UserName]")) + +} From 23911dfbbf1f92e41d6d43e50d1216889841858a Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Mon, 3 Mar 2025 14:15:49 +0100 Subject: [PATCH 3/3] Add a comment explaining the stale symbol fix --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 010e5114dab1..3df25e06a3b1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -712,6 +712,12 @@ class Namer { typer: Typer => enterSymbol(classConstructorCompanion(classSym.asClass)) else for moduleSym <- companionVals do + // by not going through `.lastKnownDenotation` (instead using `.current`), + // we guarantee that the `moduleSym` will be brought forward to the current run, + // rendering `moduleSym.isDefinedInCurrentRun` as always true. + // We want to regenerate the companion instead of bringing it forward, + // as even if we are able to bring forward the object symbol, + // we might not be able to do the same with its stale module class symbol (see `tests/pos/i20449`) if moduleSym.lastKnownDenotation.is(Module) && !moduleSym.isDefinedInCurrentRun then val companion = if needsConstructorProxies(classSym) then