From 6f4b38cf9d1711ec525219d9b0b8049c95047f38 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Jun 2015 11:23:38 +0200 Subject: [PATCH 1/5] Insert a space between symbol and `:' when printing SelectionProtos Previously, we'd see something like `? { :+: Int }`, which is confusing. Now we see instead `? { :+ : Int }`. --- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 2288fe9c0c73..70fab7e0fc5a 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -148,7 +148,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case JavaArrayType(elemtp) => return toText(elemtp) ~ "[]" case tp: SelectionProto => - return "?{ " ~ toText(tp.name) ~ ": " ~ toText(tp.memberProto) ~ " }" + return "?{ " ~ toText(tp.name) ~ (" " provided !tp.name.decode.last.isLetterOrDigit) ~ + ": " ~ toText(tp.memberProto) ~ " }" case tp: ViewProto => return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType) case tp @ FunProto(args, resultType, _) => From 131ab5c1261608715cf2fde3aa44a04777cbda64 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Jun 2015 13:27:43 +0200 Subject: [PATCH 2/5] Avoid double context creation when modes change on a fresh context. Move addMode and friends to two decorators, one for Context, the other for FreshContext. Implement behavior accordingly. This avoids creating two contexts in situations like: c.fresh.setxploreTyperState.addMode(...) Mow we can write c.fresh.addMode(...).setExploreTyperState Because addMode returns a fresh context when applied to a fresh context. Note that we specifically do not want virtual dispatch of addMode, that's why it was moved to a decorator. Also: removed mention of ".fresh: when just forllowed by an addMode, because that one is redundant. --- src/dotty/tools/dotc/core/Contexts.scala | 22 +++++++++++++------ .../tools/dotc/printing/PlainPrinter.scala | 2 +- src/dotty/tools/dotc/typer/Implicits.scala | 5 +++-- src/dotty/tools/dotc/typer/Namer.scala | 2 +- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index b00896117750..c9deaab102d5 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -386,13 +386,6 @@ object Contexts { final def withOwner(owner: Symbol): Context = if (owner ne this.owner) fresh.setOwner(owner) else this - final def withMode(mode: Mode): Context = - if (mode != this.mode) fresh.setMode(mode) else this - - final def addMode(mode: Mode): Context = withMode(this.mode | mode) - final def maskMode(mode: Mode): Context = withMode(this.mode & mode) - final def retractMode(mode: Mode): Context = withMode(this.mode &~ mode) - override def toString = "Context(\n" + (outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}") mkString "\n") @@ -444,6 +437,21 @@ object Contexts { def setDebug = setSetting(base.settings.debug, true) } + implicit class ModeChanges(val c: Context) extends AnyVal { + final def withMode(mode: Mode): Context = + if (mode != c.mode) c.fresh.setMode(mode) else c + + final def addMode(mode: Mode): Context = withMode(c.mode | mode) + final def maskMode(mode: Mode): Context = withMode(c.mode & mode) + final def retractMode(mode: Mode): Context = withMode(c.mode &~ mode) + } + + implicit class FreshModeChanges(val c: FreshContext) extends AnyVal { + final def addMode(mode: Mode): c.type = c.setMode(c.mode | mode) + final def maskMode(mode: Mode): c.type = c.setMode(c.mode & mode) + final def retractMode(mode: Mode): c.type = c.setMode(c.mode &~ mode) + } + /** A class defining the initial context with given context base * and set of possible settings. */ diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 86dd3878c80a..5d2309e95158 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -12,7 +12,7 @@ import typer.Mode import scala.annotation.switch class PlainPrinter(_ctx: Context) extends Printer { - protected[this] implicit def ctx: Context = _ctx.fresh.addMode(Mode.Printing) + protected[this] implicit def ctx: Context = _ctx.addMode(Mode.Printing) protected def maxToTextRecursions = 100 diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 43d93b2b81d2..be76553be55b 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -90,7 +90,7 @@ object Implicits { } if (refs.isEmpty) refs - else refs filter (refMatches(_)(ctx.fresh.setExploreTyperState.addMode(Mode.TypevarsMissContext))) // create a defensive copy of ctx to avoid constraint pollution + else refs filter (refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution } } @@ -480,7 +480,8 @@ trait Implicits { self: Typer => pt) val generated1 = adapt(generated, pt) lazy val shadowing = - typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(nestedContext.setNewTyperState.addMode(Mode.ImplicitShadowing)) + typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto) + (nestedContext.addMode(Mode.ImplicitShadowing).setNewTyperState) def refMatches(shadowing: Tree): Boolean = ref.symbol == closureBody(shadowing).symbol || { shadowing match { diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 11a0bd76e2bd..d5a6ec8f45b0 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -701,7 +701,7 @@ class Namer { typer: Typer => // println(s"final inherited for $sym: ${inherited.toString}") !!! // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") - val rhsCtx = ctx.fresh addMode Mode.InferringReturnType + val rhsCtx = ctx.addMode(Mode.InferringReturnType) def rhsType = ctx.deskolemize( typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen.approximateUnion) def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos) From 204158d4141254d1148534e14d3d26b361b7bc12 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Jun 2015 15:07:25 +0200 Subject: [PATCH 3/5] Stratify classTags in DottyPredef Test case in pending/run/array-addition.scala. The problem was that implicit search failed to insert `genericArrayOps` around `Array()`. The reason was that the implicit parameter for `genericArrayOps` was ambiguous, multiple `classTag` methods matched the expected type `ClassTag[?T >: Nothing]`. Stratifying ClassTags in DottyPredef into layers according to implicit priority avoids this problem. This is a temporary solution until we have proper ClassTag handling in place. --- src/dotty/DottyPredef.scala | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index 7c8098bd1763..0a5d4d7f3492 100644 --- a/src/dotty/DottyPredef.scala +++ b/src/dotty/DottyPredef.scala @@ -4,22 +4,34 @@ import scala.reflect.ClassTag import scala.reflect.runtime.universe.TypeTag import scala.Predef.??? // this is currently ineffective, because of #530 -object DottyPredef { - /** implicits for ClassTag and TypeTag. Should be implemented with macros */ +abstract class I1 { implicit def classTag[T]: ClassTag[T] = scala.Predef.??? implicit def typeTag[T]: TypeTag[T] = scala.Predef.??? - - - /** ClassTags for final classes */ + implicit val DoubleClassTag: ClassTag[Double] = ClassTag.Double +} +abstract class I2 extends I1 { + implicit val FloatClassTag: ClassTag[Double] = ClassTag.Double +} +abstract class I3 extends I2 { + implicit val LongClassTag: ClassTag[Long] = ClassTag.Long +} +abstract class I4 extends I3 { implicit val IntClassTag: ClassTag[Int] = ClassTag.Int - implicit val ByteClassTag: ClassTag[Byte] = ClassTag.Byte +} +abstract class I5 extends I4 { implicit val ShortClassTag: ClassTag[Short] = ClassTag.Short +} +abstract class I6 extends I5 { + implicit val ByteClassTag: ClassTag[Byte] = ClassTag.Byte implicit val CharClassTag: ClassTag[Char] = ClassTag.Char - implicit val LongClassTag: ClassTag[Long] = ClassTag.Long - implicit val FloatClassTag: ClassTag[Float] = ClassTag.Float - implicit val DoubleClassTag: ClassTag[Double] = ClassTag.Double implicit val BooleanClassTag: ClassTag[Boolean] = ClassTag.Boolean implicit val UnitClassTag: ClassTag[Unit] = ClassTag.Unit implicit val NullClassTag: ClassTag[Null] = ClassTag.Null +} + +/** implicits for ClassTag and TypeTag. Should be implemented with macros */ +object DottyPredef extends I6 { + + /** ClassTags for final classes */ implicit val NothingClassTag: ClassTag[Nothing] = ClassTag.Nothing } From c3bcad807db47ee4ab27ac4a725ba5f402667b4d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Jun 2015 15:15:42 +0200 Subject: [PATCH 4/5] Do not check for ambiguous implicits in viewExists Previously `viewExists(X, Y)` failed if there were ambiguous implicit conversions from X to Y. This is too fragile, as demonstrated by test case run/array-addition.scala. Here, the `genericArrayOps` implicit was not inserted because its result type `Array[?T]` was deemed to be incompatible with `? { +: : ? }`. It was incompatible because there were multiple implicits that added :+ to arrays of various element types. But once `genericArrayOps` gets applied, the type parameter `?T` of the array result is fixed, and the ambuity goes away. The scenario shows that we should not test for ambiguous implicits in viewExists. Such a test is fragile because it depends on the progress of type inference when the test is made. It's preferable to just test for any implicit conversion to exist and to check for ambiguities later, when the implicit conversion is actually applied. This has also the potential of speeding up implicit search in situations where `viewExists` is called often (e.g. when coupled with overloading resolution). --- src/dotty/tools/dotc/typer/Implicits.scala | 11 ++++++++--- src/dotty/tools/dotc/typer/Mode.scala | 6 ++++++ tests/{pending => }/run/array-addition.check | 0 tests/{pending => }/run/array-addition.scala | 0 4 files changed, 14 insertions(+), 3 deletions(-) rename tests/{pending => }/run/array-addition.check (100%) rename tests/{pending => }/run/array-addition.scala (100%) diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index be76553be55b..a3ddca5d99db 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -384,7 +384,9 @@ trait Implicits { self: Typer => && (ctx.mode is Mode.ImplicitsEnabled) && from.isInstanceOf[ValueType] && ( from.isValueSubType(to) - || inferView(dummyTreeOfType(from), to)(ctx.fresh.setExploreTyperState).isInstanceOf[SearchSuccess] + || inferView(dummyTreeOfType(from), to) + (ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState) + .isInstanceOf[SearchSuccess] ) ) @@ -515,8 +517,11 @@ trait Implicits { self: Typer => case fail: SearchFailure => rankImplicits(pending1, acc) case best: SearchSuccess => - val newPending = pending1 filter (isAsGood(_, best.ref)(nestedContext.setExploreTyperState)) - rankImplicits(newPending, best :: acc) + if (ctx.mode.is(Mode.ImplicitExploration)) best :: Nil + else { + val newPending = pending1 filter (isAsGood(_, best.ref)(nestedContext.setExploreTyperState)) + rankImplicits(newPending, best :: acc) + } } case nil => acc } diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala index e84ef27846e3..31766adc6acd 100644 --- a/src/dotty/tools/dotc/typer/Mode.scala +++ b/src/dotty/tools/dotc/typer/Mode.scala @@ -73,5 +73,11 @@ object Mode { */ val ImplicitShadowing = newMode(11, "ImplicitShadowing") + /** We are currently in a `viewExists` check. In that case, ambiguous + * implicits checks are disabled and we succeed with teh first implicit + * found. + */ + val ImplicitExploration = newMode(12, "ImplicitExploration") + val PatternOrType = Pattern | Type } diff --git a/tests/pending/run/array-addition.check b/tests/run/array-addition.check similarity index 100% rename from tests/pending/run/array-addition.check rename to tests/run/array-addition.check diff --git a/tests/pending/run/array-addition.scala b/tests/run/array-addition.scala similarity index 100% rename from tests/pending/run/array-addition.scala rename to tests/run/array-addition.scala From 74e9107e25a2b2f50a8d80b3b13136e5ab9eb6e9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 22 Jun 2015 11:15:20 +0200 Subject: [PATCH 5/5] Fixed typo --- src/dotty/tools/dotc/typer/Mode.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala index 31766adc6acd..b903049d2d29 100644 --- a/src/dotty/tools/dotc/typer/Mode.scala +++ b/src/dotty/tools/dotc/typer/Mode.scala @@ -74,7 +74,7 @@ object Mode { val ImplicitShadowing = newMode(11, "ImplicitShadowing") /** We are currently in a `viewExists` check. In that case, ambiguous - * implicits checks are disabled and we succeed with teh first implicit + * implicits checks are disabled and we succeed with the first implicit * found. */ val ImplicitExploration = newMode(12, "ImplicitExploration")