Skip to content

Commit 1b2c3fb

Browse files
committed
Fix additional test case
Certain macros could return nodes typed with incorrect ThisTypes, which would reference module types outside of their scope. We remap those problematic nodes to TermRefs pointing to objects, and then possibly manually cast the returned node to the remapped type, as the ensureConforms method would just leave the previous incorrect type after confirming that the remapped type is a subtype of the previous incorrect one.
1 parent 8aa15e8 commit 1b2c3fb

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

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

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -574,17 +574,43 @@ object Inlines:
574574
val inlined = tpd.Inlined(call, bindings, expansion)
575575

576576
val hasOpaquesInResultFromCallWithTransparentContext =
577+
val owners = call.symbol.ownersIterator.toSet
577578
call.tpe.widenTermRefExpr.existsPart(
578-
part => part.typeSymbol.is(Opaque) && call.symbol.ownersIterator.contains(part.typeSymbol.owner)
579+
part => part.typeSymbol.is(Opaque) && owners.contains(part.typeSymbol.owner)
579580
)
580581

582+
/* Remap ThisType nodes that are incorrect in the inlined context.
583+
* Incorrect ThisType nodes can cause unwanted opaque type dealiasing later
584+
* See test i113461-d
585+
* */
586+
def fixThisTypeModuleClassReferences(tpe: Type): Type =
587+
val owners = ctx.owner.ownersIterator.toSet
588+
println("owners " + owners)
589+
TreeTypeMap(
590+
typeMap = new TypeMap:
591+
override def stopAt = StopAt.Package
592+
def apply(t: Type) = mapOver {
593+
t match
594+
case ThisType(tref @ TypeRef(prefix, _)) if tref.symbol.flags.is(Module) && !owners.contains(tref.symbol) =>
595+
TermRef(apply(prefix), tref.symbol.companionModule)
596+
case _ => mapOver(t)
597+
}
598+
).typeMap(tpe)
599+
581600
if !hasOpaqueProxies && !hasOpaquesInResultFromCallWithTransparentContext then inlined
582601
else
583-
val target =
602+
val (target, forceCast) =
584603
if inlinedMethod.is(Transparent) then
585-
call.tpe & unpackProxiesFromResultType(inlined)
586-
else call.tpe
587-
inlined.ensureConforms(target)
604+
val unpacked = unpackProxiesFromResultType(inlined)
605+
val withAdjustedThisTypes = if call.symbol.is(Macro) then fixThisTypeModuleClassReferences(unpacked) else unpacked
606+
(call.tpe & withAdjustedThisTypes, withAdjustedThisTypes != unpacked)
607+
else (call.tpe, false)
608+
if forceCast then
609+
// we need to force the cast for issues with ThisTypes, as ensureConforms will just
610+
// check subtyping and then choose not to cast, leaving the previous, incorrect type
611+
inlined.cast(target)
612+
else
613+
inlined.ensureConforms(target)
588614
// Make sure that the sealing with the declared type
589615
// is type correct. Without it we might get problems since the
590616
// expression's type is the opaque alias but the call's type is
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import scala.quoted.*
2+
opaque type MyOpaque = Int
3+
object MyOpaque:
4+
val one: MyOpaque = 1
5+
transparent inline def apply(): MyOpaque = ${ applyMacro }
6+
private def applyMacro(using Quotes): Expr[MyOpaque] =
7+
import quotes.reflect.*
8+
'{ one }
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
trait Leak[T]:
2+
type Out
3+
given [T]: Leak[T] with
4+
type Out = T
5+
extension [T](t: T)(using l: Leak[T]) def leak: l.Out = ???
6+
7+
val x = MyOpaque().leak
8+
val shouldWork = summon[x.type <:< MyOpaque]

0 commit comments

Comments
 (0)