From 29b12b4a3646699dafdb15a6023143924ec527c2 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 5 Jul 2022 21:41:21 +0200 Subject: [PATCH 1/2] Fix #15374: Make sure prefix of outer select has the correct class symbol If we have an outer select `e.outer_`, we must make sure that the class symbol of `e` is the class where the outer this is located in. Otherwise, the phase `ElimOuterSelect` would get confused. Co-authored-by: Dale Wijnand --- .../src/dotty/tools/dotc/inlines/Inliner.scala | 18 ++++++++++++------ tests/run/i15374.scala | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 tests/run/i15374.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 3f60480f3ffd..08316093d911 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -275,12 +275,18 @@ class Inliner(val call: tpd.Tree)(using Context): // All needed this-proxies, paired-with and sorted-by nesting depth of // the classes they represent (innermost first) val sortedProxies = thisProxy.toList - .map((cls, proxy) => (cls.ownersIterator.length, proxy.symbol)) + .map((cls, proxy) => (cls.ownersIterator.length, proxy.symbol, cls)) .sortBy(-_._1) + def outerSelect(prefix: Symbol, prefixCls: Symbol, level: Int, info: Type) = + val tpt = TypeTree(adaptToPrefix(prefixCls.appliedRef)) + val qual = Typed(ref(prefix), tpt) + qual.outerSelect(level, info) + var lastSelf: Symbol = NoSymbol + var lastCls: Symbol = NoSymbol var lastLevel: Int = 0 - for ((level, selfSym) <- sortedProxies) { + for ((level, selfSym, cls) <- sortedProxies) { val rhs = selfSym.info.dealias match case info: TermRef if info.isStable && (lastSelf.exists || isPureExpr(inlineCallPrefix)) => @@ -292,7 +298,7 @@ class Inliner(val call: tpd.Tree)(using Context): if rhsClsSym.is(Module) && rhsClsSym.isStatic then ref(rhsClsSym.sourceModule) else if lastSelf.exists then - ref(lastSelf).outerSelect(lastLevel - level, selfSym.info) + outerSelect(lastSelf, lastCls, lastLevel - level, selfSym.info) else val pre = inlineCallPrefix match case Super(qual, _) => qual @@ -307,6 +313,7 @@ class Inliner(val call: tpd.Tree)(using Context): inlining.println(i"proxy at $level: $selfSym = ${bindingsBuf.last}") lastSelf = selfSym lastLevel = level + lastCls = cls } } @@ -417,6 +424,8 @@ class Inliner(val call: tpd.Tree)(using Context): || tpe.cls.is(Package) || tpe.cls.isStaticOwner && !(tpe.cls.seesOpaques && inlinedMethod.isContainedIn(tpe.cls)) + private def adaptToPrefix(tp: Type) = tp.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) + /** Populate `thisProxy` and `paramProxy` as follows: * * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference, @@ -432,14 +441,11 @@ class Inliner(val call: tpd.Tree)(using Context): private def registerType(tpe: Type): Unit = tpe match { case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) => val proxyName = s"${tpe.cls.name}_this".toTermName - def adaptToPrefix(tp: Type) = tp.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) val proxyType = inlineCallPrefix.tpe.dealias.tryNormalize match { case typeMatchResult if typeMatchResult.exists => typeMatchResult case _ => adaptToPrefix(tpe).widenIfUnstable } thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef - if (!tpe.cls.isStaticOwner) - registerType(inlinedMethod.owner.thisType) // make sure we have a base from which to outer-select for (param <- tpe.cls.typeParams) paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) case tpe: NamedType diff --git a/tests/run/i15374.scala b/tests/run/i15374.scala new file mode 100644 index 000000000000..8d4e66d52263 --- /dev/null +++ b/tests/run/i15374.scala @@ -0,0 +1,18 @@ +class A(val x: Int): + class X: + inline def foo() = A.this.foo() + + private def foo(): Int = x + +class B extends A(20): + val a = new A(10) + val y: Y = new Y + + class Y extends a.X + +class C: + var b = new B + assert(b.y.foo() == 10) + +@main +def Test = new C() From ad698a5c98e8d3ec36e52697eed111192e2cca93 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 5 Jul 2022 22:30:39 +0200 Subject: [PATCH 2/2] Fix another outerSelect --- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 08316093d911..153ceaaee9e9 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -278,10 +278,10 @@ class Inliner(val call: tpd.Tree)(using Context): .map((cls, proxy) => (cls.ownersIterator.length, proxy.symbol, cls)) .sortBy(-_._1) - def outerSelect(prefix: Symbol, prefixCls: Symbol, level: Int, info: Type) = + def outerSelect(prefix: Tree, prefixCls: Symbol, hops: Int, info: Type) = val tpt = TypeTree(adaptToPrefix(prefixCls.appliedRef)) - val qual = Typed(ref(prefix), tpt) - qual.outerSelect(level, info) + val qual = Typed(prefix, tpt) + qual.outerSelect(hops, info) var lastSelf: Symbol = NoSymbol var lastCls: Symbol = NoSymbol @@ -298,13 +298,13 @@ class Inliner(val call: tpd.Tree)(using Context): if rhsClsSym.is(Module) && rhsClsSym.isStatic then ref(rhsClsSym.sourceModule) else if lastSelf.exists then - outerSelect(lastSelf, lastCls, lastLevel - level, selfSym.info) + outerSelect(ref(lastSelf), lastCls, lastLevel - level, selfSym.info) else val pre = inlineCallPrefix match case Super(qual, _) => qual case pre => pre val preLevel = inlinedMethod.owner.ownersIterator.length - if preLevel > level then pre.outerSelect(preLevel - level, selfSym.info) + if preLevel > level then outerSelect(pre, inlinedMethod.owner, preLevel - level, selfSym.info) else pre val binding = accountForOpaques(