Type de retour statique des macros Scala

J’ai donc cette macro:

import language.experimental.macros import scala.reflect.macros.Context class Foo class Bar extends Foo { def launchMissiles = "launching" } object FooExample { def foo: Foo = macro foo_impl def foo_impl(c: Context): c.Expr[Foo] = c.Expr[Foo](c.universe.reify(new Bar).tree) } 

J’ai dit trois fois que je voulais que foo renvoie un Foo , et pourtant je peux faire ce qui suit (dans 2.10.0-RC3):

 scala> FooExample.foo res0: Bar = Bar@4118f8dd scala> res0.launchMissiles res1: Ssortingng = launching 

La même chose se produit si je supprime les parameters de type sur c.Expr . Si je veux vraiment m’assurer que quiconque appelle foo ne peut pas voir qu’il obtient une Bar , je dois append une atsortingbution de type dans l’arborescence elle-même.

C’est en fait assez bien – cela signifie par exemple que je peux pointer une macro sur un schéma quelconque et créer une sous-classe anonyme de certaines classes de Vocabulary avec des méthodes membres représentant des termes du vocabulaire, et ils seront disponibles sur l’object renvoyé.

J’aimerais comprendre exactement ce que je fais, alors j’ai quelques questions. En premier lieu, quel est le type de retour sur la méthode foo ? Est-ce juste disponible pour la documentation (facultative)? Cela contraint clairement le type de retour (par exemple, je ne peux pas le changer en Int dans ce cas), et si je le supprime entièrement, je reçois une erreur comme celle-ci:

 scala> FooExample.foo :8: error: type mismatch; found : Bar required: Nothing FooExample.foo ^ 

Mais je peux le changer en Any et obtenir toujours une Bar type statique lorsque j’appelle foo .

Deuxièmement, ce comportement est-il spécifié quelque part? Cela semble être un ensemble d’éléments assez élémentaires, mais je n’ai pas été en mesure de rechercher une explication ou une discussion claire.

Ce comportement est sous-spécifié, mais prévu, même s’il peut paraître déroutant. Nous prévoyons de développer le rôle du type de retour dans les signatures de macro, mais pour le moment, je pense que la flexibilité est une bonne chose à avoir.

Parfois, le comportement est incohérent, par exemple lorsque la macro est interceptée au milieu d’une inférence de type, sa signature statique sera utilisée (c’est-à-dire Foo dans votre exemple), pas le type de l’expansion réelle. En effet, l’expansion des macros est intentionnellement retardée jusqu’à ce que l’inférence de type soit effectuée (de sorte que les implémentations de macros voient les types inférés, pas les vars de type). Ceci est un compromis et pas nécessairement le meilleur, nous prévoyons donc de le revoir bientôt: https://issues.scala-lang.org/browse/SI-6755 .

Un autre problème dans ce département concerne les macros implicites. Lorsque le type de retour d’une macro implicite est générique et doit être déduit du type demandé d’une valeur implicite, de mauvaises choses se produisent. Cela rend actuellement impossible l’utilisation de macros pour générer des balises de type: https://issues.scala-lang.org/browse/SI-5923 .