summaryrefslogtreecommitdiff
path: root/src/compiler/scala
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2014-02-17 12:08:28 +0100
committerJason Zaugg <jzaugg@gmail.com>2014-02-17 12:13:20 +0100
commit3ca9038d08dd4a528ae15ee93d44fc7f1392de4e (patch)
treeefc189a76947191215cb0f48ca1b6b7895bc5667 /src/compiler/scala
parent6ef6c96eff2f0d2f505d45a1436d73a960193076 (diff)
downloadscala-3ca9038d08dd4a528ae15ee93d44fc7f1392de4e.tar.gz
scala-3ca9038d08dd4a528ae15ee93d44fc7f1392de4e.tar.bz2
scala-3ca9038d08dd4a528ae15ee93d44fc7f1392de4e.zip
Revert "SI-5920 enables default and named args in macros"
This reverts commit a02e053a5dec134f7c7dc53a2c1091039218237d. That commit lead to an error compiling Specs2: [info] [warn] /localhome/jenkinsdbuild/workspace/Community-2.11.x-retronym/dbuild-0.7.1-M1/target-0.7.1-M1/project-builds/specs2-aaa8091b47a34817ca90134ace8b09a9e0f854e9/core/src/test/scala/org/specs2/text/EditDistanceSpec.scala:6: Unused import [info] [warn] import DiffShortener._ [info] [warn] ^ [info] [error] /localhome/jenkinsdbuild/workspace/Community-2.11.x-retronym/dbuild-0.7.1-M1/target-0.7.1-M1/project-builds/specs2-aaa8091b47a34817ca90134ace8b09a9e0f854e9/core/src/test/scala/org/specs2/text/LinesContentDifferenceSpec.scala:7: exception during macro expansion: [info] [error] java.lang.UnsupportedOperationException: Position.point on NoPosition [info] [error] at scala.reflect.internal.util.Position.fail(Position.scala:53) [info] [error] at scala.reflect.internal.util.UndefinedPosition.point(Position.scala:131) [info] [error] at scala.reflect.internal.util.UndefinedPosition.point(Position.scala:126) [info] [error] at org.specs2.reflect.Macros$.sourceOf(Macros.scala:25) [info] [error] at org.specs2.reflect.Macros$.stringExpr(Macros.scala:19)
Diffstat (limited to 'src/compiler/scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala28
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala93
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala19
4 files changed, 58 insertions, 85 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 40b97394f2..2043eb5d5d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -501,6 +501,10 @@ trait ContextErrors {
}
// doTypeApply
+ //tryNamesDefaults
+ def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) =
+ NormalTypeError(tree, "macro applications do not support named and/or default arguments")
+
def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) =
NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun))
@@ -599,11 +603,12 @@ trait ContextErrors {
//adapt
def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = {
- val errorExplanation = "missing arguments for " + meth.fullLocationString
- val suggestPartialApplication = ";\nfollow this method with `_' if you want to treat it as a partially applied function"
val message =
- if (meth.isMacro || meth.isConstructor) errorExplanation
- else errorExplanation + suggestPartialApplication
+ if (meth.isMacro) MacroTooFewArgumentListsMessage
+ else "missing arguments for " + meth.fullLocationString + (
+ if (meth.isConstructor) ""
+ else ";\nfollow this method with `_' if you want to treat it as a partially applied function"
+ )
issueNormalTypeError(tree, message)
setError(tree)
}
@@ -743,12 +748,15 @@ trait ContextErrors {
throw MacroExpansionException
}
- def MacroFastTrackFailed(expandee: Tree) = {
- // here we speculate that the reason why FastTrackEntry.validate failed is the lack arguments for a given method
- // that's not ideal, but on the other hand this allows us to keep FastTrack simple without hooking errorgen into it
- MissingArgsForMethodTpeError(expandee, expandee.symbol)
- throw MacroExpansionException
- }
+ private def MacroTooFewArgumentListsMessage = "too few argument lists for macro invocation"
+ def MacroTooFewArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooFewArgumentListsMessage)
+
+ private def MacroTooManyArgumentListsMessage = "too many argument lists for macro invocation"
+ def MacroTooManyArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooManyArgumentListsMessage)
+
+ def MacroTooFewArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too few arguments for macro invocation")
+
+ def MacroTooManyArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too many arguments for macro invocation")
def MacroGeneratedAbort(expandee: Tree, ex: AbortMacroException) = {
// errors have been reported by the macro itself, so we do nothing here
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 1f90dd4939..677c94e063 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -353,7 +353,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
new {
val universe: self.global.type = self.global
val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer]
- val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).desugared orElse duplicateAndKeepPositions(expandeeTree)
+ val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).original orElse duplicateAndKeepPositions(expandeeTree)
} with UnaffiliatedMacroContext {
val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing)
override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */)
@@ -371,15 +371,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
def standardMacroArgs(typer: Typer, expandee: Tree): MacroArgs = {
val macroDef = expandee.symbol
val paramss = macroDef.paramss
- val treeInfo.Applied(core, targs, maybeNamedArgss) = expandee
- val argss = map2(maybeNamedArgss, paramss)((args, params) => {
- if (args.exists(_.isInstanceOf[AssignOrNamedArg])) {
- val sorted = ListBuffer.fill(params.length)(EmptyTree: Tree)
- args foreach { case AssignOrNamedArg(Ident(name), arg) => sorted(params.indexWhere(_.name == name)) = arg }
- sorted.toList
- } else if (params.length == args.length) args
- else args ++ List.fill(params.length - args.length)(EmptyTree)
- })
+ val treeInfo.Applied(core, targs, argss) = expandee
val prefix = core match { case Select(qual, _) => qual; case _ => EmptyTree }
val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefix, expandee))
@@ -391,11 +383,16 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
|paramss: $paramss
""".trim)
+ import typer.TyperErrorGen._
+ val isNullaryArgsEmptyParams = argss.isEmpty && paramss == ListOfNil
+ if (paramss.length < argss.length) MacroTooManyArgumentListsError(expandee)
+ if (paramss.length > argss.length && !isNullaryArgsEmptyParams) MacroTooFewArgumentListsError(expandee)
+
val macroImplArgs: List[Any] =
if (fastTrack contains macroDef) {
// Take a dry run of the fast track implementation
if (fastTrack(macroDef) validate expandee) argss.flatten
- else typer.TyperErrorGen.MacroFastTrackFailed(expandee)
+ else MacroTooFewArgumentListsError(expandee)
}
else {
def calculateMacroArgs(binding: MacroImplBinding) = {
@@ -406,6 +403,14 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
// wrap argss in c.Expr if necessary (i.e. if corresponding macro impl param is of type c.Expr[T])
// expand varargs (nb! varargs can apply to any parameter section, not necessarily to the last one)
val trees = map3(argss, paramss, signature)((args, defParams, implParams) => {
+ val isVarargs = isVarArgsList(defParams)
+ if (isVarargs) {
+ if (defParams.length > args.length + 1) MacroTooFewArgumentsError(expandee)
+ } else {
+ if (defParams.length < args.length) MacroTooManyArgumentsError(expandee)
+ if (defParams.length > args.length) MacroTooFewArgumentsError(expandee)
+ }
+
val wrappedArgs = mapWithIndex(args)((arg, j) => {
val fingerprint = implParams(min(j, implParams.length - 1))
fingerprint match {
@@ -416,7 +421,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}
})
- if (isVarArgsList(defParams)) {
+ if (isVarargs) {
val (normal, varargs) = wrappedArgs splitAt (defParams.length - 1)
normal :+ varargs // pack all varargs into a single Seq argument (varargs Scala style)
} else wrappedArgs
@@ -524,11 +529,9 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
* the expandee with an error marker set if there has been an error
*/
abstract class MacroExpander(val typer: Typer, val expandee: Tree) {
- val symbol = expandee match { case Block(_, expr) => expr.symbol; case tree => tree.symbol }
-
def onSuccess(expanded: Tree): Tree
def onFallback(expanded: Tree): Tree
- def onSuppressed(expanded: Tree): Tree = expanded
+ def onSuppressed(expandee: Tree): Tree = expandee
def onDelayed(expanded: Tree): Tree = expanded
def onSkipped(expanded: Tree): Tree = expanded
def onFailure(expanded: Tree): Tree = { typer.infer.setError(expandee); expandee }
@@ -548,15 +551,15 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
if (Statistics.canEnable) Statistics.incCounter(macroExpandCount)
try {
withInfoLevel(nodePrinters.InfoLevel.Quiet) { // verbose printing might cause recursive macro expansions
- if (symbol.isErroneous || (expandee exists (_.isErroneous)) || (desugared exists (_.isErroneous))) {
- val reason = if (symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments"
+ if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) {
+ val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments"
macroLogVerbose(s"cancelled macro expansion because of $reason: $expandee")
onFailure(typer.infer.setError(expandee))
} else try {
val expanded = {
- val runtime = macroRuntime(desugared)
- if (runtime != null) macroExpandWithRuntime(typer, desugared, runtime)
- else macroExpandWithoutRuntime(typer, desugared)
+ val runtime = macroRuntime(expandee)
+ if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
+ else macroExpandWithoutRuntime(typer, expandee)
}
expanded match {
case Success(expanded) =>
@@ -588,7 +591,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
extends MacroExpander(typer, expandee) {
lazy val innerPt = {
val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
- if (isBlackbox(symbol)) tp
+ if (isBlackbox(expandee)) tp
else {
// approximation is necessary for whitebox macros to guide type inference
// read more in the comments for onDelayed below
@@ -596,50 +599,6 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
deriveTypeWithWildcards(undetparams)(tp)
}
}
- override protected def expand(desugared: Tree) = {
- // SI-5940 in order for a macro expansion that involves named or default arguments
- // to see the actual prefix and arguments being passed by the user instead of their desugarings
- // we need to inline synthetics in case when `fun` is actually a macro
- // underlying macro implementation is going to get explicitly passed arguments in correct order
- // and the rest (defaults filled in by the vanilla part of `tryNamesDefaults`) will become empty trees
- // in order for the macro to be able to account for evaluation order, the original is provided in `c.macroApplication`
- // of course, ideally we would like to provide the impl with right-hand sides of those default arguments
- // but currently that is flat out impossible because of the difference in scopes
- // anyway this is already an improvement over the former status quo when named/default invocations were outright prohibited
- def undoNamesDefaults(tree: Tree): Tree = {
- val (qualsym, qual, vdefs0, app @ Applied(_, _, argss)) = tree match {
- case Block((qualdef @ ValDef(_, name, _, qual)) +: vdefs, app) if name.startsWith(nme.QUAL_PREFIX) => (qualdef.symbol, qual, vdefs, app)
- case Block(vdefs, app) => (NoSymbol, EmptyTree, vdefs, app)
- case tree => (NoSymbol, EmptyTree, Nil, tree)
- }
- val vdefs = vdefs0.map{ case vdef: ValDef => vdef }
- def hasNamesDefaults(args: List[Tree]) = {
- args.exists(arg => isDefaultGetter(arg) || vdefs.exists(_.symbol == arg.symbol))
- }
- def undoNamesDefaults(args: List[Tree], depth: Int) = {
- def extractRhs(vdef: ValDef) = vdef.rhs.changeOwner(vdef.symbol -> typer.context.owner)
- case class Arg(tree: Tree, ipos: Int, inamed: Int) { val param = app.symbol.paramss(depth)(ipos) }
- val indexed = args.map(arg => arg -> vdefs.indexWhere(_.symbol == arg.symbol)).zipWithIndex.flatMap({
- /* default */ case ((arg, _), _) if isDefaultGetter(arg) => None
- /* positional */ case ((arg, -1), ipos) => Some(Arg(arg, ipos, -1))
- /* default+named */ case ((_, inamed), _) if isDefaultGetter(extractRhs(vdefs(inamed))) => None
- /* named */ case ((arg, inamed), ipos) => Some(Arg(extractRhs(vdefs(inamed)), ipos, inamed))
- })
- if (indexed.forall(_.inamed == -1)) indexed.map(_.tree)
- else indexed.sortBy(_.inamed).map(arg => AssignOrNamedArg(Ident(arg.param.name), arg.tree))
- }
- def loop(tree: Tree, depth: Int): Tree = tree match {
- case Apply(fun, args) if hasNamesDefaults(args) => treeCopy.Apply(tree, loop(fun, depth - 1), undoNamesDefaults(args, depth))
- case Apply(fun, args) => treeCopy.Apply(tree, loop(fun, depth - 1), args)
- case TypeApply(core, targs) => treeCopy.TypeApply(tree, core, targs)
- case Select(core, name) if qualsym != NoSymbol && core.symbol == qualsym => treeCopy.Select(tree, qual, name)
- case core => core
- }
- if (app.symbol == null || app.symbol == NoSymbol || app.exists(_.isErroneous)) tree
- else loop(app, depth = argss.length - 1)
- }
- super.expand(undoNamesDefaults(desugared))
- }
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
@@ -657,7 +616,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}
}
- if (isBlackbox(symbol)) {
+ if (isBlackbox(expandee)) {
val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt)))
typecheck("blackbox typecheck", expanded1, outerPt)
} else {
@@ -719,7 +678,7 @@ 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(symbol)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
+ if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
else {
forced += delayed
typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
index 1a6d2f0011..57f27a05fd 100644
--- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
@@ -85,7 +85,6 @@ trait StdAttachments {
tree match {
// see the comment to `isMacroExpansionSuppressed` to learn why we need
// a special traversal strategy here
- case Block(_, expr) => unsuppressMacroExpansion(expr)
case Apply(fn, _) => unsuppressMacroExpansion(fn)
case TypeApply(fn, _) => unsuppressMacroExpansion(fn)
case _ => // do nothing
@@ -102,8 +101,6 @@ trait StdAttachments {
// we have to account for the fact that during typechecking an expandee might become wrapped,
// i.e. surrounded by an inferred implicit argument application or by an inferred type argument application.
// in that case the expandee itself will no longer be suppressed and we need to look at the core
- // upd. we also need to allow for blocks, because that's what names and defaults are often desugared to
- case Block(_, expr) => isMacroExpansionSuppressed(expr)
case Apply(fn, _) => isMacroExpansionSuppressed(fn)
case TypeApply(fn, _) => isMacroExpansionSuppressed(fn)
case _ => false
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index aadc8f5e3b..f4d2a2cea0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1161,7 +1161,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case mt: MethodType if mode.typingExprNotFunNotLhs && mt.isImplicit => // (4.1)
adaptToImplicitMethod(mt)
- case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode =>
+ case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode && !treeInfo.isMacroApplicationOrBlock(tree) =>
instantiateToMethodType(mt)
case _ =>
vanillaAdapt(tree)
@@ -3272,6 +3272,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
*/
def tryNamesDefaults: Tree = {
val lencmp = compareLengths(args, formals)
+
+ def checkNotMacro() = {
+ if (treeInfo.isMacroApplication(fun))
+ tryTupleApply orElse duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun))
+ }
+
if (mt.isErroneous) duplErrTree
else if (mode.inPatternMode) {
// #2064
@@ -3290,15 +3296,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
else if (allArgsArePositional(argPos) && !isNamedApplyBlock(fun)) {
// if there's no re-ordering, and fun is not transformed, no need to transform
// more than an optimization, e.g. important in "synchronized { x = update-x }"
+ checkNotMacro()
doTypedApply(tree, fun, namelessArgs, mode, pt)
} else {
- unsuppressMacroExpansion(transformNamedApplication(Typer.this, mode, pt)(
- treeCopy.Apply(tree, suppressMacroExpansion(fun), namelessArgs), argPos))
+ checkNotMacro()
+ transformNamedApplication(Typer.this, mode, pt)(
+ treeCopy.Apply(tree, fun, namelessArgs), argPos)
}
} else {
// defaults are needed. they are added to the argument list in named style as
// calls to the default getters. Example:
// foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a))
+ checkNotMacro()
// SI-8111 transformNamedApplication eagerly shuffles around the application to preserve
// evaluation order. During this process, it calls `changeOwner` on symbols that
@@ -3321,7 +3330,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
symsOwnedByContextOwner foreach (_.owner = context.owner)
}
- val fun1 = transformNamedApplication(Typer.this, mode, pt)(suppressMacroExpansion(fun), x => x)
+ val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x)
if (fun1.isErroneous) duplErrTree
else {
assert(isNamedApplyBlock(fun1), fun1)
@@ -3347,7 +3356,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]()
val note = "Error occurred in an application involving default arguments."
if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic
- unsuppressMacroExpansion(doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt))
+ doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)
} else {
rollbackNamesDefaultsOwnerChanges()
tryTupleApply orElse duplErrorTree(NotEnoughArgsError(tree, fun, missing))