diff options
author | Paul Phillips <paulp@improving.org> | 2013-05-16 22:30:24 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-05-16 22:34:16 -0700 |
commit | 0c3ca2248d55eeeee2db6693e5fc46b9ba049eda (patch) | |
tree | 71686d0b99fc1d4a245ddb24efe50398b0c00c78 /src/compiler | |
parent | c663ecf8677eda3fe8c91170b614eb7166b18711 (diff) | |
parent | 13b4628aabb8e7a293a382dcab6db5848f672f94 (diff) | |
download | scala-0c3ca2248d55eeeee2db6693e5fc46b9ba049eda.tar.gz scala-0c3ca2248d55eeeee2db6693e5fc46b9ba049eda.tar.bz2 scala-0c3ca2248d55eeeee2db6693e5fc46b9ba049eda.zip |
Merge branch 'master' into HEAD
* master:
SI-7469 Remove @deprecated scala.util.logging
SI-3943 Test case for already-fixed Java interop bug
Fix formatting for couple of docs in the compiler
SI-7476 Add documentation to GenericTraversableTemplate
SI-7469 Remove @deprecated scala.util.parsing.ast
SI-7469 Remove @deprecated MurmurHash elements
SI-7469 Remove deprecated elements in s.u.parsing.combinator
SI-7469 Make @deprecated elems in scala.concurrent private[scala]
removes duplication in inferImplicitValue
SI-7047 fixes silent for c.inferImplicitXXX
SI-7167 implicit macros decide what is divergence
macroExpandAll is now triggered in all invocations of typed
SI-5923 instantiates targs in deferred macro applications
SI-6406 Restore deprecated API
SI-6039 Harden against irrelevant filesystem details
Limit unnecessary calls to Type#toString.
fix typo in comment
SI-7432 add testcases
SI-7432 Range.min should throw NoSuchElementException on empty range
AbstractFile.getDirectory does not return null when outDir is "."
Conflicts:
src/compiler/scala/tools/nsc/typechecker/Typers.scala
Diffstat (limited to 'src/compiler')
12 files changed, 109 insertions, 68 deletions
diff --git a/src/compiler/scala/reflect/macros/runtime/Aliases.scala b/src/compiler/scala/reflect/macros/runtime/Aliases.scala index ff870e728e..1c6703aeee 100644 --- a/src/compiler/scala/reflect/macros/runtime/Aliases.scala +++ b/src/compiler/scala/reflect/macros/runtime/Aliases.scala @@ -28,4 +28,8 @@ trait Aliases { override def typeTag[T](implicit ttag: TypeTag[T]) = ttag override def weakTypeOf[T](implicit attag: WeakTypeTag[T]): Type = attag.tpe override def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe + + implicit class RichOpenImplicit(oi: universe.analyzer.OpenImplicit) { + def toImplicitCandidate = ImplicitCandidate(oi.info.pre, oi.info.sym, oi.pt, oi.tree) + } }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/runtime/Enclosures.scala b/src/compiler/scala/reflect/macros/runtime/Enclosures.scala index 8fe0b09700..f3f92550de 100644 --- a/src/compiler/scala/reflect/macros/runtime/Enclosures.scala +++ b/src/compiler/scala/reflect/macros/runtime/Enclosures.scala @@ -21,16 +21,16 @@ trait Enclosures { // vals are eager to simplify debugging // after all we wouldn't save that much time by making them lazy - val macroApplication: Tree = expandee - def enclosingPackage: PackageDef = strictEnclosure[PackageDef] - val enclosingClass: Tree = lenientEnclosure[ImplDef] - def enclosingImpl: ImplDef = strictEnclosure[ImplDef] - def enclosingTemplate: Template = strictEnclosure[Template] - val enclosingImplicits: List[(Type, Tree)] = site.openImplicits - val enclosingMacros: List[Context] = this :: universe.analyzer.openMacros // include self - val enclosingMethod: Tree = lenientEnclosure[DefDef] - def enclosingDef: DefDef = strictEnclosure[DefDef] - val enclosingPosition: Position = if (enclPoses.isEmpty) NoPosition else enclPoses.head.pos - val enclosingUnit: CompilationUnit = universe.currentRun.currentUnit - val enclosingRun: Run = universe.currentRun + val macroApplication: Tree = expandee + def enclosingPackage: PackageDef = strictEnclosure[PackageDef] + val enclosingClass: Tree = lenientEnclosure[ImplDef] + def enclosingImpl: ImplDef = strictEnclosure[ImplDef] + def enclosingTemplate: Template = strictEnclosure[Template] + val enclosingImplicits: List[ImplicitCandidate] = site.openImplicits.map(_.toImplicitCandidate) + val enclosingMacros: List[Context] = this :: universe.analyzer.openMacros // include self + val enclosingMethod: Tree = lenientEnclosure[DefDef] + def enclosingDef: DefDef = strictEnclosure[DefDef] + val enclosingPosition: Position = if (enclPoses.isEmpty) NoPosition else enclPoses.head.pos + val enclosingUnit: CompilationUnit = universe.currentRun.currentUnit + val enclosingRun: Run = universe.currentRun } diff --git a/src/compiler/scala/reflect/macros/runtime/Typers.scala b/src/compiler/scala/reflect/macros/runtime/Typers.scala index 0ebcf43de0..f60b8dfeb4 100644 --- a/src/compiler/scala/reflect/macros/runtime/Typers.scala +++ b/src/compiler/scala/reflect/macros/runtime/Typers.scala @@ -8,7 +8,7 @@ trait Typers { def openMacros: List[Context] = this :: universe.analyzer.openMacros - def openImplicits: List[(Type, Tree)] = callsiteTyper.context.openImplicits + def openImplicits: List[ImplicitCandidate] = callsiteTyper.context.openImplicits.map(_.toImplicitCandidate) /** * @see [[scala.tools.reflect.ToolBox.typeCheck]] @@ -37,30 +37,13 @@ trait Typers { def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { macroLogVerbose("inferring implicit value of type %s, macros = %s".format(pt, !withMacrosDisabled)) - inferImplicit(universe.EmptyTree, pt, isView = false, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = pos) + universe.analyzer.inferImplicit(universe.EmptyTree, pt, false, callsiteTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw TypecheckException(pos, msg)) } def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { macroLogVerbose("inferring implicit view from %s to %s for %s, macros = %s".format(from, to, tree, !withMacrosDisabled)) val viewTpe = universe.appliedType(universe.definitions.FunctionClass(1).toTypeConstructor, List(from, to)) - inferImplicit(tree, viewTpe, isView = true, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = pos) - } - - private def inferImplicit(tree: Tree, pt: Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: Position): Tree = { - import universe.analyzer.SearchResult - val context = callsiteTyper.context - val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _) - def wrapper (inference: => SearchResult) = wrapper1(inference) - wrapper(universe.analyzer.inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) match { - case failure if failure.tree.isEmpty => - macroLogVerbose("implicit search has failed. to find out the reason, turn on -Xlog-implicits") - context.firstError match { - case Some(err) => throw new TypecheckException(err.errPos, err.errMsg) - case None => universe.EmptyTree - } - case success => - success.tree - } + universe.analyzer.inferImplicit(tree, viewTpe, true, callsiteTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw TypecheckException(pos, msg)) } def resetAllAttrs(tree: Tree): Tree = universe.resetAllAttrs(tree) diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 250feb69bf..98fb6bd0ef 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -234,10 +234,14 @@ abstract class SymbolLoaders { } } if (!root.isEmptyPackageClass) { + // Only enter packages which contain a class or a non-empty package for (pkg <- classpath.packages) { - enterPackage(root, pkg.name, new PackageLoader(pkg)) + if (pkg.isEmptyOfClassfiles) { + log(s"Discarding $root/$pkg as it contains no classfiles.") + } + else + enterPackage(root, pkg.name, new PackageLoader(pkg)) } - openPackageModule(root) } } diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 9e867917f9..6f422fcc90 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -89,12 +89,12 @@ abstract class TailCalls extends Transform { */ class TailCallElimination(unit: CompilationUnit) extends Transformer { private def defaultReason = "it contains a recursive call not in tail position" - private val failPositions = perRunCaches.newMap[TailContext, Position]() - private val failReasons = perRunCaches.newMap[TailContext, String]() + private val failPositions = perRunCaches.newMap[TailContext, Position]() withDefault (_.methodPos) + private val failReasons = perRunCaches.newMap[TailContext, String]() withDefaultValue defaultReason private def tailrecFailure(ctx: TailContext) { - val method = ctx.method - val failReason = failReasons.getOrElse(ctx, defaultReason) - val failPos = failPositions.getOrElse(ctx, ctx.methodPos) + val method = ctx.method + val failReason = failReasons(ctx) + val failPos = failPositions(ctx) unit.error(failPos, s"could not optimize @tailrec annotated $method: $failReason") } @@ -237,7 +237,7 @@ abstract class TailCalls extends Transform { if (!ctx.isEligible) fail("it is neither private nor final so can be overridden") else if (!isRecursiveCall) { - if (receiverIsSuper) failHere("it contains a recursive call targeting supertype " + receiver.tpe) + if (receiverIsSuper) failHere("it contains a recursive call targeting a supertype") else failHere(defaultReason) } else if (!matchesTypeArgs) failHere("it is called recursively with different type arguments") diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index 8be8b72130..63834ae51e 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -129,8 +129,9 @@ trait Interface extends ast.TreeDSL { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Interface with user-defined match monad? - * if there's a `__match` in scope, we use this as the match strategy, assuming it conforms to MatchStrategy as defined below: + * if there's a <code>__match</code> in scope, we use this as the match strategy, assuming it conforms to MatchStrategy as defined below: + {{{ type Matcher[P[_], M[+_], A] = { def flatMap[B](f: P[A] => M[B]): M[B] def orElse[B >: A](alternative: => M[B]): M[B] @@ -144,12 +145,14 @@ trait Interface extends ast.TreeDSL { def one[T](x: P[T]): M[T] def guard[T](cond: P[Boolean], then: => P[T]): M[T] } + }}} * P and M are derived from one's signature (`def one[T](x: P[T]): M[T]`) - * if no `__match` is found, we assume the following implementation (and generate optimized code accordingly) + * if no <code>__match</code> is found, we assume the following implementation (and generate optimized code accordingly) + {{{ object __match extends MatchStrategy[({type Id[x] = x})#Id, Option] { def zero = None def one[T](x: T) = Some(x) @@ -157,6 +160,7 @@ trait Interface extends ast.TreeDSL { def guard[T](cond: Boolean, then: => T): Option[T] = if(cond) Some(then) else None def runOrElse[T, U](x: T)(f: T => Option[U]): U = f(x) getOrElse (throw new MatchError(x)) } + }}} */ trait MatchMonadInterface { diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 21c33aad0d..9794497667 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -213,7 +213,7 @@ trait Contexts { self: Analyzer => def isRootImport: Boolean = false /** Types for which implicit arguments are currently searched */ - var openImplicits: List[(Type,Tree)] = List() + var openImplicits: List[OpenImplicit] = List() /* For a named application block (`Tree`) the corresponding `NamedApplyInfo`. */ var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index f27c15180e..05db86635a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -100,6 +100,21 @@ trait Implicits { result } + /** A friendly wrapper over inferImplicit to be used in macro contexts and toolboxes. + */ + def inferImplicit(tree: Tree, pt: Type, isView: Boolean, context: Context, silent: Boolean, withMacrosDisabled: Boolean, pos: Position, onError: (Position, String) => Unit): Tree = { + val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _) + def wrapper(inference: => SearchResult) = wrapper1(inference) + val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) + if (result.isFailure && !silent) { + val err = context.firstError + val errPos = err.map(_.errPos).getOrElse(pos) + val errMsg = err.map(_.errMsg).getOrElse("implicit search has failed. to find out the reason, turn on -Xlog-implicits") + onError(errPos, errMsg) + } + result.tree + } + /** Find all views from type `tp` (in which `tpars` are free) * * Note that the trees in the search results in the returned list share the same type variables. @@ -224,6 +239,10 @@ trait Implicits { ) } + /** A class which is used to track pending implicits to prevent infinite implicit searches. + */ + case class OpenImplicit(info: ImplicitInfo, pt: Type, tree: Tree) + /** A sentinel indicating no implicit was found */ val NoImplicitInfo = new ImplicitInfo(null, NoType, NoSymbol) { // equals used to be implemented in ImplicitInfo with an `if(this eq NoImplicitInfo)` @@ -407,13 +426,25 @@ trait Implicits { * @pre `info.tpe` does not contain an error */ private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean, isLocal: Boolean): SearchResult = { - (context.openImplicits find { case (tp, tree1) => tree1.symbol == tree.symbol && dominates(pt, tp)}) match { + // SI-7167 let implicit macros decide what amounts for a divergent implicit search + // imagine a macro writer which wants to synthesize a complex implicit Complex[T] by making recursive calls to Complex[U] for its parts + // e.g. we have `class Foo(val bar: Bar)` and `class Bar(val x: Int)` + // then it's quite reasonable for the macro writer to synthesize Complex[Foo] by calling `inferImplicitValue(typeOf[Complex[Bar])` + // however if we didn't insert the `info.sym.isMacro` check here, then under some circumstances + // (e.g. as described here http://groups.google.com/group/scala-internals/browse_thread/thread/545462b377b0ac0a) + // `dominates` might decide that `Bar` dominates `Foo` and therefore a recursive implicit search should be prohibited + // now when we yield control of divergent expansions to the macro writer, what happens next? + // in the worst case, if the macro writer is careless, we'll get a StackOverflowException from repeated macro calls + // otherwise, the macro writer could check `c.openMacros` and `c.openImplicits` and do `c.abort` when expansions are deemed to be divergent + // upon receiving `c.abort` the typechecker will decide that the corresponding implicit search has failed + // which will fail the entire stack of implicit searches, producing a nice error message provided by the programmer + (context.openImplicits find { case OpenImplicit(info, tp, tree1) => !info.sym.isMacro && tree1.symbol == tree.symbol && dominates(pt, tp)}) match { case Some(pending) => //println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG DivergentSearchFailure case None => try { - context.openImplicits = (pt, tree) :: context.openImplicits + context.openImplicits = OpenImplicit(info, pt, tree) :: context.openImplicits // println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG val result = typedImplicit0(info, ptChecked, isLocal) if (result.isDivergent) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 31cc8bd93c..2cd2f9380b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -396,7 +396,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { case Fallback(fallback) => typed1Expr(fallback) case Delayed(delayed) => - delayed + typer.instantiate(delayed, EXPRmode, WildcardType) case Skipped(skipped) => skipped case Failure(failure) => @@ -780,6 +780,29 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { expanded2 } } + override def onDelayed(delayed: Tree) = { + // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee), + // then there are two possible situations we're in: + // 1) We're in POLYmode, when the typer tests the waters wrt type inference + // (e.g. as in typedArgToPoly in doTypedApply). + // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type + // (e.g. if we're an argument to a function call, then this means that no previous argument lists + // can determine our type variables for us). + // + // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that + // there's nothing outrageously wrong with our undetermined type params (from what I understand!). + // + // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer + // the undetermined type params. Therefore we need to do something ourselves or otherwise this + // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum + // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases, + // but sometimes, if the inferencer lacks information, it will be forced to approximate. This prevents + // an important class of macros, fundep materializers, from working, which I perceive is a problem we need to solve. + // For details see SI-7470. + val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode + if (shouldInstantiate) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) + else delayed + } } expander(expandee) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 5cce4865cc..5b1f7160b7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1233,29 +1233,21 @@ trait Typers extends Adaptations with Tags { } if (tree.isType) adaptType() + else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree)) + macroExpandApply(this, tree, mode, pt) else if (mode.typingPatternFun) adaptConstrPattern() else if (shouldInsertApply(tree)) insertApply() else if (hasUndetsInMonoMode) { // (9) assert(!context.inTypeConstructor, context) //@M - if (mode.typingExprNotFun && pt.typeSymbol == UnitClass) - instantiateExpectingUnit(tree, mode) - else - instantiate(tree, mode, pt) + instantiatePossiblyExpectingUnit(tree, mode, pt) } else if (tree.tpe <:< pt) tree else fallbackAfterVanillaAdapt() } - def expandMacroAndVanillaAdapt(): Tree = { - if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree)) { - val tree1 = macroExpandApply(this, tree, mode, pt) - if (tree == tree1) vanillaAdapt(tree) else tree1 - } - else vanillaAdapt(tree) - } // begin adapt tree.tpe match { @@ -1294,7 +1286,7 @@ trait Typers extends Adaptations with Tags { case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode && !treeInfo.isMacroApplicationOrBlock(tree) => instantiateToMethodType(mt) case _ => - expandMacroAndVanillaAdapt() + vanillaAdapt(tree) } } @@ -1315,6 +1307,13 @@ trait Typers extends Adaptations with Tags { } } + def instantiatePossiblyExpectingUnit(tree: Tree, mode: Mode, pt: Type): Tree = { + if (mode.typingExprNotFun && pt.typeSymbol == UnitClass) + instantiateExpectingUnit(tree, mode) + else + instantiate(tree, mode, pt) + } + private def isAdaptableWithView(qual: Tree) = { val qtpe = qual.tpe.widen ( !isPastTyper diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 7f9b81e1ec..536a281e6c 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -211,6 +211,8 @@ abstract class ClassPath[T] { def validPackage(name: String) = (name != "META-INF") && (name != "") && (name.charAt(0) != '.') def validSourceFile(name: String) = endsScala(name) || endsJava(name) + def isEmptyOfClassfiles: Boolean = classes.isEmpty && packages.forall(_.isEmptyOfClassfiles) + /** * Find a ClassRep given a class name of the form "package.subpackage.ClassName". * Does not support nested classes on .NET diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 602982337f..59408096a3 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -180,16 +180,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => transformDuringTyper(tree, withImplicitViewsDisabled = false, withMacrosDisabled = withMacrosDisabled)( (currentTyper, tree) => { trace("inferring implicit %s (macros = %s): ".format(if (isView) "view" else "value", !withMacrosDisabled))(showAttributed(pt, true, true, settings.Yshowsymkinds.value)) - val context = currentTyper.context - val result = analyzer.inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos) - if (result.isFailure) { - // @H: what's the point of tracing an empty tree? - trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits: ")(result.tree) - context.firstError foreach { err => - throw ToolBoxError("reflective implicit search has failed: %s".format(err.errMsg)) - } - } - result.tree + analyzer.inferImplicit(tree, pt, isView, currentTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw ToolBoxError(msg)) }) def compile(expr0: Tree): () => Any = { |