Without any restrictions on macro expansion, macro applications will expand at will, and when type inference is involved, expansions will end up using yet uninferred type params.
Without any restrictions on macro expansion, macro applications will expand at will, and when type inference is involved, expansions will end up using yet uninferred type params.
For some macros this might be ok (thanks to TreeTypeSubstituter that replaces the occurrences of undetparams with their inferred values), but in general case this won't work. E.g. for reification simple substitution is not enough - we actually need to re-reify inferred types.
Luckily, there exists a very simple way to fix the problem: delay macro expansion until everything is inferred. Here are the exact rules. Macro application gets delayed if any of its subtrees contain: 1) type vars (tpe.isInstanceOf[TypeVar]) // [Eugene] this check is disabled right now, because TypeVars seem to be created from undetparams anyways 2) undetparams (sym.isTypeParameter && !sym.isSkolem)
Should become private again once we're done with migrating typetag generation from implicits
Performs macro expansion:
1) Checks whether the expansion needs to be delayed (see mustDelayMacroExpansion)
2) Loads macro implementation using
macroMirror
3) Synthesizes invocation arguments for the macro implementation
4) Checks that the result is a tree bound to this universe
5) Typechecks the result against the return type of the macro definition
Performs macro expansion:
1) Checks whether the expansion needs to be delayed (see mustDelayMacroExpansion)
2) Loads macro implementation using
macroMirror
3) Synthesizes invocation arguments for the macro implementation
4) Checks that the result is a tree bound to this universe
5) Typechecks the result against the return type of the macro definition
If -Ymacro-debug-lite is enabled, you will get basic notifications about macro expansion along with macro expansions logged in the form that can be copy/pasted verbatim into REPL.
If -Ymacro-debug-verbose is enabled, you will get detailed log of how exactly this function performs class loading and method resolution in order to load the macro implementation. The log will also include other non-trivial steps of macro expansion.
@return the expansion result if the expansion has been successful, the fallback method invocation if the expansion has been unsuccessful, but there is a fallback, the expandee unchanged if the expansion has been delayed, the expandee fully expanded if the expansion has been delayed before and has been expanded now, the expandee with an error marker set if the expansion has been cancelled due malformed arguments or implementation the expandee with an error marker set if there has been an error
Performs macro expansion on all subtrees of a given tree.
Performs macro expansion on all subtrees of a given tree.
Innermost macros are expanded first, outermost macros are expanded last.
See the documentation for macroExpand for more information.
Keeps track of macros in-flight.
Keeps track of macros in-flight.
See more informations in comments to openMacros in
scala.reflect.makro.Context.
As specified above, body of a macro definition must reference its implementation.
As specified above, body of a macro definition must reference its implementation. This function verifies that the body indeed refers to a method, and that the referenced macro implementation is compatible with the given macro definition.
This means that macro implementation (fooBar in example above) must: 1) Refer to a statically accessible, non-overloaded method. 2) Have the right parameter lists as outlined in the SIP / in the doc comment of this class.
typechecked rhs of the given macro definition
Code to deal with macros, namely with: * Compilation of macro definitions * Expansion of macro applications
Say we have in a class C:
def foo[T](xs: List[T]): T = macro fooBar
Then fooBar needs to point to a static method of the following form:
def fooBar[T: c.TypeTag] (c: scala.reflect.makro.Context) (xs: c.Expr[List[T]]) : c.mirror.Tree = { ... }
Then, if foo is called in qual.foo[Int](elems), where qual: D, the macro application is expanded to a reflective invocation of fooBar with parameters
(simpleMacroContext{ type PrefixType = D; val prefix = qual }) (Expr(elems)) (TypeTag(Int))