Skip to content

Commit 86b59aa

Browse files
authored
Fix opaque types leaking rhs when inlined and found in type params (and a related stale symbol issue) (#22655)
This PR fixes the 2 issues found in #20449, split into 2 commits. The first commit fixes the stale symbol related issue found if the files from the issue minimization are compiled together. After suspending and retrying compilation, the classDefs that are defined directly in packages previously would sometimes not have companion objects regenerated, instead relying on the stale symbols from the previous run, causing them not to to pass the reallyExists check when looking for a specific ref. Now we make sure to go through lastKnwonDenotation, since the current one may not exists and may not point us to a Module flag when checking if to regenerate it. The second commit fixes the opaque type alias rhs leaking in a macro. That was caused by building proxies for all parts of the type, including type arguments to opaque types - from the perspective of a type like Object[OpaqueType], the opaque type rhs should not be visible.
2 parents 63a02dd + 23911df commit 86b59aa

File tree

7 files changed

+147
-2
lines changed

7 files changed

+147
-2
lines changed

Diff for: compiler/src/dotty/tools/dotc/inlines/Inliner.scala

+11-1
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

Diff for: compiler/src/dotty/tools/dotc/typer/Namer.scala

+7-1
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,13 @@ class Namer { typer: Typer =>
712712
enterSymbol(classConstructorCompanion(classSym.asClass))
713713
else
714714
for moduleSym <- companionVals do
715-
if moduleSym.is(Module) && !moduleSym.isDefinedInCurrentRun then
715+
// by not going through `.lastKnownDenotation` (instead using `.current`),
716+
// we guarantee that the `moduleSym` will be brought forward to the current run,
717+
// rendering `moduleSym.isDefinedInCurrentRun` as always true.
718+
// We want to regenerate the companion instead of bringing it forward,
719+
// as even if we are able to bring forward the object symbol,
720+
// we might not be able to do the same with its stale module class symbol (see `tests/pos/i20449`)
721+
if moduleSym.lastKnownDenotation.is(Module) && !moduleSym.isDefinedInCurrentRun then
716722
val companion =
717723
if needsConstructorProxies(classSym) then
718724
classConstructorCompanion(classSym.asClass)

Diff for: tests/pos-macros/i20449/Macro.scala

+3
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] = '{ () }

Diff for: tests/pos-macros/i20449/Main.scala

+6
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+
}

Diff for: tests/run-macros/i20449.check

+50
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+

Diff for: tests/run-macros/i20449/Macro_1.scala

+29
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+
}

Diff for: tests/run-macros/i20449/Main_2.scala

+41
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)