@@ -713,6 +713,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
713713
714714 var expectedTpe = expandee.tpe
715715 if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType
716+ if (settings.XfundepMaterialization .value) {
717+ // approximation is necessary for whitebox macros to guide type inference
718+ // read more in the comments for onDelayed below
719+ val undetparams = expectedTpe collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
720+ expectedTpe = deriveTypeWithWildcards(undetparams)(expectedTpe)
721+ }
722+
716723 // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc
717724 val expanded0 = duplicateAndKeepPositions(expanded)
718725 val expanded1 = typecheck(" macro def return type" , expanded0, expectedTpe)
@@ -766,9 +773,24 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
766773 // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype,
767774 // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information
768775 // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want.
776+ //
777+ // =========== THE SOLUTION ===========
778+ //
779+ // To give materializers a chance to say their word before vanilla inference kicks in,
780+ // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo)
781+ // and then trigger macro expansion with the undetermined type parameters still there.
782+ // Thanks to that the materializer can take a look at what's going on and react accordingly.
783+ //
784+ // NOTE: This functionality is only available under the -Xfundep-materialization flag in Scala 2.10,
785+ // but is enabled by default in Scala 2.11.
769786 val shouldInstantiate = typer.context.undetparams.nonEmpty && ! inPolyMode(mode)
770- if (shouldInstantiate) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt)
771- else delayed
787+ if (shouldInstantiate) {
788+ if (settings.XfundepMaterialization .value) {
789+ forced += delayed
790+ typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false )
791+ macroExpand(typer, delayed, mode, pt)
792+ } else typer.instantiatePossiblyExpectingUnit(delayed, mode, pt)
793+ } else delayed
772794 case Fallback (fallback) =>
773795 typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode , pt))
774796 case Other (result) =>
@@ -886,10 +908,12 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
886908 * 2) undetparams (sym.isTypeParameter && !sym.isSkolem)
887909 */
888910 var hasPendingMacroExpansions = false
911+ private val forced = perRunCaches.newWeakSet[Tree ]
889912 private val delayed = perRunCaches.newWeakMap[Tree , scala.collection.mutable.Set [Int ]]
890913 private def isDelayed (expandee : Tree ) = delayed contains expandee
891914 private def calculateUndetparams (expandee : Tree ): scala.collection.mutable.Set [Int ] =
892- delayed.get(expandee).getOrElse {
915+ if (forced(expandee)) scala.collection.mutable.Set [Int ]()
916+ else delayed.getOrElse(expandee, {
893917 val calculated = scala.collection.mutable.Set [Symbol ]()
894918 expandee foreach (sub => {
895919 def traverse (sym : Symbol ) = if (sym != null && (undetparams contains sym.id)) calculated += sym
@@ -898,7 +922,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
898922 })
899923 macroLogVerbose(" calculateUndetparams: %s" .format(calculated))
900924 calculated map (_.id)
901- }
925+ })
902926 private val undetparams = perRunCaches.newSet[Int ]
903927 def notifyUndetparamsAdded (newUndets : List [Symbol ]): Unit = {
904928 undetparams ++= newUndets map (_.id)
0 commit comments