Using macros breaks type inference

70 views
Skip to first unread message

Paolo Giarrusso

unread,
Aug 28, 2012, 4:17:47 PM8/28/12
to scala-i...@googlegroups.com
I have an identity macro named `macroId` and an expression `expr` such
that type inference breaks on `macroId expr` compared to `expr`: while
the latter succeeds, the former gives a compile-time error because
type inference gives _a different result_.

Of course, that's a problem when trying to write more interesting
macros. I checked this on -M7 and on 2.10.x HEAD.

This bothers me — read on if that bothers you, too: a minimized bug
report follows. I plan to raise a ticket if this is accepted as valid,
and I can provide additional data on request.

## Identity macro [*1]:
def macroId(arg: Any) = macro macroId_impl
def macroId_impl(c: Context)(arg: c.Expr[Any]): c.Expr[Any] = arg

This is supposed to be an identity macro; I think you'll agree.

## Test expressions:
val coll: Exp[List[Int]] = ...
val f15_noMacro = {
coll map (i => i) /*Type inference makes this map[Int, List[Int]]*/
}

val f15 = macroId { //macroId is used here!
coll map (i => i) /*Type inference makes this map[Int, Any] which
causes a compile error.*/
}
where map is a pimped^H enriched method with signature:

def map[U, That <: Traversable[U] with TraversableLike[U, That]](f:
Exp[T] => Exp[U])(implicit c: CanBuildFrom[Repr, U, That]): Exp[That]
= ???

(For details see [*2]).

## Error text:

src/test/scala/ivm/expressiontree/MacroFailureMinimal.scala:27: error:
type arguments [Int,Any] do not conform to method map's type parameter
bounds [U,That <: Traversable[U] with
scala.collection.TraversableLike[U,That]]
coll map (i => i)
^
src/test/scala/ivm/expressiontree/MacroFailureMinimal.scala:31: error:
type arguments [Int,Any] do not conform to method map's type parameter
bounds [U,That <: Traversable[U] with
scala.collection.TraversableLike[U,That]]
coll map (i => i)
^
two errors found

## Additional experiments:

If inside macroId, I also call showRaw:
println("Stringify: " + arg.tree.toString)
println("showRaw: " + showRaw(arg.tree, printTypes = true, printIds = true))
I can verify that type inference is already complete (with Any instead
of the right result) at the time of this call.

Stringify: MacroFailureMinimalHelper.TraversableLikeOps[Int,
List[Int]](MacroFailureMinimalHelper.coll).map[Int, Any](((i: iv
m.expressiontree.MacroFailureMinimalHelper.Exp[Int]) =>
i))(immutable.this.List.canBuildFrom[Int])
showRaw: Apply[1](Apply[2](TypeApply[3](Select[4](Apply[5](TypeApply[6](Select[7](Ident[8](ivm.expressiontree.MacroFailureMin
imalHelper#11488), newTermName("TraversableLikeOps")#11524),
List(TypeTree[9](), TypeTree[10]())), List(Select[11](Ident[8](i
vm.expressiontree.MacroFailureMinimalHelper#11488),
newTermName("coll")#11529))), newTermName("map")#11536),
List(TypeTree[9]
(), TypeTree[12]())),
List(Function[13](List(ValDefNoType(Modifiers(PARAM),
newTermName("i"), TypeTree[14](), EmptyTree)), Id
ent[14](newTermName("i")#28098)))),
List(TypeApply[15](Select[16](Select[17](This[18](newTypeName("immutable")),
scala.collec
tion.immutable.List#15618), newTermName("canBuildFrom")#15876),
List(TypeTree[9]()))))
[...]
[12] TypeRef(ThisType(scala#19), scala.Any#3870, List()) //The culprit
type is here, it seems to me a normal scala.Any without surprises.

Furthermore, the problem only happens when the "problematic call" (to
map) is at the outer layer (close to the macro call). The following
works instead:
val f15_show_2 = macroId { //macroId is used here!
coll map (i => i) filter (_ => false) /*Calling filter here seems
to protect the call to map from this bug. filter's signature is simple
enough for this bug not to hit it.*/
}


*1 https://github.com/ps-mr/LinqOnSteroids/blob/topic/macro-bugreport/src/main/scala/ivm/expressiontree/MacroFrontend.scala#L44
*2 https://github.com/ps-mr/LinqOnSteroids/blob/topic/macro-bugreport/src/test/scala/ivm/expressiontree/MacroFailureMinimal.scala#L12
--
Paolo Giarrusso - Ph.D. Student, Philipps-University Marburg
http://www.informatik.uni-marburg.de/~pgiarrusso/

Eugene Burmako

unread,
Aug 28, 2012, 4:39:56 PM8/28/12
to scala-i...@googlegroups.com

Sorry I don't have time to investigate this right now. Could you please take a look?

Roland Kuhn

unread,
Aug 29, 2012, 2:00:59 AM8/29/12
to scala-i...@googlegroups.com, scala-i...@googlegroups.com
Given Eugene's reply I might be missing something, but is it not your code which throws precise types out the window by declaring in&out as Any? I think the same would happen if you used a normal method with the same signature. I'd try adding a TypeTag and changing in&out to Expr[T].


Regards,

Roland Kuhn
Typesafe — The software stack for applications that scale
twitter: @rolandkuhn

Paolo G. Giarrusso

unread,
Aug 29, 2012, 2:02:05 PM8/29/12
to scala-i...@googlegroups.com
Eugene, Roland, you're both right, thanks for the answers.

* TypeTag is now AbsTypeTag (even though the SIP is not up-to-date with this—I raised a comment). So, a minimal correct identity macro is:

  def macroId[T](arg: T): T = macro macroId_impl[T]
  def macroId_impl[T: c.AbsTypeTag](c: Context)(arg: c.Expr[T]): c.Expr[T] = arg

And this indeed works.

* As Roland suggested, an identity function like this has the same problem:
  def idAny(v: Any): Any = v
I agree with Eugene that the type inferencer's behavior is not the most desirable, but this bug is similar to many other very hard-to-fix type inference limitations, so I think this is not going to change soon. Therefore, using Any for parameters, in macro declarations or otherwise, should just be taught to be bad style. But such examples appear in SIP-16, both before and after the introduction of type parameters, without warning, like:

def assert(cond: Boolean, msg: Any) = macro Asserts.assertImpl

It's rather unlikely that Any there will cause problems, still a warning on the page, as long as it serves as a tutorial, might be appropriate. I'll add a warning there and add some information to the JIRA ticket, since the basic problem is similar.

Best regards

Paolo G. Giarrusso

unread,
Aug 29, 2012, 2:03:04 PM8/29/12
to scala-i...@googlegroups.com
And I forgot to say: sorry for the noise.

Best regards
Reply all
Reply to author
Forward
0 new messages