Skip to content

Commit 55d8d63

Browse files
authored
Merge pull request #277 from scala/backport-lts-3.3-22655
Backport "Fix opaque types leaking rhs when inlined and found in type params (and a related stale symbol issue)" to 3.3 LTS
2 parents 923b826 + 504574d commit 55d8d63

File tree

7 files changed

+147
-2
lines changed

7 files changed

+147
-2
lines changed

compiler/src/dotty/tools/dotc/inlines/Inliner.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,17 @@ class Inliner(val call: tpd.Tree)(using Context):
399399
* type aliases, add proxy definitions to `opaqueProxies` that expose these aliases.
400400
*/
401401
private def addOpaqueProxies(tp: Type, span: Span, forThisProxy: Boolean)(using Context): Unit =
402-
tp.foreachPart {
402+
val foreachTpPart =
403+
(p: Type => Unit) =>
404+
if forThisProxy then
405+
// Performs operations on all parts of this type, outside of the applied type arguments
406+
new ForeachAccumulator(p, StopAt.None) {
407+
override def apply(x: Unit, tp: Type) = tp match
408+
case AppliedType(tycon, _) => super.apply(x, tycon)
409+
case other => super.apply(x, other)
410+
}.apply((), tp)
411+
else tp.foreachPart(p)
412+
foreachTpPart {
403413
case ref: TermRef =>
404414
for cls <- ref.widen.baseClasses do
405415
if cls.containsOpaques

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,13 @@ class Namer { typer: Typer =>
694694
enterSymbol(classConstructorCompanion(classSym.asClass))
695695
else
696696
for moduleSym <- companionVals do
697-
if moduleSym.is(Module) && !moduleSym.isDefinedInCurrentRun then
697+
// by not going through `.lastKnownDenotation` (instead using `.current`),
698+
// we guarantee that the `moduleSym` will be brought forward to the current run,
699+
// rendering `moduleSym.isDefinedInCurrentRun` as always true.
700+
// We want to regenerate the companion instead of bringing it forward,
701+
// as even if we are able to bring forward the object symbol,
702+
// we might not be able to do the same with its stale module class symbol (see `tests/pos/i20449`)
703+
if moduleSym.lastKnownDenotation.is(Module) && !moduleSym.isDefinedInCurrentRun then
698704
val companion =
699705
if needsConstructorProxies(classSym) then
700706
classConstructorCompanion(classSym.asClass)

tests/pos-macros/i20449/Macro.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import scala.quoted.*
2+
transparent inline def getTypeInfo[T]() = ${ getTypeInfoImpl[T] }
3+
def getTypeInfoImpl[T: Type](using ctx: Quotes): Expr[Unit] = '{ () }

tests/pos-macros/i20449/Main.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
class Wrapper1[A]
3+
val a = {
4+
getTypeInfo[Any]()
5+
val wrapper2 = Wrapper1[Any]()
6+
}

tests/run-macros/i20449.check

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
------ UserName.T - Directly -------
2+
Original: Main_2$package.UserName.T
3+
Dealias: Main_2$package.UserName.T
4+
Dealias dealias: Main_2$package.UserName.T
5+
6+
------ UserName.T - Directly -------
7+
Original: Main_2$package.UserName
8+
Dealias: Main_2$package.UserName.T
9+
Dealias dealias: Main_2$package.UserName.T
10+
11+
------ ForeignWrapper1[UserName.T] -------
12+
Original: Main_2$package.UserName.T
13+
Dealias: Main_2$package.UserName.T
14+
Dealias dealias: Main_2$package.UserName.T
15+
16+
------ ForeignWrapper2[UserName.T] -------
17+
Original: Main_2$package.UserName.T
18+
Dealias: Main_2$package.UserName.T
19+
Dealias dealias: Main_2$package.UserName.T
20+
21+
------ ForeignWrapper1[UserName] -------
22+
Original: Main_2$package.UserName
23+
Dealias: Main_2$package.UserName.T
24+
Dealias dealias: Main_2$package.UserName.T
25+
26+
------ ForeignWrapper2[UserName] -------
27+
Original: Main_2$package.UserName
28+
Dealias: Main_2$package.UserName.T
29+
Dealias dealias: Main_2$package.UserName.T
30+
31+
------ Wrapper1[UserName.T] -------
32+
Original: Main_2$package.UserName.T
33+
Dealias: Main_2$package.UserName.T
34+
Dealias dealias: Main_2$package.UserName.T
35+
36+
------ Wrapper2[UserName.T] -------
37+
Original: Main_2$package.UserName.T
38+
Dealias: Main_2$package.UserName.T
39+
Dealias dealias: Main_2$package.UserName.T
40+
41+
------ Wrapper1[UserName] -------
42+
Original: Main_2$package.UserName
43+
Dealias: Main_2$package.UserName.T
44+
Dealias dealias: Main_2$package.UserName.T
45+
46+
------ Wrapper2[UserName] -------
47+
Original: Main_2$package.UserName
48+
Dealias: Main_2$package.UserName.T
49+
Dealias dealias: Main_2$package.UserName.T
50+

tests/run-macros/i20449/Macro_1.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import scala.quoted.*
2+
3+
class ForeignWrapper1[-A] {
4+
inline def getTypeInfo(inline source: String): String =
5+
${ getTypeInfoImpl[A]('source) }
6+
def createWrapper2 = ForeignWrapper2(this)
7+
}
8+
9+
class ForeignWrapper2[-A](val self: ForeignWrapper1[A]) {
10+
inline def getTypeInfo(inline source: String): String =
11+
${getTypeInfoImpl[A]('source)}
12+
}
13+
14+
transparent inline def getTypeInfo[T](inline source: String) =
15+
${ getTypeInfoImpl[T]('source) }
16+
17+
def getTypeInfoImpl[T: Type](source: Expr[String])(using ctx: Quotes) : Expr[String] = {
18+
import ctx.reflect.*
19+
20+
val tpe = TypeRepr.of[T]
21+
val str =
22+
s"""|------ ${source.valueOrAbort} -------
23+
|Original: ${tpe.show}
24+
|Dealias: ${tpe.dealias.show}
25+
|Dealias dealias: ${tpe.dealias.dealias.show}
26+
""".stripMargin
27+
28+
Expr(str)
29+
}

tests/run-macros/i20449/Main_2.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
object UserName {
2+
opaque type T = String
3+
4+
def apply(s: String): T = s
5+
}
6+
7+
type UserName = UserName.T
8+
9+
class Wrapper1[-A] {
10+
inline def getTypeInfo(inline source: String): String =
11+
${ getTypeInfoImpl[A]('source) }
12+
def createWrapper2 = Wrapper2(this)
13+
}
14+
15+
class Wrapper2[-A](val self: Wrapper1[A]) {
16+
inline def getTypeInfo(inline source: String): String =
17+
${getTypeInfoImpl[A]('source)}
18+
}
19+
20+
21+
@main def Test() = {
22+
println(getTypeInfo[UserName.T]("UserName.T - Directly"))
23+
println(getTypeInfo[UserName]("UserName.T - Directly"))
24+
25+
val foreignWrapper = ForeignWrapper1[UserName.T]()
26+
println(foreignWrapper.getTypeInfo("ForeignWrapper1[UserName.T]"))
27+
println(foreignWrapper.createWrapper2.getTypeInfo("ForeignWrapper2[UserName.T]"))
28+
29+
val foreignWrapper2 = ForeignWrapper1[UserName]()
30+
println(foreignWrapper2.getTypeInfo("ForeignWrapper1[UserName]"))
31+
println(foreignWrapper2.createWrapper2.getTypeInfo("ForeignWrapper2[UserName]"))
32+
33+
val wrapper = Wrapper1[UserName.T]()
34+
println(wrapper.getTypeInfo("Wrapper1[UserName.T]"))
35+
println(wrapper.createWrapper2.getTypeInfo("Wrapper2[UserName.T]"))
36+
37+
val wrapper2 = Wrapper1[UserName]()
38+
println(wrapper2.getTypeInfo("Wrapper1[UserName]"))
39+
println(wrapper2.createWrapper2.getTypeInfo("Wrapper2[UserName]"))
40+
41+
}

0 commit comments

Comments
 (0)