From fc4a38f85163fa75db5c399b71ca6db96254c277 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 13 Jul 2019 11:44:49 +0200 Subject: [PATCH 1/7] Partial fix for inlining with opaque types When compiling shapeless, we saw an error message ``` [error] -- [E007] Type Mismatch Error: /home/miles/projects/shapeless-ng/core/src/test/scala/shapeless/type-classes.scala:53:80 [error] 53 | inline def derived[A] given (gen: K0.ProductGeneric[A]): Monoid[A] = monoidGen [error] | ^ [error] |Found: shapeless.K0.ProductInstances[shapeless.Monoid, shapeless.ISB] [error] |Required: shapeless.K0.ProductInstances[shapeless.Monoid, shapeless.ISB] ``` With -Yprint-debug that became clearer: ``` |Found: K0.this.ProductInstances[[A] => shapeless.this.Monoid[A], shapeless.this.ISB] |Required: shapeless.this.K0.ProductInstances[[A] => shapeless.this.Monoid[A], ``` This commit ensures that the type K0.this.ProductInstances does not show up outside of `K0`. There were two places in the inliner where `M.this` for static `M` was kept as is. Normally that's OK, but it does matter if `M` contains opaque types. This is a partial fix only. Compared to the original shapeless code, test pos/shapeless.scala also contains two casts (on lines 65 and 68), which would lead to inlining failures if missing. Unfortunately, this second problem looks impossible to fix in general. We cannot simply move code compiled inside an opaque alias scope to outside this scope. The compiler can try to insert some casts to make it work, but I see no general proven way to do so, because opaque types are explained through type abstraction instead of through implicit conversions. --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 2452081bf98c..cf2f56b2d17d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -331,7 +331,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { private def canElideThis(tpe: ThisType): Boolean = inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(tpe.cls) || tpe.cls.isContainedIn(inlinedMethod) || - tpe.cls.is(Package) + !tpe.cls.membersNeedAsSeenFrom(inlineCallPrefix.tpe) /** Populate `thisProxy` and `paramProxy` as follows: * @@ -354,7 +354,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { case _ => adaptToPrefix(tpe).widenIfUnstable } thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef - if (!tpe.cls.isStaticOwner) + if (tpe.cls.membersNeedAsSeenFrom(proxyType)) 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) @@ -367,7 +367,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { /** Register type of leaf node */ private def registerLeaf(tree: Tree): Unit = tree match { case _: This | _: Ident | _: TypeTree => - tree.tpe.foreachPart(registerType, stopAtStatic = true) + tree.tpe.foreachPart(registerType) case _ => } @@ -427,9 +427,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { val inliner = new TreeTypeMap( typeMap = new TypeMap { + override val stopAtStatic = false def apply(t: Type) = t match { case t: ThisType => thisProxy.getOrElse(t.cls, t) case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) + case t: TermRef if t.symbol.is(Package) => t case t: SingletonType => paramProxy.getOrElse(t, mapOver(t)) case t => mapOver(t) } From 5ebef18f50f74bd7f7aa72127bf4fb587c9cd65f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 13 Jul 2019 12:23:09 +0200 Subject: [PATCH 2/7] Fix initialization of this-proxies for objects containing opaque types --- .../src/dotty/tools/dotc/typer/Inliner.scala | 2 +- tests/pos/shapeless.scala | 265 ++++++++++++++++++ 2 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 tests/pos/shapeless.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index cf2f56b2d17d..359aa1602704 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -320,7 +320,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { ref(rhsClsSym.sourceModule) else inlineCallPrefix - val binding = ValDef(selfSym.asTerm, rhs).withSpan(selfSym.span).setDefTree + val binding = ValDef(selfSym.asTerm, rhs.ensureConforms(selfSym.info)).withSpan(selfSym.span).setDefTree bindingsBuf += binding inlining.println(i"proxy at $level: $selfSym = ${bindingsBuf.last}") lastSelf = selfSym diff --git a/tests/pos/shapeless.scala b/tests/pos/shapeless.scala new file mode 100644 index 000000000000..0f40265eb9e9 --- /dev/null +++ b/tests/pos/shapeless.scala @@ -0,0 +1,265 @@ +package test.shapeless { + +import scala.collection.mutable.WrappedArray +import scala.compiletime._ +import scala.deriving._ +import annotation.tailrec + +object K0 { + type Generic[O] = Mirror { type MirroredType = O ; type MirroredElemTypes } + type ProductGeneric[O] = Mirror.Product { type MirroredType = O ; type MirroredElemTypes } + + def Generic[O] given (gen: Generic[O]): Generic[O] { type MirroredElemTypes = gen.MirroredElemTypes ; type MirroredLabel = gen.MirroredLabel ; type MirroredElemLabels = gen.MirroredElemLabels } = gen + def ProductGeneric[O] given (gen: ProductGeneric[O]): ProductGeneric[O] { type MirroredElemTypes = gen.MirroredElemTypes ; type MirroredLabel = gen.MirroredLabel ; type MirroredElemLabels = gen.MirroredElemLabels } = gen + + opaque type Instances[F[_], T] = ErasedInstances[F[T]] + opaque type ProductInstances[F[_], T] = ErasedProductInstances[F[T]] + + def Instances[F[_], T] given (inst: Instances[F, T]): inst.type = inst + def ProductInstances[F[_], T] given (inst: ProductInstances[F, T]): inst.type = inst + + type ToUnion[T] = T match { + case Unit => Nothing + case a *: b => a | ToUnion[b] + } + + type IndexOf[E, X] = IndexOf0[E, X, 0] + + type IndexOf0[E, X, I <: Int] <: Int = X match { + case Unit => -1 + case x *: xs => x match { + case E => I + case _ => IndexOf0[E, xs, S[I]] + } + } + + inline def summonAsArray[F[_], T]: Array[Any] = inline erasedValue[T] match { + case _: Unit => Array() + case _: Tuple1[a] => Array(summon[F[a]]) + case _: (a, b) => Array(summon[F[a]], summon[F[b]]) + case _: (a, b, c) => Array(summon[F[a]], summon[F[b]], summon[F[c]]) + case _: (a, b, c, d) => Array(summon[F[a]], summon[F[b]], summon[F[c]], summon[F[d]]) + case _: (a, b, c, d, e) => Array(summon[F[a]], summon[F[b]], summon[F[c]], summon[F[d]], summon[F[e]]) + // Add fallback for larger sizes + } + + type LiftP[F[_], T] <: Tuple = T match { + case Unit => Unit + case a *: b => F[a] *: LiftP[F, b] + } + + inline def summonFirst[F[_], T, U]: F[U] = summonFirst0[LiftP[F, T]].asInstanceOf[F[U]] + + inline def summonFirst0[T] <: Any = inline erasedValue[T] match { + case _: (a *: b) => implicit match { + case aa: `a` => aa + case _ => summonFirst0[b] + } + } + + given Ops { + inline def (gen: ProductGeneric[Obj]) toRepr [Obj] (o: Obj): gen.MirroredElemTypes = Tuple.fromProduct(o.asInstanceOf).asInstanceOf[gen.MirroredElemTypes] + inline def (gen: ProductGeneric[Obj]) fromRepr [Obj] (r: gen.MirroredElemTypes): Obj = gen.fromProduct(r.asInstanceOf).asInstanceOf[Obj] + + inline def (inst: ProductInstances[F, T]) construct [F[_], T] (f: [t] => F[t] => t): T = + inst.asInstanceOf[ErasedProductInstances[F[T]]].erasedConstruct(f.asInstanceOf).asInstanceOf + // Note the necessary cast here + inline def (inst: ProductInstances[F, T]) map2 [F[_], T] (x: T, y: T)(f: [t] => (F[t], t, t) => t): T = + inst.asInstanceOf[ErasedProductInstances[F[T]]].erasedMap2(x, y)(f.asInstanceOf).asInstanceOf + // Note the necessary cast here + } + + type ProductGenericR[O, R] = Mirror.Product { type MirroredType = O ; type MirroredElemTypes = R } + + inline given mkInstances[F[_], T] as Instances[F, T] given (gen: Generic[T]) = + inline gen match { + case p: ProductGeneric[T] => mkProductInstances[F, T] given p + } + + inline given mkProductInstances[F[_], T] as ProductInstances[F, T] given (gen: ProductGeneric[T]) = + new ErasedProductInstances(gen, summonAsArray[F, gen.MirroredElemTypes]).asInstanceOf[ProductInstances[F, T]] + + inline def derive[F[_], T](gen: Generic[T], pg: ProductInstances[F, T] => F[T]): F[T] = + inline gen match { + case p: ProductGeneric[T] => pg(mkProductInstances[F, T] given p) + } +} + +// -------------------------------------------------------------- + +abstract class ErasedInstances[FT] { + def erasedMap(x: Any)(f: (Any, Any) => Any): Any +} + +final class ErasedProductInstances[FT](val mirror: Mirror.Product, is0: => Array[Any]) extends ErasedInstances[FT] { + lazy val is = is0 + + inline def toProduct(x: Any): Product = x.asInstanceOf[Product] + + class ArrayProduct(val elems: Array[Any]) extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = elems(n) + def productArity = elems.length + override def productIterator: Iterator[Any] = elems.iterator + } + + def erasedConstruct(f: Any => Any): Any = { + val n = is.length + val arr = new Array[Any](n) + var i = 0 + while(i < n) { + arr(i) = f(is(i)) + i = i+1 + } + mirror.fromProduct(ArrayProduct(arr)) + } + + def erasedUnfold(a: Any)(f: (Any, Any) => (Any, Option[Any])): (Any, Option[Any]) = { + val n = is.length + val arr = new Array[Any](n) + var acc = a + var i = 0 + while(i < n) { + val (acc0, e0) = f(acc, is(i)) + e0 match { + case Some(e) => + acc = acc0 + arr(i) = e + case None => + return (acc0, None) + } + i = i+1 + } + (acc, Some(mirror.fromProduct(ArrayProduct(arr)))) + } + + def erasedMap(x0: Any)(f: (Any, Any) => Any): Any = { + val x = toProduct(x0) + val n = is.length + val arr = new Array[Any](n) + var i = 0 + while(i < n) { + arr(i) = f(is(i), x.productElement(i)) + i = i+1 + } + mirror.fromProduct(ArrayProduct(arr)) + } + + def erasedMap2(x0: Any, y0: Any)(f: (Any, Any, Any) => Any): Any = { + val x = toProduct(x0) + val y = toProduct(y0) + val n = is.length + val arr = new Array[Any](n) + var i = 0 + while(i < n) { + arr(i) = f(is(i), x.productElement(i), y.productElement(i)) + i = i+1 + } + mirror.fromProduct(ArrayProduct(arr)) + } + + def erasedFoldLeft(x0: Any)(i: Any)(f: (Any, Any, Any) => CompleteOr[Any]): Any = { + val x = toProduct(x0) + val n = x.productArity + @tailrec + def loop(i: Int, acc: Any): Any = + if(i >= n) acc + else + f(acc, is(i), x.productElement(i)) match { + case Complete(r) => r + case Continue(acc) => + loop(i+1, acc) + } + + loop(0, i) + } + + def erasedFoldLeft2(x0: Any, y0: Any)(i: Any)(f: (Any, Any, Any, Any) => CompleteOr[Any]): Any = { + val x = toProduct(x0) + val y = toProduct(y0) + val n = x.productArity + @tailrec + def loop(i: Int, acc: Any): Any = + if(i >= n) acc + else + f(acc, is(i), x.productElement(i), y.productElement(i)) match { + case Complete(r) => r + case Continue(acc) => + loop(i+1, acc) + } + + loop(0, i) + } +} + +// --------------------------------------------------- + +type Id[t] = t +type Const[c] = [t] =>> c +case class Wrap[T](t: T) + +type ~>[A[_], B[_]] = [t] => A[t] => B[t] + +inline def summon[T] = implicit match { + case t: T => t +} + +inline def summonValues[T] <: Tuple = inline erasedValue[T] match { + case _: Unit => () + case _: (a *: b) => constValue[a] *: summonValues[b] +} + +inline def summonValuesAsArray[T]: Array[Any] = inline erasedValue[Id[T]] match { + case _: Unit => Array() + case _: Tuple1[a] => Array(constValue[a]) + case _: (a, b) => Array(constValue[a], constValue[b]) + case _: (a, b, c) => Array(constValue[a], constValue[b], constValue[c]) + case _: (a, b, c, d) => Array(constValue[a], constValue[b], constValue[c], constValue[d]) + case _: (a, b, c, d, e) => Array(constValue[a], constValue[b], constValue[c], constValue[d], constValue[e]) + // Add fallback for larger sizes +} + +case class Labelling[T](label: String, elemLabels: Seq[String]) +object Labelling { + inline given apply[T0] as Labelling[T0] given (mirror: Mirror { type MirroredType = T0 }) = + Labelling[T0]( + constValue[mirror.MirroredLabel & String], + WrappedArray.make[String](summonValuesAsArray[mirror.MirroredElemLabels]) + ) +} + +sealed trait CompleteOr[T] +case class Complete[T](t: T) extends CompleteOr[T] +case class Continue[T](t: T) extends CompleteOr[T] + +object Complete { + inline def apply[T](c: Boolean)(t: T)(f: T): CompleteOr[T] = + if(c) Complete(t) + else Continue(f) +} + +// --------------------------------------------------------------------- + +case class ISB(i: Int) derives Monoid + +trait Monoid[A] { + def empty: A + def combine(x: A, y: A): A +} + +object Monoid { + given as Monoid[Int] = new Monoid[Int] { + def empty: Int = 0 + def combine(x: Int, y: Int): Int = x+y + } + + given monoidGen[A] as Monoid[A] given (inst: K0.ProductInstances[Monoid, A]) { + def empty: A = inst.construct([t] => (ma: Monoid[t]) => ma.empty) + def combine(x: A, y: A): A = inst.map2(x, y)([t] => (mt: Monoid[t], t0: t, t1: t) => mt.combine(t0, t1)) + } + + inline def derived[A] given (gen: K0.ProductGeneric[A]): Monoid[A] = monoidGen +} + + +} \ No newline at end of file From dc117fe39e5b262d469d1588f401dfa23833cfb1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 13 Jul 2019 19:06:05 +0200 Subject: [PATCH 3/7] Fix this proxy initialization --- .../src/dotty/tools/dotc/typer/Inliner.scala | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 359aa1602704..2d28f1bc5b5c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -312,12 +312,16 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { var lastSelf: Symbol = NoSymbol var lastLevel: Int = 0 for ((level, selfSym) <- sortedProxies) { - lazy val rhsClsSym = selfSym.info.widenDealias.classSymbol + def widenSelfInfo(tp: Type): Type = tp.widenDealias match { + case RefinedType(parent, _, _) => widenSelfInfo(parent) + case tpw => tpw + } + val rhsClsSym = widenSelfInfo(selfSym.info).classSymbol val rhs = - if (lastSelf.exists) - ref(lastSelf).outerSelect(lastLevel - level, selfSym.info) - else if (rhsClsSym.is(Module) && rhsClsSym.isStatic) + if (rhsClsSym.is(Module) && rhsClsSym.isStatic) ref(rhsClsSym.sourceModule) + else if (lastSelf.exists) + ref(lastSelf).outerSelect(lastLevel - level, selfSym.info) else inlineCallPrefix val binding = ValDef(selfSym.asTerm, rhs.ensureConforms(selfSym.info)).withSpan(selfSym.span).setDefTree @@ -328,10 +332,12 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { } } - private def canElideThis(tpe: ThisType): Boolean = - inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(tpe.cls) || - tpe.cls.isContainedIn(inlinedMethod) || - !tpe.cls.membersNeedAsSeenFrom(inlineCallPrefix.tpe) + private def canElideThis(tpe: ThisType): Boolean = { + val cls = tpe.cls + inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(cls) || + cls.isContainedIn(inlinedMethod) || + cls.isStaticOwner && !cls.seesOpaques + } /** Populate `thisProxy` and `paramProxy` as follows: * @@ -354,7 +360,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { case _ => adaptToPrefix(tpe).widenIfUnstable } thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef - if (tpe.cls.membersNeedAsSeenFrom(proxyType)) + 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) From 5cb53e966c3c663693deca1991322c899f7791a7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 13 Jul 2019 19:36:32 +0200 Subject: [PATCH 4/7] Fix computation of inlineCallPrefix --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 2d28f1bc5b5c..955112e1c73d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -201,7 +201,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { private val (methPart, callTypeArgs, callValueArgss) = decomposeCall(call) private val inlinedMethod = methPart.symbol - private val inlineCallPrefix = qualifier(methPart) + private val inlineCallPrefix = + qualifier(methPart).orElse(This(inlinedMethod.enclosingClass.asClass)) // Make sure all type arguments to the call are fully determined for (targ <- callTypeArgs) fullyDefinedType(targ.tpe, "inlined type argument", targ.span) From 83c0b9b26717a7f97670129cd0e7ff833d0f2278 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 14 Jul 2019 11:47:36 +0200 Subject: [PATCH 5/7] Disable shapeless test in FromTasty Need to minimize first before figuring out what goes wrong here. --- compiler/test/dotc/pos-from-tasty.blacklist | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/test/dotc/pos-from-tasty.blacklist b/compiler/test/dotc/pos-from-tasty.blacklist index 1c1b2e32e86c..472a26da7ca1 100644 --- a/compiler/test/dotc/pos-from-tasty.blacklist +++ b/compiler/test/dotc/pos-from-tasty.blacklist @@ -6,3 +6,6 @@ t3612.scala # Other failure t802.scala + +# Recursion limit exceeded: find-member K0.ToUnion +shapeless.scala From dd520c65efaa89883d5f36f58bdb74a9ce247560 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 14 Jul 2019 15:02:14 +0200 Subject: [PATCH 6/7] Inliner: compute this proxies before computing parameter bindings This is an intermediate step so that we can do mappings involving this proxies when generating parameter bindings. --- .../src/dotty/tools/dotc/typer/Inliner.scala | 126 +++++++++++------- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- 2 files changed, 78 insertions(+), 50 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 955112e1c73d..ae00b9e4a5d6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -244,6 +244,75 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { private def newSym(name: Name, flags: FlagSet, info: Type)(implicit ctx: Context): Symbol = ctx.newSymbol(ctx.owner, name, flags, info, coord = call.span) + /** A this-proxy for `tpe` is not needed if + * - `tpe` is the type of the inline call receiver and the inline goes to the same class. + * Example: + * + * class C { + * inline def f() + * this.f() + * } + * + * - The class referenced by `tpe` is inside the inline method + * - The class referenced by `tpe` is a static object that does not have opaque types + * in its scope or environment + */ + private def canElideThis(tpe: ThisType): Boolean = { + val cls = tpe.cls + inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(cls) || + cls.isContainedIn(inlinedMethod) || + cls.isStaticOwner && !cls.seesOpaques + } + + /** Populate `thisProxy` and some parts of `paramProxy` as follows: + * + * 1. If given type refers to a this type of a class that cannot be elided, + * create a proxy symbol and bind the thistype to refer to the proxy. + * The proxy is not yet entered in `bindingsBuf`; that will come later. + * 2. If this type goes to a class or non-static module, also create a this proxy + * for the this type of the owner of the inline method, so that we have a root + * to compute the initializers of this-proxies. + * 3. If the class referenced by a this type has type parameters, create parameter + * proxies for them. + */ + private def registerThisProxies(tpe: Type): Unit = tpe match { + case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) => + def adaptToPrefix(tp: Type) = tp.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) + val proxyName = s"${tpe.cls.name}_this".toTermName + 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) + registerThisProxies(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 _ => + } + + /** Populate the rest of `paramProxy` as follows: + * + * If the given type refers to a parameter, make `paramProxy` refer to the entry stored + * in `paramNames` under the parameter's name. This roundabout way to bind parameter + * references to proxies is done because we don't know a priori what the parameter + * references of a method are (we only know the method's type, but that contains TypeParamRefs + * and MethodParams, not TypeRefs or TermRefs. + */ + private def registerParamProxies(tpe: Type): Unit = tpe match { + case tpe: NamedType + if tpe.symbol.is(Param) && tpe.symbol.owner == inlinedMethod && !paramProxy.contains(tpe) => + paramProxy(tpe) = paramBinding(tpe.name) + case _ => + } + + /** Perform `op` for each leaf tree (of type This, Ident, or TypeTree) of rhsToInline` */ + private def foreachRHSLeaf(op: Type => Unit): Unit = + rhsToInline.foreachSubTree { + case tree: (This | Ident | TypeTree) => tree.tpe.foreachPart(op) + case _ => + } + /** A binding for the parameter of an inline method. This is a `val` def for * by-value parameters and a `def` def for by-name parameters. `val` defs inherit * inline annotations from their parameters. The generated `def` is appended @@ -333,51 +402,6 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { } } - private def canElideThis(tpe: ThisType): Boolean = { - val cls = tpe.cls - inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(cls) || - cls.isContainedIn(inlinedMethod) || - cls.isStaticOwner && !cls.seesOpaques - } - - /** Populate `thisProxy` and `paramProxy` as follows: - * - * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference, - * 1b. If given type refers to an instance this to a class that is not contained in the - * inline method, create a proxy symbol and bind the thistype to refer to the proxy. - * The proxy is not yet entered in `bindingsBuf`; that will come later. - * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored - * in `paramNames` under the parameter's name. This roundabout way to bind parameter - * references to proxies is done because we don't know a priori what the parameter - * references of a method are (we only know the method's type, but that contains TypeParamRefs - * and MethodParams, not TypeRefs or TermRefs. - */ - 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 - if tpe.symbol.is(Param) && tpe.symbol.owner == inlinedMethod && !paramProxy.contains(tpe) => - paramProxy(tpe) = paramBinding(tpe.name) - case _ => - } - - /** Register type of leaf node */ - private def registerLeaf(tree: Tree): Unit = tree match { - case _: This | _: Ident | _: TypeTree => - tree.tpe.foreachPart(registerType) - case _ => - } - /** Make `tree` part of inlined expansion. This means its owner has to be changed * from its `originalOwner`, and, if it comes from outside the inlined method * itself, it has to be marked as an inlined argument. @@ -408,14 +432,18 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { ) } + // make sure prefix is executed if it is impure + if (!isIdempotentExpr(inlineCallPrefix)) + registerThisProxies(inlinedMethod.owner.thisType) + + // Compute this proxies for all leaves of the inlined body + foreachRHSLeaf(registerThisProxies) + // Compute bindings for all parameters, appending them to bindingsBuf computeParamBindings(inlinedMethod.info, callTypeArgs, callValueArgss) - // make sure prefix is executed if it is impure - if (!isIdempotentExpr(inlineCallPrefix)) registerType(inlinedMethod.owner.thisType) - // Register types of all leaves of inlined body so that the `paramProxy` and `thisProxy` maps are defined. - rhsToInline.foreachSubTree(registerLeaf) + foreachRHSLeaf(registerParamProxies) // Compute bindings for all this-proxies, appending them to bindingsBuf computeThisBindings() diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6d6404fb7316..f3edbcbe82bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -49,7 +49,7 @@ object Typer { */ enum BindingPrec { case NothingBound, PackageClause, WildImport, NamedImport, Definition - + def isImportPrec = this == NamedImport || this == WildImport } From 07b8835222ae2e23b2650445ed02df9df4ce7c9b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 14 Jul 2019 15:07:40 +0200 Subject: [PATCH 7/7] Fix SymDenotations#seesOpaques --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 19f7ee238d99..9644313e925d 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -592,7 +592,7 @@ object SymDenotations { def seesOpaques(implicit ctx: Context): Boolean = containsOpaques || - is(Module, butNot = Package) && owner.containsOpaques + is(Module, butNot = Package) && owner.seesOpaques /** Is this the denotation of a self symbol of some class? * This is the case if one of two conditions holds: diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index fd000257c75c..7c194b3a7283 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2463,9 +2463,9 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { if (skipped) op else { indent += 2 - b append "\n" append (" " * indent) append "==> " append str + b.append("\n").append(" " * indent).append("==> ").append(str) val res = op - b append "\n" append (" " * indent) append "<== " append str append " = " append show(res) + b.append("\n").append(" " * indent).append("<== ").append(str).append(" = ").append(show(res)) indent -= 2 res }