diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/files/run/t5923a/Macros_1.scala | 42 | ||||
-rw-r--r-- | test/files/run/t5923c.check | 1 | ||||
-rw-r--r-- | test/files/run/t5923c/Macros_1.scala | 39 | ||||
-rw-r--r-- | test/files/run/t5923c/Test_2.scala | 12 | ||||
-rw-r--r-- | test/files/run/t5923d.check | 0 | ||||
-rw-r--r-- | test/files/run/t5923d/Macros_1.scala | 9 | ||||
-rw-r--r-- | test/files/run/t5923d/Test_2.scala | 7 |
7 files changed, 108 insertions, 2 deletions
diff --git a/test/files/run/t5923a/Macros_1.scala b/test/files/run/t5923a/Macros_1.scala index 6d21362c4d..97076eb102 100644 --- a/test/files/run/t5923a/Macros_1.scala +++ b/test/files/run/t5923a/Macros_1.scala @@ -7,8 +7,46 @@ object C { } object Macros { - def impl[T: c.WeakTypeTag](c: Context) = { + def impl[T](c: Context)(ttag: c.WeakTypeTag[T]) = { import c.universe._ - reify(C[T](c.literal(weakTypeOf[T].toString).splice)) + val ttag0 = ttag; + { + // When we're expanding implicitly[C[Nothing]], the type inferencer will see + // that foo[T] returns C[T] and that we request an implicit of type C[Nothing]. + // + // Then the type inferencer will try to match C[T] against C[Nothing] and infer everything it can infer + // from that match, but not more (e.g. if we were returning Iso[T, U] and the type we were looking at was Iso[Foo, L], + // we wouldn't want U to be auto-inferred to Nothing, as it usually happens with normal methods, + // but would rather want it to remain unknown, so that our macro could take a stab at inferring it: + // see the comments in this commit for more information). + // + // Equipped with common sense, in our case of C[T] and C[Nothing] we would expect T to be inferred as Nothing, and then we + // would expect T in the corresponding macro invocation to be Nothing. Unfortunately it is not that simple. + // + // Internally the type inferencer uses Nothing as a dummy value, which stands for "don't know how to + // infer this type parameter". In the Iso example, matching Iso[T, U] against Iso[Foo, L] would result in + // T being inferred as Foo and U being inferred as Nothing (!!). Then the type inferencer will think: + // "Aha! U ended up being Nothing. This means that I failed to infer it, + // therefore the result of my work is: T -> Foo, U -> still unknown". + // + // That's all very good and works very well until Nothing is a genuine result of type inference, + // as in our original example of inferring T in C[T] from C[Nothing]. In that case, the inferencer becomes confused + // and here in the macro implementation we get weakTypeOf[T] equal to some dummy type carrying a type parameter + // instead of Nothing. + // + // This eccentric behavior of the type inferencer is a long-standing problem in scalac, + // so the best one can do for now until it's fixed is to work around, manually converting + // suspicious T's into Nothings. Of course, this means that we would have to approximate, + // because there's no way to know whether having T here stands for a failed attempt to infer Nothing + // or for a failed attempt to infer anything, but at least we're in full control of making the best + // of this sad situation. + implicit def ttag: WeakTypeTag[T] = { + val tpe = ttag0.tpe + val sym = tpe.typeSymbol.asType + if (sym.isParameter && !sym.isSkolem) TypeTag.Nothing.asInstanceOf[TypeTag[T]] + else ttag0 + } + reify(C[T](c.literal(weakTypeOf[T].toString).splice)) + } } }
\ No newline at end of file diff --git a/test/files/run/t5923c.check b/test/files/run/t5923c.check new file mode 100644 index 0000000000..bed7429108 --- /dev/null +++ b/test/files/run/t5923c.check @@ -0,0 +1 @@ +(23,foo,true) diff --git a/test/files/run/t5923c/Macros_1.scala b/test/files/run/t5923c/Macros_1.scala new file mode 100644 index 0000000000..0b7a3399e2 --- /dev/null +++ b/test/files/run/t5923c/Macros_1.scala @@ -0,0 +1,39 @@ +import language.experimental.macros +import scala.reflect.macros.Context + +trait Iso[T, U] { + def to(t : T) : U + // def from(u : U) : T +} + +object Iso { + implicit def materializeIso[T, U]: Iso[T, U] = macro impl[T, U] + def impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: Context): c.Expr[Iso[T, U]] = { + import c.universe._ + import definitions._ + import Flag._ + + val sym = c.weakTypeOf[T].typeSymbol + if (!sym.isClass || !sym.asClass.isCaseClass) c.abort(c.enclosingPosition, s"$sym is not a case class") + val fields = sym.typeSignature.declarations.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x } + + def mkTpt() = { + val core = Ident(TupleClass(fields.length) orElse UnitClass) + if (fields.length == 0) core + else AppliedTypeTree(core, fields map (f => TypeTree(f.typeSignature))) + } + + def mkFrom() = { + if (fields.length == 0) Literal(Constant(Unit)) + else Apply(Ident(newTermName("Tuple" + fields.length)), fields map (f => Select(Ident(newTermName("f")), newTermName(f.name.toString.trim)))) + } + + val evidenceClass = ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template( + List(AppliedTypeTree(Ident(newTypeName("Iso")), List(Ident(sym), mkTpt()))), + emptyValDef, + List( + DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), + DefDef(Modifiers(), newTermName("to"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("f"), Ident(sym), EmptyTree))), TypeTree(), mkFrom())))) + c.Expr[Iso[T, U]](Block(List(evidenceClass), Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()))) + } +} diff --git a/test/files/run/t5923c/Test_2.scala b/test/files/run/t5923c/Test_2.scala new file mode 100644 index 0000000000..a00f4ed7db --- /dev/null +++ b/test/files/run/t5923c/Test_2.scala @@ -0,0 +1,12 @@ +// see the comments for macroExpandApply.onDelayed for an explanation of what's tested here +object Test extends App { + case class Foo(i: Int, s: String, b: Boolean) + def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c) + + { + val equiv = foo(Foo(23, "foo", true)) + def typed[T](t: => T) {} + typed[(Int, String, Boolean)](equiv) + println(equiv) + } +}
\ No newline at end of file diff --git a/test/files/run/t5923d.check b/test/files/run/t5923d.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/t5923d.check diff --git a/test/files/run/t5923d/Macros_1.scala b/test/files/run/t5923d/Macros_1.scala new file mode 100644 index 0000000000..f32d1af704 --- /dev/null +++ b/test/files/run/t5923d/Macros_1.scala @@ -0,0 +1,9 @@ +import scala.language.experimental.macros +import scala.reflect.macros.Context + +trait MappedRow +trait RowMapper[T <: MappedRow] +object RowMapper { + implicit def mapper[T <: MappedRow]: RowMapper[T] = macro impl[T] + def impl[T <: MappedRow : c.WeakTypeTag](c: Context) = c.universe.reify(new RowMapper[T]{}) +}
\ No newline at end of file diff --git a/test/files/run/t5923d/Test_2.scala b/test/files/run/t5923d/Test_2.scala new file mode 100644 index 0000000000..6be10227c2 --- /dev/null +++ b/test/files/run/t5923d/Test_2.scala @@ -0,0 +1,7 @@ +class RowA extends MappedRow +class RowB extends MappedRow + +object Test extends App { + implicitly[RowMapper[RowA]] + implicitly[RowMapper[RowB]] +}
\ No newline at end of file |