From 5d59f03e971fd6ef5a9cd9afbe419a374a3dff88 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 23 Aug 2020 12:13:43 +0200 Subject: [PATCH 1/7] Avoid redundant filter in memberNames --- compiler/src/dotty/tools/dotc/core/Types.scala | 16 +++++++++++++++- .../tools/dotc/interactive/Completion.scala | 1 + .../src/dotty/tools/dotc/typer/RefChecks.scala | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 19800fc635e6..e0feca8c8d1b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -793,7 +793,8 @@ object Types { */ final def memberNames(keepOnly: NameFilter, pre: Type = this)(using Context): Set[Name] = this match { case tp: ClassInfo => - tp.cls.classDenot.memberNames(keepOnly) filter (keepOnly(pre, _)) + val names = tp.cls.classDenot.memberNames(keepOnly) + if keepOnly.isStable then names else names.filter(keepOnly(pre, _)) case tp: RefinedType => val ns = tp.parent.memberNames(keepOnly, pre) if (keepOnly(pre, tp.refinedName)) ns + tp.refinedName else ns @@ -5665,6 +5666,11 @@ object Types { */ abstract class NameFilter { def apply(pre: Type, name: Name)(using Context): Boolean + + /** Filter does not need to be rechecked with full prefix, if it + * has been already checked for the class denotation of the prefix + */ + def isStable: Boolean } /** A filter for names of abstract types of a given type */ @@ -5674,6 +5680,7 @@ object Types { val mbr = pre.nonPrivateMember(name) mbr.symbol.is(Deferred) && mbr.info.isInstanceOf[RealTypeBounds] } + def isStable = false } /** A filter for names of abstract types of a given type */ @@ -5683,12 +5690,14 @@ object Types { val mbr = pre.member(name) mbr.symbol.isType && !mbr.symbol.isClass } + def isStable = false } /** A filter for names of deferred term definitions of a given type */ object abstractTermNameFilter extends NameFilter { def apply(pre: Type, name: Name)(using Context): Boolean = name.isTermName && pre.nonPrivateMember(name).hasAltWith(_.symbol.is(Deferred)) + def isStable = false } /** A filter for names of type aliases of a given type */ @@ -5698,19 +5707,23 @@ object Types { val mbr = pre.nonPrivateMember(name) mbr.symbol.isAliasType } + def isStable = false } object typeNameFilter extends NameFilter { def apply(pre: Type, name: Name)(using Context): Boolean = name.isTypeName + def isStable = true } object fieldFilter extends NameFilter { def apply(pre: Type, name: Name)(using Context): Boolean = name.isTermName && (pre member name).hasAltWith(!_.symbol.is(Method)) + def isStable = true } object takeAllFilter extends NameFilter { def apply(pre: Type, name: Name)(using Context): Boolean = true + def isStable = true } object implicitFilter extends NameFilter { @@ -5719,6 +5732,7 @@ object Types { * no post-filtering is needed. */ def apply(pre: Type, name: Name)(using Context): Boolean = true + def isStable = true } // ----- Debug --------------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index c5de3024fcd2..4bb1a828d731 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -323,6 +323,7 @@ object Completion { private object completionsFilter extends NameFilter { def apply(pre: Type, name: Name)(using Context): Boolean = !name.isConstructorName && name.toTermName.info.kind == SimpleNameKind + def isStable = true } } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index d09dad5ca063..c5a274068a91 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -29,6 +29,7 @@ object RefChecks { private val defaultMethodFilter = new NameFilter { def apply(pre: Type, name: Name)(using Context): Boolean = name.is(DefaultGetterName) + def isStable = true } /** Only one overloaded alternative is allowed to define default arguments */ From 36f421f52d60373cb0965fd3f7434738b3aace28 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Oct 2020 17:55:16 +0200 Subject: [PATCH 2/7] Avoid more collection ops in frontend --- .../src/dotty/tools/dotc/transform/SymUtils.scala | 11 +++++++++-- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 7 +++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index e70f720501fe..9adc11585638 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -22,12 +22,19 @@ object SymUtils { extension (self: Symbol) { - /** All traits implemented by a class or trait except for those inherited through the superclass. */ + /** All traits implemented by a class or trait except for those inherited + * through the superclass. Traits are given in the order they appear in the + * parents clause (which is the reverse of their order in baseClasses) + */ def directlyInheritedTraits(using Context): List[ClassSymbol] = { val superCls = self.asClass.superClass val baseClasses = self.asClass.baseClasses if (baseClasses.isEmpty) Nil - else baseClasses.tail.takeWhile(_ ne superCls).reverse + else + def recur(bcs: List[ClassSymbol], acc: List[ClassSymbol]): List[ClassSymbol] = bcs match + case bc :: bcs1 => if bc eq superCls then acc else recur(bcs1, bc :: acc) + case nil => acc + recur(baseClasses.tail, Nil) } /** All traits implemented by a class, except for those inherited through the superclass. diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index c5a274068a91..b7712876aeb5 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -491,10 +491,9 @@ object RefChecks { */ def missingTermSymbols: List[Symbol] = val buf = new mutable.ListBuffer[Symbol] - for bc <- clazz.baseClasses - sym <- bc.info.decls.toList - if sym.is(DeferredTerm) && !isImplemented(sym) && !ignoreDeferred(sym) - do buf += sym + for bc <- clazz.baseClasses; sym <- bc.info.decls.toList do + if sym.is(DeferredTerm) && !isImplemented(sym) && !ignoreDeferred(sym) + buf += sym buf.toList // 2. Check that only abstract classes have deferred members From f20474519f96904b70fe6288a02058a98613e6e0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Aug 2020 18:34:21 +0200 Subject: [PATCH 3/7] Make all orElse methods inline methods Make all orElse methods that take a call-by-name argument inline methods. --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 2 +- compiler/src/dotty/tools/dotc/core/Denotations.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Symbols.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 53b072fdeec8..721bac57d907 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -174,7 +174,7 @@ object Trees { def toList: List[Tree[T]] = this :: Nil /** if this tree is the empty tree, the alternative, else this tree */ - def orElse[U >: Untyped <: T](that: => Tree[U]): Tree[U] = + inline def orElse[U >: Untyped <: T](inline that: Tree[U]): Tree[U] = if (this eq genericEmptyTree) that else this /** The number of nodes in this tree */ diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 73e98461046e..752ab3aaa955 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -237,7 +237,7 @@ object Denotations { def mapInfo(f: Type => Type)(using Context): Denotation /** If this denotation does not exist, fallback to alternative */ - final def orElse(that: => Denotation): Denotation = if (this.exists) this else that + inline def orElse(inline that: Denotation): Denotation = if (this.exists) this else that /** The set of alternative single-denotations making up this denotation */ final def alternatives: List[SingleDenotation] = altsWith(alwaysTrue) @@ -596,7 +596,7 @@ object Denotations { def mapInfo(f: Type => Type)(using Context): SingleDenotation = derivedSingleDenotation(symbol, f(info)) - def orElse(that: => SingleDenotation): SingleDenotation = if (this.exists) this else that + inline def orElse(inline that: SingleDenotation): SingleDenotation = if (this.exists) this else that def altsWith(p: Symbol => Boolean): List[SingleDenotation] = if (exists && p(symbol)) this :: Nil else Nil diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 9b0b42734381..7ba320159531 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -239,7 +239,7 @@ object Symbols { } /** This symbol, if it exists, otherwise the result of evaluating `that` */ - def orElse(that: => Symbol)(using Context): Symbol = + inline def orElse(inline that: Symbol)(using Context): Symbol = if (this.exists) this else that /** If this symbol satisfies predicate `p` this symbol, otherwise `NoSymbol` */ From 005a338f9887fa9983e7e50fcf24c8f93a30e702 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2020 12:08:26 +0200 Subject: [PATCH 4/7] Reduce context creations in implicits --- compiler/src/dotty/tools/dotc/core/Contexts.scala | 6 +++++- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index b2258dee613d..179b40a18bb4 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -674,7 +674,7 @@ object Contexts { final def retractMode(mode: Mode): c.type = c.setMode(c.mode &~ mode) } - private def exploreCtx(using Context): Context = + private def exploreCtx(using Context): FreshContext = util.Stats.record("explore") val base = ctx.base import base._ @@ -701,6 +701,10 @@ object Contexts { val ectx = exploreCtx try op(using ectx) finally wrapUpExplore(ectx) + inline def exploreInFreshCtx[T](inline op: FreshContext ?=> T)(using Context): T = + val ectx = exploreCtx + try op(using ectx) finally wrapUpExplore(ectx) + private def changeOwnerCtx(owner: Symbol)(using Context): Context = val base = ctx.base import base._ diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 14ece3c6c594..d52e672f1cee 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -245,10 +245,12 @@ object Implicits: if refs.isEmpty && (!considerExtension || companionRefs.isEmpty) then Nil else - val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext) val candidates = new mutable.ListBuffer[Candidate] def tryCandidate(extensionOnly: Boolean)(ref: ImplicitRef) = - var ckind = explore(candidateKind(ref.underlyingRef))(using nestedCtx) + var ckind = exploreInFreshCtx { (using ctx: FreshContext) => + ctx.setMode(ctx.mode | Mode.TypevarsMissContext) + candidateKind(ref.underlyingRef) + } if extensionOnly then ckind &= Candidate.Extension if ckind != Candidate.None then candidates += Candidate(ref, ckind, level) From 4c6f31788e960024b9facfa9abdb9cc70312b274 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Sep 2020 19:28:14 +0200 Subject: [PATCH 5/7] Adapt to stricter conditional syntax --- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index b7712876aeb5..1a6fca704e7d 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -492,7 +492,7 @@ object RefChecks { def missingTermSymbols: List[Symbol] = val buf = new mutable.ListBuffer[Symbol] for bc <- clazz.baseClasses; sym <- bc.info.decls.toList do - if sym.is(DeferredTerm) && !isImplemented(sym) && !ignoreDeferred(sym) + if sym.is(DeferredTerm) && !isImplemented(sym) && !ignoreDeferred(sym) then buf += sym buf.toList From f63ff908841a0121c2ba272d7bb773eb683d3718 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 27 Sep 2020 18:38:50 +0200 Subject: [PATCH 6/7] Add @switch annotion in Scanner --- compiler/src/dotty/tools/dotc/parsing/Scanners.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 241319ea6ff7..bdf74623b244 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -565,7 +565,7 @@ object Scanners { lastOffset = prev.lastOffset lineOffset = prev.lineOffset } - token match { + (token: @switch) match { case CASE => lookAhead() if (token == CLASS) fuse(CASECLASS) @@ -601,8 +601,8 @@ object Scanners { if colonSyntax then observeColonEOL() case RBRACE | RPAREN | RBRACKET => closeIndented() - case EOF if !source.maybeIncomplete => - closeIndented() + case EOF => + if !source.maybeIncomplete then closeIndented() case _ => } } From 4ed6a3fda0c9fe160fad6f8222575c8326f589d5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 2 Oct 2020 16:11:29 +0200 Subject: [PATCH 7/7] Instrument method invocations under -Yinstrument-defs --- .../dotty/tools/dotc/config/ScalaSettings.scala | 1 + .../tools/dotc/transform/Instrumentation.scala | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 2c3f88c1ce5c..b1d2addfe6af 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -181,6 +181,7 @@ class ScalaSettings extends Settings.SettingGroup { val YnoDecodeStacktraces: Setting[Boolean] = BooleanSetting("-Yno-decode-stacktraces", "Show raw StackOverflow stacktraces, instead of decoding them into triggering operations.") val Yinstrument: Setting[Boolean] = BooleanSetting("-Yinstrument", "Add instrumentation code that counts allocations and closure creations.") + val YinstrumentDefs: Setting[Boolean] = BooleanSetting("-Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") /** Dottydoc specific settings */ val siteRoot: Setting[String] = StringSetting( diff --git a/compiler/src/dotty/tools/dotc/transform/Instrumentation.scala b/compiler/src/dotty/tools/dotc/transform/Instrumentation.scala index b4d2c541f2c5..676cf00e2f46 100644 --- a/compiler/src/dotty/tools/dotc/transform/Instrumentation.scala +++ b/compiler/src/dotty/tools/dotc/transform/Instrumentation.scala @@ -74,6 +74,21 @@ class Instrumentation extends MiniPhase { thisPhase => private def ok(using Context) = !ctx.owner.ownersIterator.exists(_.name.toString.startsWith("Stats")) + override def transformDefDef(tree: DefDef)(using Context): Tree = + val sym = tree.symbol + if ctx.settings.YinstrumentDefs.value + && ok + && sym.exists + && !sym.isOneOf(Synthetic | Artifact) + then + def icall = record(i"method/${sym.fullName}", tree) + def rhs1 = tree.rhs match + case rhs @ Block(stats, expr) => cpy.Block(rhs)(icall :: stats, expr) + case _: Match | _: If | _: Try | _: Labeled => cpy.Block(tree.rhs)(icall :: Nil, tree.rhs) + case rhs => rhs + cpy.DefDef(tree)(rhs = rhs1) + else tree + override def transformApply(tree: Apply)(using Context): Tree = tree.fun match { case Select(nu: New, _) => cpy.Block(tree)(record(i"alloc/${nu.tpe}", tree) :: Nil, tree)