diff options
Diffstat (limited to 'src/compiler')
12 files changed, 124 insertions, 101 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala index 30ba082a81..9799428b40 100644 --- a/src/compiler/scala/reflect/macros/compiler/Errors.scala +++ b/src/compiler/scala/reflect/macros/compiler/Errors.scala @@ -33,7 +33,7 @@ trait Errors extends Traces { def MacroBundleNonStaticError() = implRefError("macro bundles must be static") - def MacroBundleWrongShapeError() = implRefError("macro bundles must be monomorphic traits extending scala.reflect.macros.Macro and not implementing its `val c: Context` member") + def MacroBundleWrongShapeError() = implRefError("macro bundles must be monomorphic traits extending either BlackboxMacro or WhiteboxMacro and not implementing their `val c: BlackboxContext/WhiteboxContext` member") // compatibility errors diff --git a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala index 9c4db1990b..03d306f593 100644 --- a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala +++ b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala @@ -15,10 +15,6 @@ trait Resolvers { private val runDefinitions = currentRun.runDefinitions import runDefinitions.{Predef_???, _} - /** Determines the type of context implied by the macro def. - */ - val ctxTpe = MacroContextClass.tpe - /** Resolves a macro impl reference provided in the right-hand side of the given macro definition. * * Acceptable shapes of the right-hand side: @@ -44,14 +40,14 @@ trait Resolvers { } val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match { - case SilentResultValue(result) if result.tpe.baseClasses.contains(MacroClass) => + case SilentResultValue(result) if mightBeMacroBundleType(result.tpe) => val bundleProto = result.tpe.typeSymbol val bundlePkg = bundleProto.enclosingPackageClass if (!isMacroBundleProtoType(bundleProto.tpe)) MacroBundleWrongShapeError() if (!bundleProto.owner.isStaticOwner) MacroBundleNonStaticError() // synthesize the bundle, i.e. given a static `trait Foo extends Macro { def expand = ... } ` - // create a top-level definition `class Foo$Bundle(val c: Context) extends Foo` in a package next to `Foo` + // create a top-level definition `class Foo$Bundle(val c: BlackboxContext/WhiteboxContext) extends Foo` in a package next to `Foo` val bundlePid = gen.mkUnattributedRef(bundlePkg) val bundlePrefix = if (bundlePkg == EmptyPackageClass) bundleProto.fullName('$') @@ -59,7 +55,8 @@ trait Resolvers { val bundleName = TypeName(bundlePrefix + tpnme.MACRO_BUNDLE_SUFFIX) val existingBundle = bundleProto.enclosingPackageClass.info.decl(bundleName) if (!currentRun.compiles(existingBundle)) { - def mkContextValDef(flags: Long) = ValDef(Modifiers(flags), nme.c, TypeTree(ctxTpe), EmptyTree) + val contextType = if (isBlackboxMacroBundleType(bundleProto.tpe)) BlackboxContextClass.tpe else WhiteboxContextClass.tpe + def mkContextValDef(flags: Long) = ValDef(Modifiers(flags), nme.c, TypeTree(contextType), EmptyTree) val contextField = mkContextValDef(PARAMACCESSOR) val contextParam = mkContextValDef(PARAM | PARAMACCESSOR) val bundleCtor = DefDef(Modifiers(), nme.CONSTRUCTOR, Nil, List(List(contextParam)), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))) @@ -88,12 +85,13 @@ trait Resolvers { // lazy val (isImplBundle, macroImplOwner, macroImpl, macroImplTargs) = private lazy val dissectedMacroImplRef = macroImplRef match { - case MacroImplReference(isBundle, owner, meth, targs) => (isBundle, owner, meth, targs) + case MacroImplReference(isBundle, isBlackbox, owner, meth, targs) => (isBundle, isBlackbox, owner, meth, targs) case _ => MacroImplReferenceWrongShapeError() } lazy val isImplBundle = dissectedMacroImplRef._1 lazy val isImplMethod = !isImplBundle - lazy val macroImplOwner = dissectedMacroImplRef._2 - lazy val macroImpl = dissectedMacroImplRef._3 - lazy val targs = dissectedMacroImplRef._4 + lazy val isImplBlackbox = dissectedMacroImplRef._2 + lazy val macroImplOwner = dissectedMacroImplRef._3 + lazy val macroImpl = dissectedMacroImplRef._4 + lazy val targs = dissectedMacroImplRef._5 } diff --git a/src/compiler/scala/reflect/macros/compiler/Validators.scala b/src/compiler/scala/reflect/macros/compiler/Validators.scala index 088b108844..e77c129c51 100644 --- a/src/compiler/scala/reflect/macros/compiler/Validators.scala +++ b/src/compiler/scala/reflect/macros/compiler/Validators.scala @@ -49,8 +49,8 @@ trait Validators { map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => { if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam) if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam) - val aparamtpe = aparam.tpe.dealias match { - case RefinedType(List(tpe), Scope(sym)) if tpe =:= ctxTpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe + val aparamtpe = aparam.tpe match { + case MacroContextType(tpe) => tpe case tpe => tpe } checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam) @@ -93,20 +93,20 @@ trait Validators { * * For the following macro impl: * def fooBar[T: c.WeakTypeTag] - * (c: scala.reflect.macros.Context) + * (c: scala.reflect.macros.BlackboxContext) * (xs: c.Expr[List[T]]) * : c.Expr[T] = ... * * This function will return: - * (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]])c.Expr[T] + * (c: scala.reflect.macros.BlackboxContext)(xs: c.Expr[List[T]])c.Expr[T] * * Note that type tag evidence parameters are not included into the result. * Type tag context bounds for macro impl tparams are optional. * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. * * This method cannot be reduced to just macroImpl.info, because macro implementations might - * come in different shapes. If the implementation is an apply method of a Macro-compatible object, - * then it won't have (c: Context) in its parameters, but will rather refer to Macro.c. + * come in different shapes. If the implementation is an apply method of a BlackboxMacro/WhiteboxMacro-compatible object, + * then it won't have (c: BlackboxContext/WhiteboxContext) in its parameters, but will rather refer to BlackboxMacro/WhiteboxMacro.c. * * @param macroImpl The macro implementation symbol */ @@ -123,7 +123,8 @@ trait Validators { * def foo[T](xs: List[T]): T = macro fooBar * * This function will return: - * (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]])c.Expr[T] + * (c: scala.reflect.macros.BlackboxContext)(xs: c.Expr[List[T]])c.Expr[T] or + * (c: scala.reflect.macros.WhiteboxContext)(xs: c.Expr[List[T]])c.Expr[T] * * Note that type tag evidence parameters are not included into the result. * Type tag context bounds for macro impl tparams are optional. @@ -145,6 +146,7 @@ trait Validators { // had to move method's body to an object because of the recursive dependencies between sigma and param object SigGenerator { val cache = scala.collection.mutable.Map[Symbol, Symbol]() + val ctxTpe = if (isImplBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe val ctxPrefix = if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC)) else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c)) diff --git a/src/compiler/scala/reflect/macros/contexts/Context.scala b/src/compiler/scala/reflect/macros/contexts/Context.scala index 1355a839d9..7b79b52a18 100644 --- a/src/compiler/scala/reflect/macros/contexts/Context.scala +++ b/src/compiler/scala/reflect/macros/contexts/Context.scala @@ -3,7 +3,8 @@ package contexts import scala.tools.nsc.Global -abstract class Context extends scala.reflect.macros.Context +abstract class Context extends scala.reflect.macros.BlackboxContext + with scala.reflect.macros.WhiteboxContext with Aliases with Enclosures with Names diff --git a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala index ffdbe11151..7de3341304 100644 --- a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala +++ b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala @@ -45,7 +45,7 @@ trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes type MacroRuntime = MacroArgs => Any class MacroRuntimeResolver(val macroDef: Symbol) extends JavaReflectionResolvers with ScalaReflectionResolvers { - val binding = loadMacroImplBinding(macroDef) + val binding = loadMacroImplBinding(macroDef).get val isBundle = binding.isBundle val className = binding.className val methName = binding.methName diff --git a/src/compiler/scala/reflect/macros/util/Helpers.scala b/src/compiler/scala/reflect/macros/util/Helpers.scala index bb4f2055ad..ff03696524 100644 --- a/src/compiler/scala/reflect/macros/util/Helpers.scala +++ b/src/compiler/scala/reflect/macros/util/Helpers.scala @@ -27,13 +27,13 @@ trait Helpers { import runDefinitions._ val MacroContextUniverse = definitions.MacroContextUniverse - val treeInfo.MacroImplReference(isBundle, _, macroImpl, _) = macroImplRef + val treeInfo.MacroImplReference(isBundle, _, _, macroImpl, _) = macroImplRef val paramss = macroImpl.paramss val ContextParam = paramss match { - case Nil | _ :+ Nil => NoSymbol // no implicit parameters in the signature => nothing to do - case _ if isBundle => macroImpl.owner.tpe member nme.c - case (cparam :: _) :: _ if cparam.tpe <:< MacroContextClass.tpe => cparam - case _ => NoSymbol // no context parameter in the signature => nothing to do + case Nil | _ :+ Nil => NoSymbol // no implicit parameters in the signature => nothing to do + case _ if isBundle => macroImpl.owner.tpe member nme.c + case (cparam :: _) :: _ if isMacroContextType(cparam.tpe) => cparam + case _ => NoSymbol // no context parameter in the signature => nothing to do } def transformTag(param: Symbol): Symbol = param.tpe.dealias match { case TypeRef(SingleType(SingleType(_, ContextParam), MacroContextUniverse), WeakTypeTagClass, targ :: Nil) => transform(param, targ.typeSymbol) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 92f95e282b..7ecc2be9be 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -527,6 +527,9 @@ trait ContextErrors { def TooManyArgsPatternError(fun: Tree) = NormalTypeError(fun, "too many arguments for unapply pattern, maximum = "+definitions.MaxTupleArity) + def BlackboxExtractorExpansion(fun: Tree) = + NormalTypeError(fun, "extractor macros can only be whitebox") + def WrongShapeExtractorExpansion(fun: Tree) = NormalTypeError(fun, "extractor macros can only expand into extractor calls") diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index b1a48f7478..01acbb8cc2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -596,7 +596,7 @@ trait Implicits { // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints val isScalaDoc = context.tree == EmptyTree - val itree = atPos(pos.focus) { + val itree0 = atPos(pos.focus) { if (isLocal && !isScalaDoc) { // SI-4270 SI-5376 Always use an unattributed Ident for implicits in the local scope, // rather than an attributed Select, to detect shadowing. @@ -608,15 +608,16 @@ trait Implicits { Select(gen.mkAttributedQualifier(info.pre), implicitMemberName) } } - typingLog("considering", typeDebug.ptTree(itree)) + val itree1 = if (isBlackbox(info.sym)) suppressMacroExpansion(itree0) else itree0 + typingLog("considering", typeDebug.ptTree(itree1)) - def fail(reason: String): SearchResult = failure(itree, reason) - def fallback = typed1(itree, EXPRmode, wildPt) + def fail(reason: String): SearchResult = failure(itree0, reason) + def fallback = typed1(itree1, EXPRmode, wildPt) try { - val itree1 = if (!isView) fallback else pt match { + val itree2 = if (!isView) fallback else pt match { case Function1(arg1, arg2) => typed1( - atPos(itree.pos)(Apply(itree, List(Ident("<argument>") setType approximate(arg1)))), + atPos(itree0.pos)(Apply(itree1, List(Ident("<argument>") setType approximate(arg1)))), EXPRmode, approximate(arg2) ) match { @@ -647,10 +648,10 @@ trait Implicits { if (Statistics.canEnable) Statistics.incCounter(typedImplicits) - val itree2 = if (isView) treeInfo.dissectApplied(itree1).callee - else adapt(itree1, EXPRmode, wildPt) + val itree3 = if (isView) treeInfo.dissectApplied(itree2).callee + else adapt(itree2, EXPRmode, wildPt) - typingStack.showAdapt(itree, itree2, pt, context) + typingStack.showAdapt(itree0, itree3, pt, context) def hasMatchingSymbol(tree: Tree): Boolean = (tree.symbol == info.sym) || { tree match { @@ -663,21 +664,21 @@ trait Implicits { if (context.hasErrors) fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg) - else if (isLocal && !hasMatchingSymbol(itree1)) + else if (isLocal && !hasMatchingSymbol(itree2)) fail("candidate implicit %s is shadowed by %s".format( - info.sym.fullLocationString, itree1.symbol.fullLocationString)) + info.sym.fullLocationString, itree2.symbol.fullLocationString)) else { val tvars = undetParams map freshVar def ptInstantiated = pt.instantiateTypeParams(undetParams, tvars) - if (matchesPt(itree2.tpe, ptInstantiated, undetParams)) { + if (matchesPt(itree3.tpe, ptInstantiated, undetParams)) { if (tvars.nonEmpty) typingLog("solve", ptLine("tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr))) - val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt), upper = false, lubDepth(itree2.tpe :: pt :: Nil)) + val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt), upper = false, lubDepth(itree3.tpe :: pt :: Nil)) // #2421: check that we correctly instantiated type parameters outside of the implicit tree: - checkBounds(itree2, NoPrefix, NoSymbol, undetParams, targs, "inferred ") + checkBounds(itree3, NoPrefix, NoSymbol, undetParams, targs, "inferred ") context.firstError match { case Some(err) => return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg) @@ -693,7 +694,7 @@ trait Implicits { if (okParams.isEmpty) EmptyTreeTypeSubstituter else { val subst = new TreeTypeSubstituter(okParams, okArgs) - subst traverse itree2 + subst traverse itree3 notifyUndetparamsInferred(okParams, okArgs) subst } @@ -711,9 +712,9 @@ trait Implicits { // This is just called for the side effect of error detection, // see SI-6966 to see what goes wrong if we use the result of this // as the SearchResult. - itree2 match { - case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args) - case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c + itree3 match { + case TypeApply(fun, args) => typedTypeApply(itree3, EXPRmode, fun, args) + case Apply(TypeApply(fun, args), _) => typedTypeApply(itree3, EXPRmode, fun, args) // t2421c case t => t } @@ -721,13 +722,13 @@ trait Implicits { case Some(err) => fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) case None => - val result = new SearchResult(itree2, subst) + val result = new SearchResult(unsuppressMacroExpansion(itree3), subst) if (Statistics.canEnable) Statistics.incCounter(foundImplicits) typingLog("success", s"inferred value of type $ptInstantiated is $result") result } } - else fail("incompatible: %s does not match expected type %s".format(itree2.tpe, ptInstantiated)) + else fail("incompatible: %s does not match expected type %s".format(itree3.tpe, ptInstantiated)) } } catch { @@ -1147,7 +1148,7 @@ trait Implicits { gen.mkAttributedThis(thisSym) case _ => // if `pre` is not a PDT, e.g. if someone wrote - // implicitly[scala.reflect.macros.Context#TypeTag[Int]] + // implicitly[scala.reflect.macros.BlackboxContext#TypeTag[Int]] // then we need to fail, because we don't know the prefix to use during type reification // upd. we also need to fail silently, because this is a very common situation // e.g. quite often we're searching for BaseUniverse#TypeTag, e.g. for a type tag in any universe diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 02fb70f3e5..27920dbd74 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -29,7 +29,7 @@ import Fingerprint._ * Then fooBar needs to point to a static method of the following form: * * def fooBar[T: c.WeakTypeTag] // type tag annotation is optional - * (c: scala.reflect.macros.Context) + * (c: scala.reflect.macros.BlackboxContext) * (xs: c.Expr[List[T]]) * : c.Expr[T] = { * ... @@ -67,7 +67,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { * * This solution is very simple, but unfortunately it's also lacking. If we use it, then * signatures of macro defs become transitively dependent on scala-reflect.jar - * (because they refer to macro impls, and macro impls refer to scala.reflect.macros.Context defined in scala-reflect.jar). + * (because they refer to macro impls, and macro impls refer to scala.reflect.macros.BlackboxContext/WhiteboxContext defined in scala-reflect.jar). * More details can be found in comments to https://issues.scala-lang.org/browse/SI-5940. * * Therefore we have to avoid putting macro impls into binding pickles and come up with our own serialization format. @@ -81,40 +81,42 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { * and various accounting information necessary when composing an argument list for the reflective invocation. */ case class MacroImplBinding( - // Is this macro impl a bundle (a trait extending Macro) or a vanilla def? - val isBundle: Boolean, - // Java class name of the class that contains the macro implementation - // is used to load the corresponding object with Java reflection - className: String, - // method name of the macro implementation - // `className` and `methName` are all we need to reflectively invoke a macro implementation - // because macro implementations cannot be overloaded - methName: String, - // flattens the macro impl's parameter lists having symbols replaced with their fingerprints - // currently fingerprints are calculated solely from types of the symbols: - // * c.Expr[T] => LiftedTyped - // * c.Tree => LiftedUntyped - // * c.WeakTypeTag[T] => Tagged(index of the type parameter corresponding to that type tag) - // * everything else (e.g. scala.reflect.macros.Context) => Other - // f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: Context)(x: c.Expr[T], y: c.Tree): (U, V) = ??? - // `signature` will be equal to List(List(Other), List(LiftedTyped, LiftedUntyped), List(Tagged(0), Tagged(2))) - signature: List[List[Fingerprint]], - // type arguments part of a macro impl ref (the right-hand side of a macro definition) - // these trees don't refer to a macro impl, so we can pickle them as is - targs: List[Tree]) { - + // Is this macro impl a bundle (a trait extending BlackboxMacro or WhiteboxMacro) or a vanilla def? + val isBundle: Boolean, + // Is this macro impl blackbox (i.e. having BlackboxContext in its signature)? + val isBlackbox: Boolean, + // Java class name of the class that contains the macro implementation + // is used to load the corresponding object with Java reflection + className: String, + // method name of the macro implementation + // `className` and `methName` are all we need to reflectively invoke a macro implementation + // because macro implementations cannot be overloaded + methName: String, + // flattens the macro impl's parameter lists having symbols replaced with their fingerprints + // currently fingerprints are calculated solely from types of the symbols: + // * c.Expr[T] => LiftedTyped + // * c.Tree => LiftedUntyped + // * c.WeakTypeTag[T] => Tagged(index of the type parameter corresponding to that type tag) + // * everything else (e.g. scala.reflect.macros.BlackboxContext/WhiteboxContext) => Other + // f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: BlackboxContext)(x: c.Expr[T], y: c.Tree): (U, V) = ??? + // `signature` will be equal to List(List(Other), List(LiftedTyped, LiftedUntyped), List(Tagged(0), Tagged(2))) + signature: List[List[Fingerprint]], + // type arguments part of a macro impl ref (the right-hand side of a macro definition) + // these trees don't refer to a macro impl, so we can pickle them as is + targs: List[Tree]) { // Was this binding derived from a `def ... = macro ???` definition? def is_??? = { val Predef_??? = currentRun.runDefinitions.Predef_??? className == Predef_???.owner.javaClassName && methName == Predef_???.name.encoded } + def isWhitebox = !isBlackbox } /** Macro def -> macro impl bindings are serialized into a `macroImpl` annotation * with synthetic content that carries the payload described in `MacroImplBinding`. * * For example, for a pair of macro definition and macro implementation: - * def impl(c: scala.reflect.macros.Context): c.Expr[Unit] = ??? + * def impl(c: scala.reflect.macros.BlackboxContext): c.Expr[Unit] = ??? * def foo: Unit = macro impl * * We will have the following annotation added on the macro definition `foo`: @@ -122,13 +124,14 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { * @scala.reflect.macros.internal.macroImpl( * `macro`( * "isBundle" = false, + * "isBlackbox" = true, * "signature" = List(Other), * "methodName" = "impl", * "versionFormat" = <current version format>, * "className" = "Macros$")) */ object MacroImplBinding { - val versionFormat = 5.0 + val versionFormat = 6.0 def pickleAtom(obj: Any): Tree = obj match { @@ -151,7 +154,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { def pickle(macroImplRef: Tree): Tree = { val runDefinitions = currentRun.runDefinitions import runDefinitions._ - val MacroImplReference(isBundle, owner, macroImpl, targs) = macroImplRef + val MacroImplReference(isBundle, isBlackbox, owner, macroImpl, targs) = macroImplRef // todo. refactor when fixing SI-5498 def className: String = { @@ -182,6 +185,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { val payload = List[(String, Any)]( "versionFormat" -> versionFormat, "isBundle" -> isBundle, + "isBlackbox" -> isBlackbox, "className" -> className, "methodName" -> macroImpl.name.toString, "signature" -> signature @@ -237,10 +241,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { if (versionFormat != pickleVersionFormat) fail(s"expected version format $versionFormat, actual $pickleVersionFormat") val isBundle = unpickle("isBundle", classOf[Boolean]) + val isBlackbox = unpickle("isBlackbox", classOf[Boolean]) val className = unpickle("className", classOf[String]) val methodName = unpickle("methodName", classOf[String]) val signature = unpickle("signature", classOf[List[List[Fingerprint]]]) - MacroImplBinding(isBundle, className, methodName, signature, targs) + MacroImplBinding(isBundle, isBlackbox, className, methodName, signature, targs) } } @@ -249,14 +254,17 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(pickle), Nil) } - def loadMacroImplBinding(macroDef: Symbol): MacroImplBinding = { - val Some(AnnotationInfo(_, List(pickle), _)) = macroDef.getAnnotation(MacroImplAnnotation) - MacroImplBinding.unpickle(pickle) - } + def loadMacroImplBinding(macroDef: Symbol): Option[MacroImplBinding] = + macroDef.getAnnotation(MacroImplAnnotation) collect { + case AnnotationInfo(_, List(pickle), _) => MacroImplBinding.unpickle(pickle) + } + + def isBlackbox(expandee: Tree): Boolean = isBlackbox(dissectApplied(expandee).core.symbol) + def isBlackbox(macroDef: Symbol): Boolean = loadMacroImplBinding(macroDef).map(_.isBlackbox).getOrElse(false) def computeMacroDefTypeFromMacroImplRef(macroDdef: DefDef, macroImplRef: Tree): Type = { macroImplRef match { - case MacroImplReference(_, _, macroImpl, targs) => + case MacroImplReference(_, _, _, macroImpl, targs) => // Step I. Transform c.Expr[T] to T and everything else to Any var runtimeType = decreaseMetalevel(macroImpl.info.finalResultType) @@ -450,7 +458,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { (trees :+ tags).flatten } - val binding = loadMacroImplBinding(macroDef) + val binding = loadMacroImplBinding(macroDef).get if (binding.is_???) Nil else calculateMacroArgs(binding) } @@ -459,7 +467,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } /** Keeps track of macros in-flight. - * See more informations in comments to `openMacros` in `scala.reflect.macros.Context`. + * See more informations in comments to `openMacros` in `scala.reflect.macros.WhiteboxContext`. */ private var _openMacros = List[MacroContext]() def openMacros = _openMacros @@ -596,21 +604,27 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { */ def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = { object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, pt) { - override def onSuccess(expanded: Tree) = { + override def onSuccess(expanded0: Tree) = { + def approximate(tp: Type) = { + // approximation is necessary for whitebox macros to guide type inference + // read more in the comments for onDelayed below + if (isBlackbox(expandee)) tp + else { + val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol } + deriveTypeWithWildcards(undetparams)(tp) + } + } + 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) - // approximation is necessary for whitebox macros to guide type inference - // read more in the comments for onDelayed below - def approximate(tp: Type) = { - val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol } - deriveTypeWithWildcards(undetparams)(tp) - } - val macroPtApprox = approximate(if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe) + // `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 macroPtApprox = $macroPtApprox): $expanded") - val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, macroPtApprox)) + 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 @@ -664,7 +678,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want. // - // =========== THE SOLUTION =========== + // =========== THE SOLUTION (ENABLED ONLY FOR WHITEBOX MACROS) =========== // // To give materializers a chance to say their word before vanilla inference kicks in, // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo) @@ -672,9 +686,12 @@ 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) { - forced += delayed - typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false) - macroExpandApply(typer, delayed, mode, pt) + if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) + else { + forced += delayed + typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false) + macroExpandApply(typer, delayed, mode, pt) + } } else delayed } } diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index f69b8a9697..ba135d7d25 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -409,9 +409,10 @@ trait PatternTypers { if (fun1.tpe.isErroneous) duplErrTree - else if (unapplyMethod.isMacro && !fun1.isInstanceOf[Apply]) - duplErrorTree(WrongShapeExtractorExpansion(tree)) - else + else if (unapplyMethod.isMacro && !fun1.isInstanceOf[Apply]) { + if (isBlackbox(unapplyMethod)) duplErrorTree(BlackboxExtractorExpansion(tree)) + else duplErrorTree(WrongShapeExtractorExpansion(tree)) + } else makeTypedUnApply() } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a9bb81c691..fa704adde2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1111,7 +1111,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } if (tree.isType) adaptType() - else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree)) + else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree) && !isMacroExpansionSuppressed(tree)) macroExpandApply(this, tree, mode, pt) else if (mode.typingConstructorPattern) typedConstructorPattern(tree, pt) diff --git a/src/compiler/scala/tools/reflect/StdTags.scala b/src/compiler/scala/tools/reflect/StdTags.scala index 6c1821f8aa..5c53c81e8b 100644 --- a/src/compiler/scala/tools/reflect/StdTags.scala +++ b/src/compiler/scala/tools/reflect/StdTags.scala @@ -49,7 +49,7 @@ object StdRuntimeTags extends StdTags { } abstract class StdContextTags extends StdTags { - val tc: scala.reflect.macros.Context + val tc: scala.reflect.macros.contexts.Context val u: tc.universe.type = tc.universe val m = tc.mirror } |