Skip to content

Commit

Permalink
refactors macroExpandApply
Browse files Browse the repository at this point in the history
Moves some code around to clearly define the concepts that the method
operates on: 1) `innerPt`, which is expected type provided by the macro
def return type, 2) `outerPt`, which is expected type provided by the
enclosing context.

Once everything is clearly defined, the gist of the expander fits in
a few lines in its end. If blackbox, do this. If whitebox, do that.

Note that unlike the subsequent commit, this commit doesn’t change
the way how macro expansion works. It just clears everything out, so that
the upcoming changes can be applied in a concise and comprehensible manner.
  • Loading branch information
xeno-by committed Dec 10, 2013
1 parent b345b42 commit bd615c6
Showing 1 changed file with 35 additions and 27 deletions.
62 changes: 35 additions & 27 deletions src/compiler/scala/tools/nsc/typechecker/Macros.scala
Expand Up @@ -600,39 +600,47 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}

/** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
* @param outerPt Expected type that comes from enclosing context (something that's traditionally called `pt`).
* @param innerPt Expected type that comes from the signature of a macro def, possibly wildcarded to help type inference.
* @see MacroExpander
*/
def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, pt) {
override def onSuccess(expanded0: Tree) = {
def approximate(tp: Type) = {
def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, outerPt: Type): Tree = {
object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, outerPt) {
lazy val innerPt = {
val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
if (isBlackbox(expandee)) tp
else {
// approximation is necessary for whitebox macros to guide type inference
// read more in the comments for onDelayed below
if (isBlackbox(expandee)) tp
val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
deriveTypeWithWildcards(undetparams)(tp)
}
}
override def onSuccess(expanded0: Tree) = {
// prematurely annotate the tree with a macro expansion attachment
// so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
linkExpandeeAndExpanded(expandee, expanded0)

def typecheck(label: String, tree: Tree, pt: Type): Tree = {
if (tree.isErrorTyped) tree
else {
val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
deriveTypeWithWildcards(undetparams)(tp)
if (macroDebugVerbose) println(s"$label (against pt = $pt): $tree")
// `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
// therefore we need to re-enable the conversions back temporarily
val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}")
result
}
}
val macroPt = approximate(if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe)
val expanded = if (isBlackbox(expandee)) atPos(enclosingMacroPosition.focus)(Typed(expanded0, TypeTree(macroPt))) else expanded0

// prematurely annotate the tree with a macro expansion attachment
// so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
linkExpandeeAndExpanded(expandee, expanded)

// `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
// therefore we need to re-enable the conversions back temporarily
if (macroDebugVerbose) println(s"typecheck #1 (against macroPt = $macroPt): $expanded")
val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, macroPt))
if (expanded1.isErrorTyped) {
if (macroDebugVerbose) println(s"typecheck #1 has failed: ${typer.context.reportBuffer.errors}")
expanded1
if (isBlackbox(expandee)) {
val expanded1 = atPos(enclosingMacroPosition.focus)(Typed(expanded0, TypeTree(innerPt)))
val expanded2 = typecheck("blackbox typecheck #1", expanded1, innerPt)
typecheck("blackbox typecheck #2", expanded1, outerPt)
} else {
if (macroDebugVerbose) println(s"typecheck #2 (against pt = $pt): $expanded1")
val expanded2 = typer.context.withImplicitsEnabled(super.onSuccess(expanded1))
if (macroDebugVerbose && expanded2.isErrorTyped) println(s"typecheck #2 has failed: ${typer.context.reportBuffer.errors}")
expanded2
val expanded1 = expanded0
val expanded2 = typecheck("whitebox typecheck #1", expanded1, innerPt)
typecheck("whitebox typecheck #2", expanded2, outerPt)
}
}
override def onDelayed(delayed: Tree) = {
Expand Down Expand Up @@ -686,11 +694,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
// Thanks to that the materializer can take a look at what's going on and react accordingly.
val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode
if (shouldInstantiate) {
if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt)
if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
else {
forced += delayed
typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false)
macroExpandApply(typer, delayed, mode, pt)
typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
macroExpandApply(typer, delayed, mode, outerPt)
}
} else delayed
}
Expand Down

0 comments on commit bd615c6

Please sign in to comment.