From 0c5dd9e02f03143372237018c55e12a07c13f8c1 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 30 Apr 2013 21:14:06 +0200 Subject: [backport] SI-7470 implements fundep materialization Backports 21a8c6c from the 2.11.x branch under -Xfundep-materialization as per Miles Sabin's request. Thanks Miles! --- .../scala/tools/nsc/settings/ScalaSettings.scala | 1 + .../scala/tools/nsc/typechecker/Macros.scala | 32 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index dbfaa2c531..3a6d183c32 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -111,6 +111,7 @@ trait ScalaSettings extends AbsScalaSettings val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") val Xdivergence211 = BooleanSetting ("-Xdivergence211", "Turn on the 2.11 behavior of implicit divergence not terminating recursive implicit searches (SI-7291).") + val XfundepMaterialization = BooleanSetting("-Xfundep-materialization", "Turn on the 2.11 behavior of macro expansion being able to influence type inference in implicit searches") /** Compatibility stubs for options whose value name did * not previously match the option name. diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index d6ec5f2cb0..6801dc068c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -713,6 +713,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { var expectedTpe = expandee.tpe if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType + if (settings.XfundepMaterialization.value) { + // approximation is necessary for whitebox macros to guide type inference + // read more in the comments for onDelayed below + val undetparams = expectedTpe collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol } + expectedTpe = deriveTypeWithWildcards(undetparams)(expectedTpe) + } + // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc val expanded0 = duplicateAndKeepPositions(expanded) val expanded1 = typecheck("macro def return type", expanded0, expectedTpe) @@ -766,9 +773,24 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype, // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want. + // + // =========== THE SOLUTION =========== + // + // To give materializers a chance to say their word before vanilla inference kicks in, + // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo) + // and then trigger macro expansion with the undetermined type parameters still there. + // Thanks to that the materializer can take a look at what's going on and react accordingly. + // + // NOTE: This functionality is only available under the -Xfundep-materialization flag in Scala 2.10, + // but is enabled by default in Scala 2.11. val shouldInstantiate = typer.context.undetparams.nonEmpty && !inPolyMode(mode) - if (shouldInstantiate) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) - else delayed + if (shouldInstantiate) { + if (settings.XfundepMaterialization.value) { + forced += delayed + typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false) + macroExpand(typer, delayed, mode, pt) + } else typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) + } else delayed case Fallback(fallback) => typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt)) case Other(result) => @@ -886,10 +908,12 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * 2) undetparams (sym.isTypeParameter && !sym.isSkolem) */ var hasPendingMacroExpansions = false + private val forced = perRunCaches.newWeakSet[Tree] private val delayed = perRunCaches.newWeakMap[Tree, scala.collection.mutable.Set[Int]] private def isDelayed(expandee: Tree) = delayed contains expandee private def calculateUndetparams(expandee: Tree): scala.collection.mutable.Set[Int] = - delayed.get(expandee).getOrElse { + if (forced(expandee)) scala.collection.mutable.Set[Int]() + else delayed.getOrElse(expandee, { val calculated = scala.collection.mutable.Set[Symbol]() expandee foreach (sub => { def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym @@ -898,7 +922,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { }) macroLogVerbose("calculateUndetparams: %s".format(calculated)) calculated map (_.id) - } + }) private val undetparams = perRunCaches.newSet[Int] def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = { undetparams ++= newUndets map (_.id) -- cgit v1.2.3 From 364c8e9fde9212d9c7cf543ffe73206b2b00c67d Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 3 Jul 2014 13:16:08 +0200 Subject: pull request feedback --- src/compiler/scala/tools/nsc/typechecker/Macros.scala | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 6801dc068c..f0770e84c4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -912,6 +912,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { private val delayed = perRunCaches.newWeakMap[Tree, scala.collection.mutable.Set[Int]] private def isDelayed(expandee: Tree) = delayed contains expandee private def calculateUndetparams(expandee: Tree): scala.collection.mutable.Set[Int] = + // !settings.XfundepMaterialization.value implies forced.isEmpty if (forced(expandee)) scala.collection.mutable.Set[Int]() else delayed.getOrElse(expandee, { val calculated = scala.collection.mutable.Set[Symbol]() -- cgit v1.2.3 From 5966a11ae1f494f1c7271a6a574b3ff4365f5847 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 9 Sep 2014 16:18:24 +0200 Subject: -Xfundep-materialization => -Yfundep-materialization To quote gkossakowski: Thinking about it more, could we hide this behind 'Y' flag instead? We have lesser obligation to keep around Y flags and this is something we should remove from 2.11/2.12. --- src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/Macros.scala | 6 +++--- test/files/run/t5923a-fundep.flags | 2 +- test/files/run/t5923c.flags | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 3a6d183c32..56fc4d7594 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -111,7 +111,6 @@ trait ScalaSettings extends AbsScalaSettings val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") val Xdivergence211 = BooleanSetting ("-Xdivergence211", "Turn on the 2.11 behavior of implicit divergence not terminating recursive implicit searches (SI-7291).") - val XfundepMaterialization = BooleanSetting("-Xfundep-materialization", "Turn on the 2.11 behavior of macro expansion being able to influence type inference in implicit searches") /** Compatibility stubs for options whose value name did * not previously match the option name. @@ -180,6 +179,7 @@ trait ScalaSettings extends AbsScalaSettings val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() + val YfundepMaterialization = BooleanSetting("-Yfundep-materialization", "Turn on the 2.11 behavior of macro expansion being able to influence type inference in implicit searches") def stop = stopAfter diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index f0770e84c4..c0844ec8fc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -713,7 +713,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { var expectedTpe = expandee.tpe if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType - if (settings.XfundepMaterialization.value) { + if (settings.YfundepMaterialization.value) { // approximation is necessary for whitebox macros to guide type inference // read more in the comments for onDelayed below val undetparams = expectedTpe collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol } @@ -785,7 +785,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // but is enabled by default in Scala 2.11. val shouldInstantiate = typer.context.undetparams.nonEmpty && !inPolyMode(mode) if (shouldInstantiate) { - if (settings.XfundepMaterialization.value) { + if (settings.YfundepMaterialization.value) { forced += delayed typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false) macroExpand(typer, delayed, mode, pt) @@ -912,7 +912,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { private val delayed = perRunCaches.newWeakMap[Tree, scala.collection.mutable.Set[Int]] private def isDelayed(expandee: Tree) = delayed contains expandee private def calculateUndetparams(expandee: Tree): scala.collection.mutable.Set[Int] = - // !settings.XfundepMaterialization.value implies forced.isEmpty + // !settings.YfundepMaterialization.value implies forced.isEmpty if (forced(expandee)) scala.collection.mutable.Set[Int]() else delayed.getOrElse(expandee, { val calculated = scala.collection.mutable.Set[Symbol]() diff --git a/test/files/run/t5923a-fundep.flags b/test/files/run/t5923a-fundep.flags index c8b4511960..384b48d1b0 100644 --- a/test/files/run/t5923a-fundep.flags +++ b/test/files/run/t5923a-fundep.flags @@ -1 +1 @@ --Xfundep-materialization \ No newline at end of file +-Yfundep-materialization \ No newline at end of file diff --git a/test/files/run/t5923c.flags b/test/files/run/t5923c.flags index c8b4511960..384b48d1b0 100644 --- a/test/files/run/t5923c.flags +++ b/test/files/run/t5923c.flags @@ -1 +1 @@ --Xfundep-materialization \ No newline at end of file +-Yfundep-materialization \ No newline at end of file -- cgit v1.2.3