@@ -361,7 +361,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
361
361
*/
362
362
case class MacroArgs (c : MacroContext , others : List [Any ])
363
363
364
- private def macroArgs (typer : Typer , expandee : Tree ): MacroArgs = {
364
+ def macroArgs (typer : Typer , expandee : Tree ): MacroArgs = {
365
365
val macroDef = expandee.symbol
366
366
val paramss = macroDef.paramss
367
367
val treeInfo .Applied (core, targs, argss) = expandee
@@ -473,10 +473,10 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
473
473
/** Keeps track of macros in-flight.
474
474
* See more informations in comments to `openMacros` in `scala.reflect.macros.WhiteboxContext`.
475
475
*/
476
- private var _openMacros = List [MacroContext ]()
476
+ var _openMacros = List [MacroContext ]()
477
477
def openMacros = _openMacros
478
- private def pushMacroContext (c : MacroContext ) = _openMacros ::= c
479
- private def popMacroContext () = _openMacros = _openMacros.tail
478
+ def pushMacroContext (c : MacroContext ) = _openMacros ::= c
479
+ def popMacroContext () = _openMacros = _openMacros.tail
480
480
def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition ) getOrElse NoPosition
481
481
482
482
/** Describes the role that the macro expandee is performing.
@@ -527,7 +527,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
527
527
* the expandee with an error marker set if the expansion has been cancelled due malformed arguments or implementation
528
528
* the expandee with an error marker set if there has been an error
529
529
*/
530
- private abstract class MacroExpander [Result : ClassTag ](val role : MacroRole , val typer : Typer , val expandee : Tree ) {
530
+ abstract class MacroExpander [Result : ClassTag ](val role : MacroRole , val typer : Typer , val expandee : Tree ) {
531
531
def allowExpandee (expandee : Tree ): Boolean = true
532
532
def allowExpanded (expanded : Tree ): Boolean = true
533
533
def allowedExpansions : String = " anything"
@@ -592,136 +592,131 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
592
592
}
593
593
}
594
594
595
- /** Expands a tree that carries a term, which happens to be a term macro.
596
- * @see MacroExpander
597
- */
598
- private abstract class TermMacroExpander (role : MacroRole , typer : Typer , expandee : Tree , mode : Mode , pt : Type )
599
- extends MacroExpander [Tree ](role, typer, expandee) {
600
- override def allowedExpansions : String = " term trees"
601
- override def allowExpandee (expandee : Tree ) = expandee.isTerm
602
- override def onSuccess (expanded : Tree ) = typer.typed(expanded, mode, pt)
603
- override def onFallback (fallback : Tree ) = typer.typed(fallback, mode, pt)
604
- }
605
-
606
595
/** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
607
596
* @param outerPt Expected type that comes from enclosing context (something that's traditionally called `pt`).
608
597
* @param innerPt Expected type that comes from the signature of a macro def, possibly wildcarded to help type inference.
609
- * @see MacroExpander
610
598
*/
611
- def macroExpand (typer : Typer , expandee : Tree , mode : Mode , outerPt : Type ): Tree = {
612
- object expander extends TermMacroExpander (APPLY_ROLE , typer, expandee, mode, outerPt) {
613
- lazy val innerPt = {
614
- val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
615
- if (isBlackbox(expandee)) tp
616
- else {
617
- // approximation is necessary for whitebox macros to guide type inference
618
- // read more in the comments for onDelayed below
619
- val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
620
- deriveTypeWithWildcards(undetparams)(tp)
621
- }
599
+ class DefMacroExpander (typer : Typer , expandee : Tree , mode : Mode , outerPt : Type )
600
+ extends MacroExpander [Tree ](APPLY_ROLE , typer, expandee) {
601
+ lazy val innerPt = {
602
+ val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
603
+ if (isBlackbox(expandee)) tp
604
+ else {
605
+ // approximation is necessary for whitebox macros to guide type inference
606
+ // read more in the comments for onDelayed below
607
+ val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
608
+ deriveTypeWithWildcards(undetparams)(tp)
622
609
}
623
- override def onSuccess (expanded0 : Tree ) = {
624
- // prematurely annotate the tree with a macro expansion attachment
625
- // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
626
- linkExpandeeAndExpanded(expandee, expanded0)
627
-
628
- def typecheck (label : String , tree : Tree , pt : Type ): Tree = {
629
- if (tree.isErrorTyped) tree
630
- else {
631
- if (macroDebugVerbose) println(s " $label (against pt = $pt): $tree" )
632
- // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
633
- // therefore we need to re-enable the conversions back temporarily
634
- val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
635
- if (result.isErrorTyped && macroDebugVerbose) println(s " $label has failed: ${typer.context.reportBuffer.errors}" )
636
- result
637
- }
638
- }
610
+ }
611
+ override def onSuccess (expanded0 : Tree ) = {
612
+ // prematurely annotate the tree with a macro expansion attachment
613
+ // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
614
+ linkExpandeeAndExpanded(expandee, expanded0)
639
615
640
- if (isBlackbox(expandee)) {
641
- val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed (expanded0, TypeTree (innerPt)))
642
- typecheck(" blackbox typecheck" , expanded1, outerPt)
643
- } else {
644
- val expanded1 = expanded0
645
- val expanded2 = typecheck(" whitebox typecheck #1" , expanded1, outerPt)
646
- typecheck(" whitebox typecheck #2" , expanded2, innerPt)
616
+ def typecheck (label : String , tree : Tree , pt : Type ): Tree = {
617
+ if (tree.isErrorTyped) tree
618
+ else {
619
+ if (macroDebugVerbose) println(s " $label (against pt = $pt): $tree" )
620
+ // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
621
+ // therefore we need to re-enable the conversions back temporarily
622
+ val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
623
+ if (result.isErrorTyped && macroDebugVerbose) println(s " $label has failed: ${typer.context.reportBuffer.errors}" )
624
+ result
647
625
}
648
626
}
649
- override def onDelayed (delayed : Tree ) = {
650
- // =========== THE SITUATION ===========
651
- //
652
- // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee),
653
- // then there are two possible situations we're in:
654
- // 1) We're in POLYmode, when the typer tests the waters wrt type inference
655
- // (e.g. as in typedArgToPoly in doTypedApply).
656
- // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type
657
- // (e.g. if we're an argument to a function call, then this means that no previous argument lists
658
- // can determine our type variables for us).
659
- //
660
- // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that
661
- // there's nothing outrageously wrong with our undetermined type params (from what I understand!).
662
- //
663
- // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer
664
- // the undetermined type params. Therefore we need to do something ourselves or otherwise this
665
- // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum
666
- // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases,
667
- // but sometimes, if the inferencer lacks information, it will be forced to approximate.
668
- //
669
- // =========== THE PROBLEM ===========
670
- //
671
- // Consider the following example (thanks, Miles!):
672
- //
673
- // Iso represents an isomorphism between two datatypes:
674
- // 1) An arbitrary one (e.g. a random case class)
675
- // 2) A uniform representation for all datatypes (e.g. an HList)
676
- //
677
- // trait Iso[T, U] {
678
- // def to(t : T) : U
679
- // def from(u : U) : T
680
- // }
681
- // implicit def materializeIso[T, U]: Iso[T, U] = macro ???
682
- //
683
- // case class Foo(i: Int, s: String, b: Boolean)
684
- // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c)
685
- // foo(Foo(23, "foo", true))
686
- //
687
- // In the snippet above, even though we know that there's a fundep going from T to U
688
- // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype,
689
- // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information
690
- // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want.
691
- //
692
- // =========== THE SOLUTION (ENABLED ONLY FOR WHITEBOX MACROS) ===========
693
- //
694
- // To give materializers a chance to say their word before vanilla inference kicks in,
695
- // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo)
696
- // and then trigger macro expansion with the undetermined type parameters still there.
697
- // Thanks to that the materializer can take a look at what's going on and react accordingly.
698
- val shouldInstantiate = typer.context.undetparams.nonEmpty && ! mode.inPolyMode
699
- if (shouldInstantiate) {
700
- if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
701
- else {
702
- forced += delayed
703
- typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false )
704
- macroExpand(typer, delayed, mode, outerPt)
705
- }
706
- } else delayed
627
+
628
+ if (isBlackbox(expandee)) {
629
+ val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed (expanded0, TypeTree (innerPt)))
630
+ typecheck(" blackbox typecheck" , expanded1, outerPt)
631
+ } else {
632
+ val expanded1 = expanded0
633
+ val expanded2 = typecheck(" whitebox typecheck #1" , expanded1, outerPt)
634
+ typecheck(" whitebox typecheck #2" , expanded2, innerPt)
707
635
}
708
636
}
637
+ override def onDelayed (delayed : Tree ) = {
638
+ // =========== THE SITUATION ===========
639
+ //
640
+ // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee),
641
+ // then there are two possible situations we're in:
642
+ // 1) We're in POLYmode, when the typer tests the waters wrt type inference
643
+ // (e.g. as in typedArgToPoly in doTypedApply).
644
+ // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type
645
+ // (e.g. if we're an argument to a function call, then this means that no previous argument lists
646
+ // can determine our type variables for us).
647
+ //
648
+ // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that
649
+ // there's nothing outrageously wrong with our undetermined type params (from what I understand!).
650
+ //
651
+ // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer
652
+ // the undetermined type params. Therefore we need to do something ourselves or otherwise this
653
+ // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum
654
+ // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases,
655
+ // but sometimes, if the inferencer lacks information, it will be forced to approximate.
656
+ //
657
+ // =========== THE PROBLEM ===========
658
+ //
659
+ // Consider the following example (thanks, Miles!):
660
+ //
661
+ // Iso represents an isomorphism between two datatypes:
662
+ // 1) An arbitrary one (e.g. a random case class)
663
+ // 2) A uniform representation for all datatypes (e.g. an HList)
664
+ //
665
+ // trait Iso[T, U] {
666
+ // def to(t : T) : U
667
+ // def from(u : U) : T
668
+ // }
669
+ // implicit def materializeIso[T, U]: Iso[T, U] = macro ???
670
+ //
671
+ // case class Foo(i: Int, s: String, b: Boolean)
672
+ // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c)
673
+ // foo(Foo(23, "foo", true))
674
+ //
675
+ // In the snippet above, even though we know that there's a fundep going from T to U
676
+ // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype,
677
+ // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information
678
+ // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want.
679
+ //
680
+ // =========== THE SOLUTION (ENABLED ONLY FOR WHITEBOX MACROS) ===========
681
+ //
682
+ // To give materializers a chance to say their word before vanilla inference kicks in,
683
+ // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo)
684
+ // and then trigger macro expansion with the undetermined type parameters still there.
685
+ // Thanks to that the materializer can take a look at what's going on and react accordingly.
686
+ val shouldInstantiate = typer.context.undetparams.nonEmpty && ! mode.inPolyMode
687
+ if (shouldInstantiate) {
688
+ if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
689
+ else {
690
+ forced += delayed
691
+ typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false )
692
+ macroExpand(typer, delayed, mode, outerPt)
693
+ }
694
+ } else delayed
695
+ }
696
+ override def onFallback (fallback : Tree ) = typer.typed(fallback, mode, outerPt)
697
+ }
698
+
699
+ /** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
700
+ * @see DefMacroExpander
701
+ */
702
+ def macroExpand (typer : Typer , expandee : Tree , mode : Mode , pt : Type ): Tree = {
703
+ val expander = new DefMacroExpander (typer, expandee, mode, pt)
709
704
expander(expandee)
710
705
}
711
706
712
- private sealed abstract class MacroStatus (val result : Tree )
713
- private case class Success (expanded : Tree ) extends MacroStatus (expanded)
714
- private case class Fallback (fallback : Tree ) extends MacroStatus (fallback) { currentRun.seenMacroExpansionsFallingBack = true }
715
- private case class Delayed (delayed : Tree ) extends MacroStatus (delayed)
716
- private case class Skipped (skipped : Tree ) extends MacroStatus (skipped)
717
- private case class Failure (failure : Tree ) extends MacroStatus (failure)
718
- private def Delay (expanded : Tree ) = Delayed (expanded)
719
- private def Skip (expanded : Tree ) = Skipped (expanded)
707
+ sealed abstract class MacroStatus (val result : Tree )
708
+ case class Success (expanded : Tree ) extends MacroStatus (expanded)
709
+ case class Fallback (fallback : Tree ) extends MacroStatus (fallback) { currentRun.seenMacroExpansionsFallingBack = true }
710
+ case class Delayed (delayed : Tree ) extends MacroStatus (delayed)
711
+ case class Skipped (skipped : Tree ) extends MacroStatus (skipped)
712
+ case class Failure (failure : Tree ) extends MacroStatus (failure)
713
+ def Delay (expanded : Tree ) = Delayed (expanded)
714
+ def Skip (expanded : Tree ) = Skipped (expanded)
720
715
721
716
/** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded
722
717
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
723
718
*/
724
- private def macroExpandWithRuntime (typer : Typer , expandee : Tree , runtime : MacroRuntime ): MacroStatus = {
719
+ def macroExpandWithRuntime (typer : Typer , expandee : Tree , runtime : MacroRuntime ): MacroStatus = {
725
720
val wasDelayed = isDelayed(expandee)
726
721
val undetparams = calculateUndetparams(expandee)
727
722
val nowDelayed = ! typer.context.macrosEnabled || undetparams.nonEmpty
@@ -778,7 +773,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
778
773
/** Expands a macro when a runtime (i.e. the macro implementation) cannot be loaded
779
774
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
780
775
*/
781
- private def macroExpandWithoutRuntime (typer : Typer , expandee : Tree ): MacroStatus = {
776
+ def macroExpandWithoutRuntime (typer : Typer , expandee : Tree ): MacroStatus = {
782
777
import typer .TyperErrorGen ._
783
778
val fallbackSym = expandee.symbol.nextOverriddenSymbol orElse MacroImplementationNotFoundError (expandee)
784
779
macroLogLite(s " falling back to: $fallbackSym" )
0 commit comments