diff options
author | Guillaume Martres <smarter@ubuntu.com> | 2016-06-03 22:04:38 +0200 |
---|---|---|
committer | Guillaume Martres <smarter@ubuntu.com> | 2016-06-03 22:04:38 +0200 |
commit | 04b0e85c70de36fd7b5a4b3ffd1b8add0963303d (patch) | |
tree | 582c91c26187bc606b43d3df424b9fd8ea40c4d4 /src | |
parent | 030ff82070197f0c126f5c0287e076b0f6b6dd8d (diff) | |
parent | 295c981f719d09a690076af9d87d48679b1ceb42 (diff) | |
download | dotty-04b0e85c70de36fd7b5a4b3ffd1b8add0963303d.tar.gz dotty-04b0e85c70de36fd7b5a4b3ffd1b8add0963303d.tar.bz2 dotty-04b0e85c70de36fd7b5a4b3ffd1b8add0963303d.zip |
Merge pull request #1288 from dotty-staging/fix/implicit-caching-2
Implicit scope caching: bug fixes and performance improvements
Diffstat (limited to 'src')
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 9 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Applications.scala | 14 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Implicits.scala | 33 |
3 files changed, 41 insertions, 15 deletions
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index a318390d1..f514a329e 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2145,7 +2145,12 @@ object Types { unique(new CachedAndType(tp1, tp2)) } def make(tp1: Type, tp2: Type)(implicit ctx: Context): Type = - if (tp1 eq tp2) tp1 else apply(tp1, tp2) + if ((tp1 eq tp2) || (tp2 eq defn.AnyType)) + tp1 + else if (tp1 eq defn.AnyType) + tp2 + else + apply(tp1, tp2) } abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType { @@ -3450,7 +3455,7 @@ object Types { case TypeBounds(_, hi) => apply(x, hi) case tp: ThisType => - apply(x, tp.underlying) + apply(x, tp.tref) case tp: ConstantType => apply(x, tp.underlying) case tp: MethodParam => diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 2b0cc4033..f743c5784 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -1146,11 +1146,17 @@ trait Applications extends Compatibility { self: Typer => alts } - def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = - alts filter ( alt => - if (!ctx.isAfterTyper) isApplicable(alt, targs, args, resultType) - else isDirectlyApplicable(alt, targs, args, resultType) + def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = { + val alts2 = alts.filter(alt => + isDirectlyApplicable(alt, targs, args, resultType) ) + if (alts2.isEmpty && !ctx.isAfterTyper) + alts.filter(alt => + isApplicable(alt, targs, args, resultType) + ) + else + alts2 + } val alts1 = narrowBySize(alts) //ctx.log(i"narrowed by size: ${alts1.map(_.symbol.showDcl)}%, %") diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 38ac709be..7de40294d 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -106,7 +106,7 @@ object Implicits { */ class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) { assert(initctx.typer != null) - val refs: List[TermRef] = { + lazy val refs: List[TermRef] = { val buf = new mutable.ListBuffer[TermRef] for (companion <- companionRefs) buf ++= companion.implicitMembers buf.toList @@ -284,10 +284,13 @@ trait ImplicitRunInfo { self: RunInfo => override implicit protected val ctx: Context = liftingCtx override def stopAtStatic = true def apply(tp: Type) = tp match { + case tp: TypeRef if tp.symbol.isLambdaTrait => + defn.AnyType case tp: TypeRef if tp.symbol.isAbstractOrAliasType => val pre = tp.prefix def joinClass(tp: Type, cls: ClassSymbol) = - AndType(tp, cls.typeRef.asSeenFrom(pre, cls.owner)) + if (cls.isLambdaTrait) tp + else AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner)) val lead = if (tp.prefix eq NoPrefix) defn.AnyType else apply(tp.prefix) (lead /: tp.classSymbols)(joinClass) case tp: TypeVar => @@ -323,7 +326,7 @@ trait ImplicitRunInfo { self: RunInfo => def addParentScope(parent: TypeRef): Unit = { iscopeRefs(parent) foreach addRef for (param <- parent.typeParams) - comps ++= iscopeRefs(pre.member(param.name).info) + comps ++= iscopeRefs(tp.member(param.name).info) } val companion = cls.companionModule if (companion.exists) addRef(companion.valRef) @@ -338,8 +341,6 @@ trait ImplicitRunInfo { self: RunInfo => } } - def ofTypeImplicits(comps: TermRefSet) = new OfTypeImplicits(tp, comps)(ctx) - /** The implicit scope of type `tp` * @param isLifted Type `tp` is the result of a `liftToClasses` application */ @@ -349,9 +350,12 @@ trait ImplicitRunInfo { self: RunInfo => ctx.typerState.ephemeral = false try { val liftedTp = if (isLifted) tp else liftToClasses(tp) - val result = - if (liftedTp ne tp) iscope(liftedTp, isLifted = true) - else ofTypeImplicits(collectCompanions(tp)) + val refs = + if (liftedTp ne tp) + iscope(liftedTp, isLifted = true).companionRefs + else + collectCompanions(tp) + val result = new OfTypeImplicits(tp, refs)(ctx) if (ctx.typerState.ephemeral) record("ephemeral cache miss: implicitScope") else if (cacheResult) implicitScopeCache(tp) = result result @@ -363,7 +367,18 @@ trait ImplicitRunInfo { self: RunInfo => computeIScope(cacheResult = false) else implicitScopeCache get tp match { case Some(is) => is - case None => computeIScope(cacheResult = seen.isEmpty) + case None => + // Implicit scopes are tricky to cache because of loops. For example + // in `tests/pos/implicit-scope-loop.scala`, the scope of B contains + // the scope of A which contains the scope of B. We break the loop + // by returning EmptyTermRefSet in `collectCompanions` for types + // that we have already seen, but this means that we cannot cache + // the computed scope of A, it is incomplete. + // Keeping track of exactly where these loops happen would require a + // lot of book-keeping, instead we choose to be conservative and only + // cache scopes before any type has been seen. This is unfortunate + // because loops are very common for types in scala.collection. + computeIScope(cacheResult = seen.isEmpty) } } |