diff options
29 files changed, 297 insertions, 158 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 2b6607e23e..d89e852124 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -103,7 +103,7 @@ trait Definitions extends reflect.api.StandardDefinitions { getMemberMethod(valueClassCompanion(className.toTermName).moduleClass, methodName) private def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f) - private def symbolsMap[T](syms: List[Symbol], f: Name => T): Map[Symbol, T] = syms zip (syms map (x => f(x.name))) toMap + private def symbolsMap[T](syms: List[Symbol], f: Name => T): Map[Symbol, T] = mapFrom(syms)(x => f(x.name)) private def symbolsMapFilt[T](syms: List[Symbol], p: Name => Boolean, f: Name => T) = symbolsMap(syms filter (x => p(x.name)), f) private def boxedName(name: Name) = sn.Boxed(name.toTypeName) diff --git a/src/compiler/scala/reflect/internal/StdAttachments.scala b/src/compiler/scala/reflect/internal/StdAttachments.scala index 488195b7dd..ae2ad87deb 100644 --- a/src/compiler/scala/reflect/internal/StdAttachments.scala +++ b/src/compiler/scala/reflect/internal/StdAttachments.scala @@ -7,6 +7,4 @@ trait StdAttachments { self: SymbolTable => case class ReifyAttachment(original: Symbol) - - case class MacroAttachment(delayed: Boolean, context: Option[MacroContext]) }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index 7d9dd282cf..9158c2a4d4 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -76,6 +76,19 @@ abstract class SymbolTable extends api.Universe result } + // For too long have we suffered in order to sort NAMES. + // I'm pretty sure there's a reasonable default for that. + // Notice challenge created by Ordering's invariance. + implicit def lowPriorityNameOrdering[T <: Names#Name]: Ordering[T] = + SimpleNameOrdering.asInstanceOf[Ordering[T]] + + private object SimpleNameOrdering extends Ordering[Names#Name] { + def compare(n1: Names#Name, n2: Names#Name) = ( + if (n1 eq n2) 0 + else n1.toString compareTo n2.toString + ) + } + /** Dump each symbol to stdout after shutdown. */ final val traceSymbolActivity = sys.props contains "scalac.debug.syms" diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 815a5c0710..2fe7dfda17 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -1509,7 +1509,8 @@ trait Types extends api.Types { self: SymbolTable => case tv: TypeVar => tvs += tv case _ => } - val varToParamMap: Map[Type, Symbol] = tvs map (tv => tv -> tv.origin.typeSymbol.cloneSymbol) toMap + val varToParamMap: Map[Type, Symbol] = + mapFrom[TypeVar, Type, Symbol](tvs.toList)(_.origin.typeSymbol.cloneSymbol) val paramToVarMap = varToParamMap map (_.swap) val varToParam = new TypeMap { def apply(tp: Type) = varToParamMap get tp match { @@ -3473,7 +3474,7 @@ trait Types extends api.Types { self: SymbolTable => case TypeRef(pre, sym, _) if sameLength(sym.typeParams, args) => val eparams = typeParamsToExistentials(sym) val bounds = args map (TypeBounds upper _) - (eparams, bounds).zipped foreach (_ setInfo _) + foreach2(eparams, bounds)(_ setInfo _) newExistentialType(eparams, typeRef(pre, sym, eparams map (_.tpe))) case _ => @@ -3655,7 +3656,7 @@ trait Types extends api.Types { self: SymbolTable => else owner.newValueParameter(name.toTermName) paramStack = newParams :: paramStack try { - (newParams, ptypes).zipped foreach ((p, t) => p setInfo this(t)) + foreach2(newParams, ptypes)((p, t) => p setInfo this(t)) val restpe1 = this(restpe) if (isType) PolyType(newParams, restpe1) else MethodType(newParams, restpe1) diff --git a/src/compiler/scala/reflect/internal/util/Collections.scala b/src/compiler/scala/reflect/internal/util/Collections.scala index 9e4ae1ca00..2e119f8ccc 100644 --- a/src/compiler/scala/reflect/internal/util/Collections.scala +++ b/src/compiler/scala/reflect/internal/util/Collections.scala @@ -70,6 +70,27 @@ trait Collections { lb.toList } + final def flatCollect[A, B](elems: List[A])(pf: PartialFunction[A, Traversable[B]]): List[B] = { + val lb = new ListBuffer[B] + for (x <- elems ; if pf isDefinedAt x) + lb ++= pf(x) + + lb.toList + } + + final def distinctBy[A, B](xs: List[A])(f: A => B): List[A] = { + val buf = new ListBuffer[A] + val seen = mutable.Set[B]() + xs foreach { x => + val y = f(x) + if (!seen(y)) { + buf += x + seen += y + } + } + buf.toList + } + @tailrec final def flattensToEmpty(xss: Seq[Seq[_]]): Boolean = { xss.isEmpty || xss.head.isEmpty && flattensToEmpty(xss.tail) } @@ -89,6 +110,10 @@ trait Collections { xs find p getOrElse orElse } + final def mapFrom[A, A1 >: A, B](xs: List[A])(f: A => B): Map[A1, B] = { + Map[A1, B](xs map (x => (x, f(x))): _*) + } + final def mapWithIndex[A, B](xs: List[A])(f: (A, Int) => B): List[B] = { val lb = new ListBuffer[B] var index = 0 diff --git a/src/compiler/scala/reflect/reify/codegen/Types.scala b/src/compiler/scala/reflect/reify/codegen/Types.scala index 841ec61e60..8fa24c5b00 100644 --- a/src/compiler/scala/reflect/reify/codegen/Types.scala +++ b/src/compiler/scala/reflect/reify/codegen/Types.scala @@ -84,7 +84,7 @@ trait Types { def spliceType(tpe: Type): Tree = { // [Eugene] it seems that depending on the context the very same symbol can be either a spliceable tparam or a quantified existential. very weird! - val quantified = currents collect { case ExistentialType(quantified, _) => quantified } flatMap identity + val quantified = currentQuantified if (tpe.isSpliceable && !(quantified contains tpe.typeSymbol)) { if (reifyDebug) println("splicing " + tpe) diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index a1ff486ed7..e03ff5832c 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -19,37 +19,47 @@ trait Reify extends Symbols import definitions._ import treeInfo._ + // `reify` looked so nice, I wanted to push the last bit of orthogonal + // logic out of it so you can see the improvement. There is no cost to + // wrapper methods of this form because the inliner will eliminate them, + // but they are very good at separating concerns like pushing/popping + // a stack, and they are great for composition and reuse. + // + // Also, please avoid public vars whenever possible. + private object reifyStack { + var currents: List[Any] = reifee :: Nil + + @inline final def push[T](reifee: Any)(body: => T): T = { + currents ::= reifee + try body + finally currents = currents.tail + } + } + def currentQuantified = flatCollect(reifyStack.currents)({ case ExistentialType(quantified, _) => quantified }) + def current = reifyStack.currents.head + /** * Reifies any supported value. * For internal use only, use ``reified'' instead. */ - var currents: List[Any] = reifee :: Nil - def current = currents.head - def reify(reifee: Any): Tree = { - currents = reifee :: currents - try { - reifee match { - // before adding some case here, in global scope, please, consider - // whether it can be localized like reifyAnnotationInfo or reifyScope - // this will help reification stay as sane as possible - case sym: Symbol => reifySymRef(sym) - case tpe: Type => reifyType(tpe) - case name: Name => reifyName(name) - case tree: Tree => reifyTree(tree) - // disabled because this is a very special case that I plan to remove later - // why do I dislike annotations? see comments to `reifyAnnotationInfo` + def reify(reifee: Any): Tree = reifyStack.push(reifee)(reifee match { + // before adding some case here, in global scope, please, consider + // whether it can be localized like reifyAnnotationInfo or reifyScope + // this will help reification stay as sane as possible + case sym: Symbol => reifySymRef(sym) + case tpe: Type => reifyType(tpe) + case name: Name => reifyName(name) + case tree: Tree => reifyTree(tree) + // disabled because this is a very special case that I plan to remove later + // why do I dislike annotations? see comments to `reifyAnnotationInfo` // case ann: AnnotationInfo => reifyAnnotationInfo(ann) - case pos: Position => reifyPosition(pos) - case mods: mirror.Modifiers => reifyModifiers(mods) - case xs: List[_] => reifyList(xs) - case s: String => Literal(Constant(s)) - case v if isAnyVal(v) => Literal(Constant(v)) - case null => Literal(Constant(null)) - case _ => - throw new Error("reifee %s of type %s is not supported".format(reifee, reifee.getClass)) - } - } finally { - currents = currents.tail - } - } + case pos: Position => reifyPosition(pos) + case mods: mirror.Modifiers => reifyModifiers(mods) + case xs: List[_] => reifyList(xs) + case s: String => Literal(Constant(s)) + case v if isAnyVal(v) => Literal(Constant(v)) + case null => Literal(Constant(null)) + case _ => + throw new Error("reifee %s of type %s is not supported".format(reifee, reifee.getClass)) + }) }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index ff58de5f12..5e5b09405c 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -2225,7 +2225,7 @@ abstract class GenICode extends SubComponent { * jumps to the given basic block. */ def patch(code: Code) { - val map = toPatch map (i => (i -> patch(i))) toMap; + val map = mapFrom(toPatch)(patch) code.blocks foreach (_ subst map) } diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala index 99541ff4b4..9646ee1cf0 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala @@ -90,8 +90,9 @@ private[tests] trait CoreTestDefs compiler.askLinkPos(tree.symbol, source, r) r.get match { case Left(pos) => + val resolvedPos = if (tree.symbol.pos.isDefined) tree.symbol.pos else pos withResponseDelimiter { - reporter.println("[response] found askHyperlinkPos for `" + tree.symbol.name + "` at " + format(pos) + " " + tree.symbol.sourceFile.name) + reporter.println("[response] found askHyperlinkPos for `" + tree.symbol.name + "` at " + format(resolvedPos) + " " + tree.symbol.sourceFile.name) } case Right(ex) => ex.printStackTrace() diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 3d77344091..a9c2ce0d09 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -369,7 +369,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def flatName(id: String) = optFlatName(id) getOrElse id def optFlatName(id: String) = requestForIdent(id) map (_ fullFlatName id) - def allDefinedNames = definedNameMap.keys.toList sortBy (_.toString) + def allDefinedNames = definedNameMap.keys.toList.sorted def pathToType(id: String): String = pathToName(newTypeName(id)) def pathToTerm(id: String): String = pathToName(newTermName(id)) def pathToName(name: Name): String = { @@ -1007,9 +1007,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(global.encode(name.toString))) def simpleNameOfType(name: TypeName) = (compilerTypeOf get name) map (_.typeSymbol.simpleName) - private def typeMap[T](f: Type => T): Map[Name, T] = { - termNames ++ typeNames map (x => x -> f(cleanMemberDecl(resultSymbol, x))) toMap - } + private def typeMap[T](f: Type => T) = + mapFrom[Name, Name, T](termNames ++ typeNames)(x => f(cleanMemberDecl(resultSymbol, x))) /** Types of variables defined by this request. */ lazy val compilerTypeOf = typeMap[Type](x => x) withDefaultValue NoType @@ -1024,8 +1023,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends typeNames.map(x => x -> compilerTypeOf(x).typeSymbol) ).toMap[Name, Symbol] withDefaultValue NoSymbol - lazy val typesOfDefinedTerms: Map[Name, Type] = - termNames map (x => x -> applyToResultMember(x, _.tpe)) toMap + lazy val typesOfDefinedTerms = mapFrom[Name, Name, Type](termNames)(x => applyToResultMember(x, _.tpe)) /** load and run the code using reflection */ def loadAndRun: (String, Boolean) = { diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index 01ace0e984..659caad1e1 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -231,7 +231,7 @@ class Power[ReplValsImpl <: ReplVals : TypeTag](val intp: IMain, replVals: ReplV def shortClass = erasure.getName split "[$.]" last def baseClasses = tpe.baseClasses - def baseClassDecls = baseClasses map (x => (x, x.info.decls.toList.sortBy(_.name.toString))) toMap + def baseClassDecls = mapFrom(baseClasses)(_.info.decls.toList.sortBy(_.name)) def ancestors = baseClasses drop 1 def ancestorDeclares(name: String) = ancestors filter (_.info member newTermName(name) ne NoSymbol) def baseTypes = tpe.baseTypeSeq.toList @@ -362,7 +362,6 @@ class Power[ReplValsImpl <: ReplVals : TypeTag](val intp: IMain, replVals: ReplV else if (s1 isLess s2) -1 else 1 } - implicit lazy val powerNameOrdering: Ordering[Name] = Ordering[String] on (_.toString) implicit lazy val powerSymbolOrdering: Ordering[Symbol] = Ordering[Name] on (_.name) implicit lazy val powerTypeOrdering: Ordering[Type] = Ordering[Symbol] on (_.typeSymbol) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 52648380ec..192cc94b90 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -62,9 +62,16 @@ abstract class Pickler extends SubComponent { // when we pickle it: so let's report an error instead. We know next // to nothing about what happened, but our supposition is a lot better // than "bad type: <error>" in terms of explanatory power. - for (t <- unit.body ; if t.isErroneous) { - unit.error(t.pos, "erroneous or inaccessible type") - return + for (t <- unit.body) { + if (t.isErroneous) { + unit.error(t.pos, "erroneous or inaccessible type") + return + } + + if (!t.isDef && t.hasSymbol && t.symbol.isTermMacro) { + unit.error(t.pos, "macro has not been expanded") + return + } } pickle(unit.body) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index ff671088ac..1fb7fac184 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -820,7 +820,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // concise printing of type env private def pp(env: TypeEnv): String = { - env.toList.sortBy(_._1.name.toString) map { + env.toList.sortBy(_._1.name) map { case (k, v) => val vsym = v.typeSymbol if (k == vsym) "" + k.name diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 18c7635b1e..a77df71312 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -24,6 +24,7 @@ trait Analyzer extends AnyRef with NamesDefaults with TypeDiagnostics with ContextErrors + with StdAttachments { val global : Global import global._ diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 1612253dd6..0d7ef71193 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -831,7 +831,7 @@ trait Implicits { /** Returns all eligible ImplicitInfos and their SearchResults in a map. */ - def findAll() = (eligible map (info => (info, typedImplicit(info, false)))).toMap + def findAll() = mapFrom(eligible)(typedImplicit(_, false)) /** Returns the SearchResult of the best match. */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index ef9bae700e..6d4c543a5a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -294,8 +294,6 @@ trait Macros { self: Analyzer => } finally { openMacros = openMacros.tail } - case Delay(result) => - result case Fallback(fallback) => typer.typed1(fallback, EXPRmode, WildcardType) case Other(result) => @@ -828,7 +826,7 @@ trait Macros { self: Analyzer => case _ => } collectMacroArgs(expandee) - val context = expandee.attachmentOpt[MacroAttachment].flatMap(_.context).getOrElse(macroContext(typer, prefixTree, expandee)) + val context = expandee.attachmentOpt[MacroAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee)) var argss: List[List[Any]] = List(context) :: exprArgs.toList macroTrace("argss: ")(argss) @@ -1007,18 +1005,6 @@ trait Macros { self: Analyzer => } finally { openMacros = openMacros.tail } - case Delay(expandee) => - // need to save the context to preserve enclosures - macroArgs(typer, expandee) match { - case Some((context: MacroContext) :: _) => - // adapting here would be premature, we must wait until undetparams are inferred - expandee withAttachment MacroAttachment(delayed = true, context = Some(context)) - case _ => - // !!! The correct place to issue an error needs to be clarified. - // I have the else condition here only as a fallback. - if (expandee.isErroneous) expandee - else fail("macros cannot be partially applied", expandee) - } case Fallback(fallback) => typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt)) case Other(result) => @@ -1032,8 +1018,8 @@ trait Macros { self: Analyzer => private sealed abstract class MacroExpansionResult extends Product with Serializable private case class Success(expanded: Tree) extends MacroExpansionResult private case class Fallback(fallback: Tree) extends MacroExpansionResult - private case class Delay(expandee: Tree) extends MacroExpansionResult private case class Other(result: Tree) extends MacroExpansionResult + private def Delay(expanded: Tree) = Other(expanded) private def Skip(expanded: Tree) = Other(expanded) private def Cancel(expandee: Tree) = Other(expandee) private def Failure(expandee: Tree) = Other(expandee) @@ -1086,75 +1072,78 @@ trait Macros { self: Analyzer => if (!wasDelayed) { if (macroDebug || macroCopypaste) println("typechecking macro expansion %s at %s".format(expandee, expandee.pos)) - if (nowDelayed) { - if (macroDebug || macroCopypaste) println("macro expansion is delayed: %s".format(expandee)) - delayed += expandee -> (typer.context, undetparams) - Delay(expandee) - } else { - val args = macroArgs(typer, expandee) - args match { - case Some(args) => - // adding stuff to openMacros is easy, but removing it is a nightmare - // it needs to be sprinkled over several different code locations - val (context: MacroContext) :: _ = args - openMacros = context :: openMacros - val expanded: MacroExpansionResult = try { - val prevNumErrors = reporter.ERROR.count - expandee.detach(null) - val expanded = runtime(args) - val currNumErrors = reporter.ERROR.count - if (currNumErrors != prevNumErrors) { - fail(typer, expandee) // errors have been reported by the macro itself - } else { - expanded match { - case expanded: Expr[_] => - if (macroDebug || macroCopypaste) { - if (macroDebug) println("original:") - println(expanded.tree) - println(showRaw(expanded.tree)) - } - - freeTerms(expanded.tree) foreach (fte => typer.context.error(expandee.pos, - ("macro expansion contains free term variable %s %s. "+ - "have you forgot to use eval when splicing this variable into a reifee? " + - "if you have troubles tracking free term variables, consider using -Xlog-free-terms").format(fte.name, fte.origin))) - freeTypes(expanded.tree) foreach (fty => typer.context.error(expandee.pos, - ("macro expansion contains free type variable %s %s. "+ - "have you forgot to use c.TypeTag annotation for this type parameter? " + - "if you have troubles tracking free type variables, consider using -Xlog-free-types").format(fty.name, fty.origin))) - - val currNumErrors = reporter.ERROR.count - if (currNumErrors != prevNumErrors) { - fail(typer, expandee) - } else { - // inherit the position from the first position-ful expandee in macro callstack - // this is essential for sane error messages - var tree = expanded.tree - var position = openMacros.find(c => c.expandee.pos != NoPosition).map(_.expandee.pos).getOrElse(NoPosition) - tree = atPos(position.focus)(tree) - - // now macro expansion gets typechecked against the macro definition return type - // however, this happens in macroExpand, not here in macroExpand1 - Success(tree) - } - case expanded if expanded.isInstanceOf[Expr[_]] => - val msg = "macro must return a compiler-specific expr; returned value is Expr, but it doesn't belong to this compiler's universe" - fail(typer, expandee, msg) - case expanded => - val msg = "macro must return a compiler-specific expr; returned value is of class: %s".format(expanded.getClass) - fail(typer, expandee, msg) + val args = macroArgs(typer, expandee) + args match { + case Some(args) => + val (context: MacroContext) :: _ = args + if (nowDelayed) { + if (macroDebug || macroCopypaste) println("macro expansion is delayed: %s".format(expandee)) + delayed += expandee -> undetparams + // need to save typer context for `macroExpandAll` + // need to save macro context to preserve enclosures + expandee attach MacroAttachment(delayed = true, typerContext = typer.context, macroContext = Some(context)) + Delay(expandee) + } else { + val expanded: MacroExpansionResult = + try { + val prevNumErrors = reporter.ERROR.count + openMacros = context :: openMacros + val expanded = runtime(args) + val currNumErrors = reporter.ERROR.count + if (currNumErrors != prevNumErrors) { + fail(typer, expandee) // errors have been reported by the macro itself + } else { + expanded match { + case expanded: Expr[_] => + if (macroDebug || macroCopypaste) { + if (macroDebug) println("original:") + println(expanded.tree) + println(showRaw(expanded.tree)) + } + + freeTerms(expanded.tree) foreach (fte => typer.context.error(expandee.pos, + ("macro expansion contains free term variable %s %s. "+ + "have you forgot to use eval when splicing this variable into a reifee? " + + "if you have troubles tracking free term variables, consider using -Xlog-free-terms").format(fte.name, fte.origin))) + freeTypes(expanded.tree) foreach (fty => typer.context.error(expandee.pos, + ("macro expansion contains free type variable %s %s. "+ + "have you forgot to use c.TypeTag annotation for this type parameter? " + + "if you have troubles tracking free type variables, consider using -Xlog-free-types").format(fty.name, fty.origin))) + + val currNumErrors = reporter.ERROR.count + if (currNumErrors != prevNumErrors) { + fail(typer, expandee) + } else { + // inherit the position from the first position-ful expandee in macro callstack + // this is essential for sane error messages + var tree = expanded.tree + var position = openMacros.find(c => c.expandee.pos != NoPosition).map(_.expandee.pos).getOrElse(NoPosition) + tree = atPos(position.focus)(tree) + + // now macro expansion gets typechecked against the macro definition return type + // however, this happens in macroExpand, not here in macroExpand1 + Success(tree) + } + case expanded if expanded.isInstanceOf[Expr[_]] => + val msg = "macro must return a compiler-specific expr; returned value is Expr, but it doesn't belong to this compiler's universe" + fail(typer, expandee, msg) + case expanded => + val msg = "macro must return a compiler-specific expr; returned value is of class: %s".format(expanded.getClass) + fail(typer, expandee, msg) + } } + } catch { + case ex: Throwable => + openMacros = openMacros.tail + throw ex + } finally { + expandee.detach(classOf[MacroAttachment]) } - } catch { - case ex: Throwable => - openMacros = openMacros.tail - throw ex - } if (!expanded.isInstanceOf[Success]) openMacros = openMacros.tail expanded - case None => - fail(typer, expandee) // error has been reported by macroArgs - } + } + case None => + fail(typer, expandee) // error has been reported by macroArgs } } else { if (nowDelayed) @@ -1164,8 +1153,6 @@ trait Macros { self: Analyzer => } } catch { case ex => handleMacroExpansionException(typer, expandee, ex) - } finally { - expandee.detach(classOf[MacroAttachment]) } private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroExpansionResult = { @@ -1261,10 +1248,10 @@ trait Macros { self: Analyzer => * 2) undetparams (sym.isTypeParameter && !sym.isSkolem) */ var hasPendingMacroExpansions = false - private val delayed = perRunCaches.newWeakMap[Tree, (Context, collection.mutable.Set[Int])] + private val delayed = perRunCaches.newWeakMap[Tree, collection.mutable.Set[Int]] private def isDelayed(expandee: Tree) = delayed contains expandee private def calculateUndetparams(expandee: Tree): collection.mutable.Set[Int] = - delayed.get(expandee).map(_._2).getOrElse { + delayed.get(expandee).getOrElse { val calculated = collection.mutable.Set[Symbol]() expandee foreach (sub => { def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym @@ -1284,7 +1271,7 @@ trait Macros { self: Analyzer => if (macroDebug) (undetNoMore zip inferreds) foreach {case (sym, tpe) => println("undetParam inferred: %s as %s".format(sym, tpe))} if (!delayed.isEmpty) delayed.toList foreach { - case (expandee, (_, undetparams)) if !undetparams.isEmpty => + case (expandee, undetparams) if !undetparams.isEmpty => undetparams --= undetNoMore map (_.id) if (undetparams.isEmpty) { hasPendingMacroExpansions = true @@ -1304,7 +1291,7 @@ trait Macros { self: Analyzer => override def transform(tree: Tree) = super.transform(tree match { // todo. expansion should work from the inside out case wannabe if (delayed contains wannabe) && calculateUndetparams(wannabe).isEmpty => - val (context, _) = delayed(wannabe) + val context = wannabe.attachment[MacroAttachment].typerContext delayed -= wannabe context.implicitsEnabled = typer.context.implicitsEnabled context.enrichmentEnabled = typer.context.enrichmentEnabled diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 96d92e0609..e5dc8e9ca9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -46,7 +46,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val outer = newTermName("<outer>") val runOrElse = newTermName("runOrElse") val zero = newTermName("zero") - val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching... + val _match = newTermName("__match") // don't call the val __match, since that will trigger virtual pattern matching... def counted(str: String, i: Int) = newTermName(str+i) } @@ -1067,7 +1067,7 @@ class Foo(x: Other) { x._1 } // no error in this order // assert(owner ne null); assert(owner ne NoSymbol) def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = - NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo /*repackExistential*/(tp) + NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo tp // codegen relevant to the structure of the translation (how extractors are combined) trait AbsCodegen { @@ -1141,7 +1141,7 @@ class Foo(x: Other) { x._1 } // no error in this order val matchStrategy: Tree def inMatchMonad(tp: Type): Type = appliedType(oneSig, List(tp)).finalResultType - def pureType(tp: Type): Type = appliedType(oneSig, List(tp)).paramTypes.head + def pureType(tp: Type): Type = appliedType(oneSig, List(tp)).paramTypes.headOption getOrElse NoType // fail gracefully (otherwise we get crashes) protected def matchMonadSym = oneSig.finalResultType.typeSymbol import CODE._ diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala new file mode 100644 index 0000000000..329a247106 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -0,0 +1,10 @@ +package scala.tools.nsc +package typechecker + +import scala.reflect.makro.runtime.{Context => MacroContext} + +trait StdAttachments { + self: Analyzer => + + case class MacroAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 4319dd10c7..a6a8d6009f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -57,7 +57,7 @@ abstract class TreeCheckers extends Analyzer { def prev = maps.init.last._2 def latest = maps.last._2 - def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString) + def sortedNewSyms = newSyms.toList.distinct sortBy (_.name) def inPrev(sym: Symbol) = { (maps.size >= 2) && (prev contains sym) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 1414424a0b..934a7567d5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -83,6 +83,9 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser private def isPastTyper = phase.id > currentRun.typerPhase.id + // don't translate matches in presentation compiler: it loses vital symbols that are needed to do hyperlinking + @inline private def doMatchTranslation = !forInteractive && opt.virtPatmat && (phase.id < currentRun.uncurryPhase.id) + abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tagging with TyperContextErrors { import context0.unit import typeDebug.{ ptTree, ptBlock, ptLine } @@ -2436,7 +2439,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser fun.body match { // later phase indicates scaladoc is calling (where shit is messed up, I tell you) // -- so fall back to old patmat, which is more forgiving - case Match(sel, cases) if opt.virtPatmat && (phase.id < currentRun.uncurryPhase.id) => + case Match(sel, cases) if doMatchTranslation => // go to outer context -- must discard the context that was created for the Function since we're discarding the function // thus, its symbol, which serves as the current context.owner, is not the right owner // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) @@ -3289,14 +3292,12 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser case tp => tp } - (hidden map { s => - // Hanging onto lower bound in case anything interesting - // happens with it. - (s, s.existentialBound match { - case TypeBounds(lo, hi) => TypeBounds(lo, hiBound(s)) - case _ => hiBound(s) - }) - }).toMap + // Hanging onto lower bound in case anything interesting + // happens with it. + mapFrom(hidden)(s => s.existentialBound match { + case TypeBounds(lo, hi) => TypeBounds(lo, hiBound(s)) + case _ => hiBound(s) + }) } /** Given a set `rawSyms` of term- and type-symbols, and a type @@ -3830,7 +3831,7 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser } def typedTranslatedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = { - if (opt.virtPatmat && (phase.id < currentRun.uncurryPhase.id)) { + if (doMatchTranslation) { if (selector ne EmptyTree) { val (selector1, selectorTp, casesAdapted, ownType, doTranslation) = typedMatch(selector, cases, mode, pt) typed(translatedMatch(selector1, selectorTp, casesAdapted, ownType, doTranslation), mode, pt) @@ -4734,9 +4735,8 @@ trait Typers extends Modes with Adaptations with Taggings with PatMatVirtualiser catches1 = catches1 map (adaptCase(_, mode, owntype)) } - if((phase.id < currentRun.uncurryPhase.id) && opt.virtPatmat) { + if (doMatchTranslation) catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos) - } treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype diff --git a/test/files/presentation/callcc-interpreter/Runner.scala b/test/files/presentation/callcc-interpreter/Runner.scala index 61b6efd50d..1ef3cf9025 100644 --- a/test/files/presentation/callcc-interpreter/Runner.scala +++ b/test/files/presentation/callcc-interpreter/Runner.scala @@ -1,5 +1,3 @@ import scala.tools.nsc.interactive.tests._ -object Test extends InteractiveTest { - settings.XoldPatmat.value = true // TODO: could this be running into some kind of race condition? sometimes the match has been translated, sometimes it hasn't -}
\ No newline at end of file +object Test extends InteractiveTest
\ No newline at end of file diff --git a/test/files/presentation/patmat.check b/test/files/presentation/patmat.check new file mode 100644 index 0000000000..29fd8b8e68 --- /dev/null +++ b/test/files/presentation/patmat.check @@ -0,0 +1,36 @@ +reload: PatMatTests.scala + +askHyperlinkPos for `CaseOne` at (12,18) PatMatTests.scala +================================================================================ +[response] found askHyperlinkPos for `CaseOne` at (5,12) PatMatTests.scala +================================================================================ + +askHyperlinkPos for `first` at (14,21) PatMatTests.scala +================================================================================ +[response] found askHyperlinkPos for `first` at (12,29) PatMatTests.scala +================================================================================ + +askHyperlinkPos for `tmp` at (15,19) PatMatTests.scala +================================================================================ +[response] found askHyperlinkPos for `tmp` at (13,13) PatMatTests.scala +================================================================================ + +askHyperlinkPos for `CaseTwo` at (17,18) PatMatTests.scala +================================================================================ +[response] found askHyperlinkPos for `CaseTwo` at (6,12) PatMatTests.scala +================================================================================ + +askHyperlinkPos for `mystring` at (18,24) PatMatTests.scala +================================================================================ +[response] found askHyperlinkPos for `mystring` at (17,25) PatMatTests.scala +================================================================================ + +askHyperlinkPos for `x` at (25,13) PatMatTests.scala +================================================================================ +[response] found askHyperlinkPos for `x` at (23,10) PatMatTests.scala +================================================================================ + +askHyperlinkPos for `y` at (25,21) PatMatTests.scala +================================================================================ +[response] found askHyperlinkPos for `y` at (23,13) PatMatTests.scala +================================================================================ diff --git a/test/files/presentation/patmat.flags b/test/files/presentation/patmat.flags new file mode 100644 index 0000000000..468b48c9e3 --- /dev/null +++ b/test/files/presentation/patmat.flags @@ -0,0 +1,3 @@ +# This test will fail in the new pattern matcher because +# it generates trees whose positions are not transparent +-Xoldpatmat diff --git a/test/files/presentation/patmat/Runner.scala b/test/files/presentation/patmat/Runner.scala new file mode 100644 index 0000000000..3d19f2d948 --- /dev/null +++ b/test/files/presentation/patmat/Runner.scala @@ -0,0 +1,11 @@ +import scala.tools.nsc.interactive.tests.InteractiveTest + +object Test extends InteractiveTest { + override def runTests() { + // make sure typer is done.. the virtual pattern matcher might translate + // some trees and mess up positions. But we'll catch it red handed! + sourceFiles foreach (src => askLoadedTyped(src).get) + super.runTests() + } + +}
\ No newline at end of file diff --git a/test/files/presentation/patmat/src/PatMatTests.scala b/test/files/presentation/patmat/src/PatMatTests.scala new file mode 100644 index 0000000000..bbd0f2e7ed --- /dev/null +++ b/test/files/presentation/patmat/src/PatMatTests.scala @@ -0,0 +1,28 @@ +package patmat + +abstract class BaseType + +case class CaseOne(x: Int, y: List[Int]) extends BaseType +case class CaseTwo(str: String) extends BaseType + +class PatMatTests { + + def foo(x: BaseType) { + x match { + case CaseOne/*#*/(10, first :: second :: Nil) => + val tmp = 23 + println(first/*#*/) + println(tmp/*#*/) + + case CaseTwo/*#*/(mystring) => + println(mystring/*#*/) + } + } + + def multipleAssign() { + val (x, y) = ("abc", "def") + + println(x/*#*/, y/*#*/) + } + +}
\ No newline at end of file diff --git a/test/files/presentation/random.check b/test/files/presentation/random.check index 1b73720312..fce4b69fb3 100644 --- a/test/files/presentation/random.check +++ b/test/files/presentation/random.check @@ -4,8 +4,7 @@ askType at Random.scala(18,14) ================================================================================ [response] askTypeAt at (18,14) val filter: Int => Boolean = try { - case <synthetic> val x1: Int = java.this.lang.Integer.parseInt(args.apply(0)); - x1 match { + java.this.lang.Integer.parseInt(args.apply(0)) match { case 1 => ((x: Int) => x.%(2).!=(0)) case 2 => ((x: Int) => x.%(2).==(0)) case _ => ((x: Int) => x.!=(0)) diff --git a/test/pending/run/t5692.flags b/test/pending/run/t5692.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/pending/run/t5692.flags @@ -0,0 +1 @@ +-language:experimental.macros
\ No newline at end of file diff --git a/test/pending/run/t5692/Impls_Macros_1.scala b/test/pending/run/t5692/Impls_Macros_1.scala new file mode 100644 index 0000000000..f9c1e5f12b --- /dev/null +++ b/test/pending/run/t5692/Impls_Macros_1.scala @@ -0,0 +1,9 @@ +import scala.reflect.makro.Context + +object Impls { + def impl[A](c: reflect.makro.Context) = c.reify(()) +} + +object Macros { + def decl[A] = macro Impls.impl[A] +}
\ No newline at end of file diff --git a/test/pending/run/t5692/Test_2.scala b/test/pending/run/t5692/Test_2.scala new file mode 100644 index 0000000000..29251a5ef5 --- /dev/null +++ b/test/pending/run/t5692/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + val x = Macros.decl + def y() { Macros.decl(); } +}
\ No newline at end of file |