diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2013-05-12 14:53:25 +0200 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2013-05-12 22:18:59 +0200 |
commit | adef4b526833a804dcbc160ff67c83e42c9fc2ee (patch) | |
tree | efa187b493141b363053765e07cff8e401bcc7be | |
parent | 7f29f8512d4975cf3a5a0b536a8910e4e3b4316b (diff) | |
download | scala-adef4b526833a804dcbc160ff67c83e42c9fc2ee.tar.gz scala-adef4b526833a804dcbc160ff67c83e42c9fc2ee.tar.bz2 scala-adef4b526833a804dcbc160ff67c83e42c9fc2ee.zip |
SI-5923 instantiates targs in deferred macro applications
In January I submitted a pull request that, as I thought back then,
fixes SI-5923: https://github.com/scala/scala/commit/fe60284769.
The pull request was merged, and everyone was happy that the bug got fixed.
Unfortunately, the fix was: a) incomplete, b) broke something else,
as noticed by Miles in https://groups.google.com/d/topic/scala-internals/7pA9CiiD3u8/discussion.
Now we got a real fix in 2.10.x (https://github.com/scala/scala/commit/90ac5c4e13),
and it's my pleasure to port it to master.
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Macros.scala | 25 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 20 | ||||
-rw-r--r-- | test/files/run/t5923a.check | 3 | ||||
-rw-r--r-- | test/files/run/t5923a/Macros_1.scala | 14 | ||||
-rw-r--r-- | test/files/run/t5923a/Test_2.scala | 5 | ||||
-rw-r--r-- | test/files/run/t5923b.check | 3 | ||||
-rw-r--r-- | test/files/run/t5923b/Test.scala | 7 |
7 files changed, 67 insertions, 10 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index d07297bb35..ecae55562b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -394,7 +394,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { case Fallback(fallback) => typer.typed1(fallback, EXPRmode, WildcardType) case Delayed(delayed) => - delayed + typer.instantiate(delayed, EXPRmode, WildcardType) case Skipped(skipped) => skipped case Failure(failure) => @@ -778,6 +778,29 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { expanded2 } } + override def onDelayed(delayed: Tree) = { + // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee), + // then there are two possible situations we're in: + // 1) We're in POLYmode, when the typer tests the waters wrt type inference + // (e.g. as in typedArgToPoly in doTypedApply). + // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type + // (e.g. if we're an argument to a function call, then this means that no previous argument lists + // can determine our type variables for us). + // + // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that + // there's nothing outrageously wrong with our undetermined type params (from what I understand!). + // + // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer + // the undetermined type params. Therefore we need to do something ourselves or otherwise this + // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum + // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases, + // but sometimes, if the inferencer lacks information, it will be forced to approximate. This prevents + // an important class of macros, fundep materializers, from working, which I perceive is a problem we need to solve. + // For details see SI-7470. + val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode + if (shouldInstantiate) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) + else delayed + } } expander(expandee) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 001808e6bc..2de59056ef 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1091,7 +1091,6 @@ trait Typers extends Adaptations with Tags { instantiateToMethodType(mt) case _ => - def vanillaAdapt(tree: Tree) = { def shouldInsertApply(tree: Tree) = mode.inAll(EXPRmode | FUNmode) && (tree.tpe match { case _: MethodType | _: OverloadedType | _: PolyType => false case _ => applyPossible @@ -1107,16 +1106,15 @@ trait Typers extends Adaptations with Tags { } if (tree.isType) adaptType() + else if (mode.inExprModeButNot(FUNmode) && treeInfo.isMacroApplication(tree)) + macroExpandApply(this, tree, mode, pt) else if (mode.inAll(PATTERNmode | FUNmode)) adaptConstrPattern() else if (shouldInsertApply(tree)) insertApply() - else if (!context.undetparams.isEmpty && !mode.inPolyMode) { // (9) + else if (context.undetparams.nonEmpty && !mode.inPolyMode) { // (9) assert(!mode.inHKMode, mode) //@M - if (mode.inExprModeButNot(FUNmode) && pt.typeSymbol == UnitClass) - instantiateExpectingUnit(tree, mode) - else - instantiate(tree, mode, pt) + instantiatePossiblyExpectingUnit(tree, mode, pt) } else if (tree.tpe <:< pt) { tree } else { @@ -1242,9 +1240,6 @@ trait Typers extends Adaptations with Tags { fallBack } } - val tree1 = if (mode.inExprModeButNot(FUNmode) && treeInfo.isMacroApplication(tree)) macroExpandApply(this, tree, mode, pt) else tree - if (tree == tree1) vanillaAdapt(tree1) else tree1 - } } def instantiate(tree: Tree, mode: Mode, pt: Type): Tree = { @@ -1264,6 +1259,13 @@ trait Typers extends Adaptations with Tags { } } + def instantiatePossiblyExpectingUnit(tree: Tree, mode: Mode, pt: Type): Tree = { + if (mode.inExprModeButNot(FUNmode) && pt.typeSymbol == UnitClass) + instantiateExpectingUnit(tree, mode) + else + instantiate(tree, mode, pt) + } + private def isAdaptableWithView(qual: Tree) = { val qtpe = qual.tpe.widen ( !isPastTyper diff --git a/test/files/run/t5923a.check b/test/files/run/t5923a.check new file mode 100644 index 0000000000..7165b734ac --- /dev/null +++ b/test/files/run/t5923a.check @@ -0,0 +1,3 @@ +C(Int) +C(String) +C(Nothing) diff --git a/test/files/run/t5923a/Macros_1.scala b/test/files/run/t5923a/Macros_1.scala new file mode 100644 index 0000000000..6d21362c4d --- /dev/null +++ b/test/files/run/t5923a/Macros_1.scala @@ -0,0 +1,14 @@ +import scala.reflect.macros.Context +import language.experimental.macros + +case class C[T](t: String) +object C { + implicit def foo[T]: C[T] = macro Macros.impl[T] +} + +object Macros { + def impl[T: c.WeakTypeTag](c: Context) = { + import c.universe._ + reify(C[T](c.literal(weakTypeOf[T].toString).splice)) + } +}
\ No newline at end of file diff --git a/test/files/run/t5923a/Test_2.scala b/test/files/run/t5923a/Test_2.scala new file mode 100644 index 0000000000..001ff9aea8 --- /dev/null +++ b/test/files/run/t5923a/Test_2.scala @@ -0,0 +1,5 @@ +object Test extends App { + println(implicitly[C[Int]]) + println(implicitly[C[String]]) + println(implicitly[C[Nothing]]) +}
\ No newline at end of file diff --git a/test/files/run/t5923b.check b/test/files/run/t5923b.check new file mode 100644 index 0000000000..d56076f84e --- /dev/null +++ b/test/files/run/t5923b.check @@ -0,0 +1,3 @@ +class [Ljava.lang.Object; +class [Ljava.lang.Object; +class [Ljava.lang.Object; diff --git a/test/files/run/t5923b/Test.scala b/test/files/run/t5923b/Test.scala new file mode 100644 index 0000000000..7c2627462a --- /dev/null +++ b/test/files/run/t5923b/Test.scala @@ -0,0 +1,7 @@ +object Test extends App { + import scala.collection.generic.CanBuildFrom + val cbf = implicitly[CanBuildFrom[Nothing, Nothing, Array[Nothing]]] + println(cbf().result.getClass) + println(new Array[Nothing](0).getClass) + println(Array[Nothing]().getClass) +}
\ No newline at end of file |