summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-03-06 06:36:04 -0800
committerPaul Phillips <paulp@improving.org>2013-03-06 06:36:04 -0800
commit5967a664ab1129e28687c591bd94c0e482cb305f (patch)
tree5848f219d73307497932206ea63a0ecfa1500724
parentb8c8299be52e1bffc0efec54bd7294510e36022e (diff)
parent67f0694af926a92dba7522488a6d2f3d6eacbeef (diff)
downloadscala-5967a664ab1129e28687c591bd94c0e482cb305f.tar.gz
scala-5967a664ab1129e28687c591bd94c0e482cb305f.tar.bz2
scala-5967a664ab1129e28687c591bd94c0e482cb305f.zip
Merge pull request #2204 from adriaanm/merge-2.10.x
Merge 2.10.x into master
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/DocComments.scala5
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala4
-rw-r--r--src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala3
-rw-r--r--src/compiler/scala/tools/nsc/interactive/CompilerControl.scala31
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Global.scala83
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Picklers.scala4
-rw-r--r--src/compiler/scala/tools/nsc/io/Pickler.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Logic.scala229
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala44
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala52
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala41
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala20
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Solving.scala242
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala101
-rw-r--r--src/library/scala/collection/mutable/ArrayOps.scala23
-rw-r--r--src/reflect/scala/reflect/internal/TreeInfo.scala3
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala19
-rw-r--r--test/files/neg/t7185.check7
-rw-r--r--test/files/neg/t7185.scala3
-rw-r--r--test/files/pos/t7126.scala11
-rw-r--r--test/files/pos/t7183.flags1
-rw-r--r--test/files/pos/t7183.scala13
-rw-r--r--test/files/pos/t7190.scala26
-rw-r--r--test/files/presentation/doc.check50
-rwxr-xr-xtest/files/presentation/doc/doc.scala118
-rwxr-xr-xtest/files/presentation/doc/src/Class.scala1
-rwxr-xr-xtest/files/presentation/doc/src/Test.scala1
-rwxr-xr-xtest/files/presentation/doc/src/p/Base.scala11
-rwxr-xr-xtest/files/presentation/doc/src/p/Derived.scala9
-rw-r--r--test/files/run/t7185.check34
-rw-r--r--test/files/run/t7185.scala12
-rw-r--r--test/files/run/t7214.scala57
-rw-r--r--test/files/run/t7215.scala6
36 files changed, 764 insertions, 511 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala
index eac43c146a..3397797927 100755
--- a/src/compiler/scala/tools/nsc/ast/DocComments.scala
+++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala
@@ -53,6 +53,11 @@ trait DocComments { self: Global =>
else sym.owner.ancestors map (sym overriddenSymbol _) filter (_ != NoSymbol)
}
+ def fillDocComment(sym: Symbol, comment: DocComment) {
+ docComments(sym) = comment
+ comment.defineVariables(sym)
+ }
+
/** The raw doc comment of symbol `sym`, minus usecase and define sections, augmented by
* missing sections of an inherited doc comment.
* If a symbol does not have a doc comment but some overridden version of it does,
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 703922b20a..55e78df4b7 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -2144,8 +2144,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
val start = st.pop()
seen ::= LocVarEntry(lv, start, end)
case _ =>
- // TODO SI-6049
- getCurrentCUnit().warning(iPos, "Visited SCOPE_EXIT before visiting corresponding SCOPE_ENTER. SI-6049")
+ // TODO SI-6049 track down the cause for these.
+ debugwarn(s"$iPos: Visited SCOPE_EXIT before visiting corresponding SCOPE_ENTER. SI-6191")
}
}
diff --git a/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala b/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala
index b8a0637b47..021e59a879 100644
--- a/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala
+++ b/src/compiler/scala/tools/nsc/doc/ScaladocGlobal.scala
@@ -25,8 +25,7 @@ trait ScaladocAnalyzer extends Analyzer {
if ((sym ne null) && (sym ne NoSymbol)) {
val comment = docDef.comment
- docComments(sym) = comment
- comment.defineVariables(sym)
+ fillDocComment(sym, comment)
val typer1 = newTyper(context.makeNewScope(docDef, context.owner))
for (useCase <- comment.useCases) {
typer1.silent(_ => typer1 defineUseCases useCase) match {
diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
index a81604235b..523e0d57b7 100644
--- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
+++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
@@ -161,17 +161,22 @@ trait CompilerControl { self: Global =>
/** Sets sync var `response` to doc comment information for a given symbol.
*
- * @param sym The symbol whose doc comment should be retrieved (might come from a classfile)
- * @param site The place where sym is observed.
- * @param source The source file that's supposed to contain the definition
- * @param response A response that will be set to the following:
- * If `source` contains a definition of a given symbol that has a doc comment,
- * the (expanded, raw, position) triplet for a comment, otherwise ("", "", NoPosition).
- * Note: This operation does not automatically load `source`. If `source`
- * is unloaded, it stays that way.
+ * @param sym The symbol whose doc comment should be retrieved (might come from a classfile)
+ * @param source The source file that's supposed to contain the definition
+ * @param site The symbol where 'sym' is observed
+ * @param fragments All symbols that can contribute to the generated documentation
+ * together with their source files.
+ * @param response A response that will be set to the following:
+ * If `source` contains a definition of a given symbol that has a doc comment,
+ * the (expanded, raw, position) triplet for a comment, otherwise ("", "", NoPosition).
+ * Note: This operation does not automatically load sources that are not yet loaded.
*/
- def askDocComment(sym: Symbol, site: Symbol, source: SourceFile, response: Response[(String, String, Position)]) =
- postWorkItem(new AskDocCommentItem(sym, site, source, response))
+ def askDocComment(sym: Symbol, source: SourceFile, site: Symbol, fragments: List[(Symbol,SourceFile)], response: Response[(String, String, Position)]): Unit =
+ postWorkItem(new AskDocCommentItem(sym, source, site, fragments, response))
+
+ @deprecated("Use method that accepts fragments", "2.10.2")
+ def askDocComment(sym: Symbol, site: Symbol, source: SourceFile, response: Response[(String, String, Position)]): Unit =
+ askDocComment(sym, source, site, (sym,source)::Nil, response)
/** Sets sync var `response` to list of members that are visible
* as members of the tree enclosing `pos`, possibly reachable by an implicit.
@@ -384,9 +389,9 @@ trait CompilerControl { self: Global =>
response raise new MissingResponse
}
- case class AskDocCommentItem(sym: Symbol, site: Symbol, source: SourceFile, response: Response[(String, String, Position)]) extends WorkItem {
- def apply() = self.getDocComment(sym, site, source, response)
- override def toString = "doc comment "+sym+" in "+source
+ case class AskDocCommentItem(sym: Symbol, source: SourceFile, site: Symbol, fragments: List[(Symbol,SourceFile)], response: Response[(String, String, Position)]) extends WorkItem {
+ def apply() = self.getDocComment(sym, source, site, fragments, response)
+ override def toString = "doc comment "+sym+" in "+source+" with fragments:"+fragments.mkString("(", ",", ")")
def raiseMissing() =
response raise new MissingResponse
diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala
index 921a07c805..33b10d1a9a 100644
--- a/src/compiler/scala/tools/nsc/interactive/Global.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Global.scala
@@ -665,8 +665,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
* If we do just removeUnit, some problems with default parameters can ensue.
* Calls to this method could probably be replaced by removeUnit once default parameters are handled more robustly.
*/
- private def afterRunRemoveUnitOf(source: SourceFile) {
- toBeRemovedAfterRun += source.file
+ private def afterRunRemoveUnitsOf(sources: List[SourceFile]) {
+ toBeRemovedAfterRun ++= sources map (_.file)
}
/** A fully attributed tree located at position `pos` */
@@ -674,7 +674,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
case None =>
reloadSources(List(pos.source))
try typedTreeAt(pos)
- finally afterRunRemoveUnitOf(pos.source)
+ finally afterRunRemoveUnitsOf(List(pos.source))
case Some(unit) =>
informIDE("typedTreeAt " + pos)
parseAndEnter(unit)
@@ -724,14 +724,23 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
respond(response)(typedTree(source, forceReload))
}
+ private def withTempUnits[T](sources: List[SourceFile])(f: (SourceFile => RichCompilationUnit) => T): T = {
+ val unitOfSrc: SourceFile => RichCompilationUnit = src => unitOfFile(src.file)
+ sources filterNot (getUnit(_).isDefined) match {
+ case Nil =>
+ f(unitOfSrc)
+ case unknown =>
+ reloadSources(unknown)
+ try {
+ f(unitOfSrc)
+ } finally
+ afterRunRemoveUnitsOf(unknown)
+ }
+ }
+
private def withTempUnit[T](source: SourceFile)(f: RichCompilationUnit => T): T =
- getUnit(source) match {
- case None =>
- reloadSources(List(source))
- try f(getUnit(source).get)
- finally afterRunRemoveUnitOf(source)
- case Some(unit) =>
- f(unit)
+ withTempUnits(List(source)){ srcToUnit =>
+ f(srcToUnit(source))
}
/** Find a 'mirror' of symbol `sym` in unit `unit`. Pre: `unit is loaded. */
@@ -795,50 +804,36 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
}
}
+ private def forceDocComment(sym: Symbol, unit: RichCompilationUnit) {
+ unit.body foreachPartial {
+ case DocDef(comment, defn) if defn.symbol == sym =>
+ fillDocComment(defn.symbol, comment)
+ EmptyTree
+ case _: ValOrDefDef =>
+ EmptyTree
+ }
+ }
+
/** Implements CompilerControl.askDocComment */
- private[interactive] def getDocComment(sym: Symbol, site: Symbol, source: SourceFile, response: Response[(String, String, Position)]) {
- informIDE("getDocComment "+sym+" "+source)
+ private[interactive] def getDocComment(sym: Symbol, source: SourceFile, site: Symbol, fragments: List[(Symbol,SourceFile)],
+ response: Response[(String, String, Position)]) {
+ informIDE(s"getDocComment $sym at $source site $site")
respond(response) {
- withTempUnit(source){ u =>
- val mirror = findMirrorSymbol(sym, u)
+ withTempUnits(fragments.toList.unzip._2){ units =>
+ for((sym, src) <- fragments) {
+ val mirror = findMirrorSymbol(sym, units(src))
+ if (mirror ne NoSymbol) forceDocComment(mirror, units(src))
+ }
+ val mirror = findMirrorSymbol(sym, units(source))
if (mirror eq NoSymbol)
("", "", NoPosition)
else {
- forceDocComment(mirror, u)
- (expandedDocComment(mirror), rawDocComment(mirror), docCommentPos(mirror))
+ (expandedDocComment(mirror, site), rawDocComment(mirror), docCommentPos(mirror))
}
}
}
}
- private def forceDocComment(sym: Symbol, unit: RichCompilationUnit) {
- // Either typer has been run and we don't find DocDef,
- // or we force the targeted typecheck here.
- // In both cases doc comment maps should be filled for the subject symbol.
- val docTree =
- unit.body find {
- case DocDef(_, defn) if defn.symbol eq sym => true
- case _ => false
- }
-
- for (t <- docTree) {
- debugLog("Found DocDef tree for "+sym)
- // Cannot get a typed tree at position since DocDef range is transparent.
- val prevPos = unit.targetPos
- val prevInterruptsEnabled = interruptsEnabled
- try {
- unit.targetPos = t.pos
- interruptsEnabled = true
- typeCheck(unit)
- } catch {
- case _: TyperResult => // ignore since we are after the side effect.
- } finally {
- unit.targetPos = prevPos
- interruptsEnabled = prevInterruptsEnabled
- }
- }
- }
-
def stabilizedType(tree: Tree): Type = tree match {
case Ident(_) if tree.symbol.isStable =>
singleType(NoPrefix, tree.symbol)
diff --git a/src/compiler/scala/tools/nsc/interactive/Picklers.scala b/src/compiler/scala/tools/nsc/interactive/Picklers.scala
index 09533fdeb5..b184afd0f5 100644
--- a/src/compiler/scala/tools/nsc/interactive/Picklers.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Picklers.scala
@@ -164,8 +164,8 @@ trait Picklers { self: Global =>
.asClass (classOf[AskLinkPosItem])
implicit def askDocCommentItem: CondPickler[AskDocCommentItem] =
- (pkl[Symbol] ~ pkl[Symbol] ~ pkl[SourceFile])
- .wrapped { case sym ~ site ~ source => new AskDocCommentItem(sym, site, source, new Response) } { item => item.sym ~ item.site ~ item.source }
+ (pkl[Symbol] ~ pkl[SourceFile] ~ pkl[Symbol] ~ pkl[List[(Symbol,SourceFile)]])
+ .wrapped { case sym ~ source ~ site ~ fragments => new AskDocCommentItem(sym, source, site, fragments, new Response) } { item => item.sym ~ item.source ~ item.site ~ item.fragments }
.asClass (classOf[AskDocCommentItem])
implicit def askLoadedTypedItem: CondPickler[AskLoadedTypedItem] =
diff --git a/src/compiler/scala/tools/nsc/io/Pickler.scala b/src/compiler/scala/tools/nsc/io/Pickler.scala
index 43a6ef3c61..0e7da37c52 100644
--- a/src/compiler/scala/tools/nsc/io/Pickler.scala
+++ b/src/compiler/scala/tools/nsc/io/Pickler.scala
@@ -327,6 +327,12 @@ object Pickler {
}
}
+ /** A pickler for pairs, represented as `~`-pairs */
+ implicit def tuple2Pickler[T1: Pickler, T2: Pickler]: Pickler[(T1, T2)] =
+ (pkl[T1] ~ pkl[T2])
+ .wrapped { case x1 ~ x2 => (x1, x2) } { case (x1, x2) => x1 ~ x2 }
+ .labelled ("tuple2")
+
/** A pickler for 3-tuples, represented as `~`-tuples */
implicit def tuple3Pickler[T1, T2, T3](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3]): Pickler[(T1, T2, T3)] =
(p1 ~ p2 ~ p3)
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index e676abb86a..55b9ce1be9 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -447,6 +447,7 @@ abstract class Erasure extends AddInterfaces
def checkPair(member: Symbol, other: Symbol) {
val otpe = specialErasure(root)(other.tpe)
val bridgeNeeded = exitingErasure (
+ !member.isMacro &&
!(other.tpe =:= member.tpe) &&
!(deconstMap(other.tpe) =:= deconstMap(member.tpe)) &&
{ var e = bridgesScope.lookupEntry(member.name)
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
index 4fc71a7410..69d9987b05 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
@@ -287,236 +287,9 @@ trait Logic extends Debugging {
def findModelFor(f: Formula): Model
def findAllModelsFor(f: Formula): List[Model]
}
-
- trait CNF extends PropositionalLogic {
-
- /** Override Array creation for efficiency (to not go through reflection). */
- private implicit val clauseTag: scala.reflect.ClassTag[Clause] = new scala.reflect.ClassTag[Clause] {
- def runtimeClass: java.lang.Class[Clause] = classOf[Clause]
- final override def newArray(len: Int): Array[Clause] = new Array[Clause](len)
- }
-
- import scala.collection.mutable.ArrayBuffer
- type FormulaBuilder = ArrayBuffer[Clause]
- def formulaBuilder = ArrayBuffer[Clause]()
- def formulaBuilderSized(init: Int) = new ArrayBuffer[Clause](init)
- def addFormula(buff: FormulaBuilder, f: Formula): Unit = buff ++= f
- def toFormula(buff: FormulaBuilder): Formula = buff
-
- // CNF: a formula is a conjunction of clauses
- type Formula = FormulaBuilder
- def formula(c: Clause*): Formula = ArrayBuffer(c: _*)
-
- type Clause = Set[Lit]
- // a clause is a disjunction of distinct literals
- def clause(l: Lit*): Clause = l.toSet
-
- type Lit
- def Lit(sym: Sym, pos: Boolean = true): Lit
-
- def andFormula(a: Formula, b: Formula): Formula = a ++ b
- def simplifyFormula(a: Formula): Formula = a.distinct
-
- private def merge(a: Clause, b: Clause) = a ++ b
-
- // throws an AnalysisBudget.Exception when the prop results in a CNF that's too big
- // TODO: be smarter/more efficient about this (http://lara.epfl.ch/w/sav09:tseitin_s_encoding)
- def eqFreePropToSolvable(p: Prop): Formula = {
- def negationNormalFormNot(p: Prop, budget: Int): Prop =
- if (budget <= 0) throw AnalysisBudget.exceeded
- else p match {
- case And(a, b) => Or(negationNormalFormNot(a, budget - 1), negationNormalFormNot(b, budget - 1))
- case Or(a, b) => And(negationNormalFormNot(a, budget - 1), negationNormalFormNot(b, budget - 1))
- case Not(p) => negationNormalForm(p, budget - 1)
- case True => False
- case False => True
- case s: Sym => Not(s)
- }
-
- def negationNormalForm(p: Prop, budget: Int = AnalysisBudget.max): Prop =
- if (budget <= 0) throw AnalysisBudget.exceeded
- else p match {
- case And(a, b) => And(negationNormalForm(a, budget - 1), negationNormalForm(b, budget - 1))
- case Or(a, b) => Or(negationNormalForm(a, budget - 1), negationNormalForm(b, budget - 1))
- case Not(negated) => negationNormalFormNot(negated, budget - 1)
- case True
- | False
- | (_ : Sym) => p
- }
-
- val TrueF = formula()
- val FalseF = formula(clause())
- def lit(s: Sym) = formula(clause(Lit(s)))
- def negLit(s: Sym) = formula(clause(Lit(s, pos = false)))
-
- def conjunctiveNormalForm(p: Prop, budget: Int = AnalysisBudget.max): Formula = {
- def distribute(a: Formula, b: Formula, budget: Int): Formula =
- if (budget <= 0) throw AnalysisBudget.exceeded
- else
- (a, b) match {
- // true \/ _ = true
- // _ \/ true = true
- case (trueA, trueB) if trueA.size == 0 || trueB.size == 0 => TrueF
- // lit \/ lit
- case (a, b) if a.size == 1 && b.size == 1 => formula(merge(a(0), b(0)))
- // (c1 /\ ... /\ cn) \/ d = ((c1 \/ d) /\ ... /\ (cn \/ d))
- // d \/ (c1 /\ ... /\ cn) = ((d \/ c1) /\ ... /\ (d \/ cn))
- case (cs, ds) =>
- val (big, small) = if (cs.size > ds.size) (cs, ds) else (ds, cs)
- big flatMap (c => distribute(formula(c), small, budget - (big.size*small.size)))
- }
-
- if (budget <= 0) throw AnalysisBudget.exceeded
-
- p match {
- case True => TrueF
- case False => FalseF
- case s: Sym => lit(s)
- case Not(s: Sym) => negLit(s)
- case And(a, b) =>
- val cnfA = conjunctiveNormalForm(a, budget - 1)
- val cnfB = conjunctiveNormalForm(b, budget - cnfA.size)
- cnfA ++ cnfB
- case Or(a, b) =>
- val cnfA = conjunctiveNormalForm(a)
- val cnfB = conjunctiveNormalForm(b)
- distribute(cnfA, cnfB, budget - (cnfA.size + cnfB.size))
- }
- }
-
- val start = if (Statistics.canEnable) Statistics.startTimer(patmatCNF) else null
- val res = conjunctiveNormalForm(negationNormalForm(p))
-
- if (Statistics.canEnable) Statistics.stopTimer(patmatCNF, start)
-
- //
- if (Statistics.canEnable) patmatCNFSizes(res.size).value += 1
-
-// debug.patmat("cnf for\n"+ p +"\nis:\n"+cnfString(res))
- res
- }
- }
-
- trait DPLLSolver extends CNF {
- // a literal is a (possibly negated) variable
- def Lit(sym: Sym, pos: Boolean = true) = new Lit(sym, pos)
- class Lit(val sym: Sym, val pos: Boolean) {
- override def toString = if (!pos) "-"+ sym.toString else sym.toString
- override def equals(o: Any) = o match {
- case o: Lit => (o.sym eq sym) && (o.pos == pos)
- case _ => false
- }
- override def hashCode = sym.hashCode + pos.hashCode
-
- def unary_- = Lit(sym, !pos)
- }
-
- def cnfString(f: Formula) = alignAcrossRows(f map (_.toList) toList, "\\/", " /\\\n")
-
- // adapted from http://lara.epfl.ch/w/sav10:simple_sat_solver (original by Hossein Hojjat)
- val EmptyModel = Map.empty[Sym, Boolean]
- val NoModel: Model = null
-
- // returns all solutions, if any (TODO: better infinite recursion backstop -- detect fixpoint??)
- def findAllModelsFor(f: Formula): List[Model] = {
- val vars: Set[Sym] = f.flatMap(_ collect {case l: Lit => l.sym}).toSet
- // debug.patmat("vars "+ vars)
- // the negation of a model -(S1=True/False /\ ... /\ SN=True/False) = clause(S1=False/True, ...., SN=False/True)
- def negateModel(m: Model) = clause(m.toSeq.map{ case (sym, pos) => Lit(sym, !pos) } : _*)
-
- def findAllModels(f: Formula, models: List[Model], recursionDepthAllowed: Int = 10): List[Model]=
- if (recursionDepthAllowed == 0) models
- else {
- debug.patmat("find all models for\n"+ cnfString(f))
- val model = findModelFor(f)
- // if we found a solution, conjunct the formula with the model's negation and recurse
- if (model ne NoModel) {
- val unassigned = (vars -- model.keySet).toList
- debug.patmat("unassigned "+ unassigned +" in "+ model)
- def force(lit: Lit) = {
- val model = withLit(findModelFor(dropUnit(f, lit)), lit)
- if (model ne NoModel) List(model)
- else Nil
- }
- val forced = unassigned flatMap { s =>
- force(Lit(s, pos = true)) ++ force(Lit(s, pos = false))
- }
- debug.patmat("forced "+ forced)
- val negated = negateModel(model)
- findAllModels(f :+ negated, model :: (forced ++ models), recursionDepthAllowed - 1)
- }
- else models
- }
-
- findAllModels(f, Nil)
- }
-
- private def withLit(res: Model, l: Lit): Model = if (res eq NoModel) NoModel else res + (l.sym -> l.pos)
- private def dropUnit(f: Formula, unitLit: Lit): Formula = {
- val negated = -unitLit
- // drop entire clauses that are trivially true
- // (i.e., disjunctions that contain the literal we're making true in the returned model),
- // and simplify clauses by dropping the negation of the literal we're making true
- // (since False \/ X == X)
- val dropped = formulaBuilderSized(f.size)
- for {
- clause <- f
- if !(clause contains unitLit)
- } dropped += (clause - negated)
- dropped
- }
-
- def findModelFor(f: Formula): Model = {
- @inline def orElse(a: Model, b: => Model) = if (a ne NoModel) a else b
-
- debug.patmat("DPLL\n"+ cnfString(f))
-
- val start = if (Statistics.canEnable) Statistics.startTimer(patmatAnaDPLL) else null
-
- val satisfiableWithModel: Model =
- if (f isEmpty) EmptyModel
- else if(f exists (_.isEmpty)) NoModel
- else f.find(_.size == 1) match {
- case Some(unitClause) =>
- val unitLit = unitClause.head
- // debug.patmat("unit: "+ unitLit)
- withLit(findModelFor(dropUnit(f, unitLit)), unitLit)
- case _ =>
- // partition symbols according to whether they appear in positive and/or negative literals
- val pos = new mutable.HashSet[Sym]()
- val neg = new mutable.HashSet[Sym]()
- f.foreach{_.foreach{ lit =>
- if (lit.pos) pos += lit.sym else neg += lit.sym
- }}
- // appearing in both positive and negative
- val impures = pos intersect neg
- // appearing only in either positive/negative positions
- val pures = (pos ++ neg) -- impures
-
- if (pures nonEmpty) {
- val pureSym = pures.head
- // turn it back into a literal
- // (since equality on literals is in terms of equality
- // of the underlying symbol and its positivity, simply construct a new Lit)
- val pureLit = Lit(pureSym, pos(pureSym))
- // debug.patmat("pure: "+ pureLit +" pures: "+ pures +" impures: "+ impures)
- val simplified = f.filterNot(_.contains(pureLit))
- withLit(findModelFor(simplified), pureLit)
- } else {
- val split = f.head.head
- // debug.patmat("split: "+ split)
- orElse(findModelFor(f :+ clause(split)), findModelFor(f :+ clause(-split)))
- }
- }
-
- if (Statistics.canEnable) Statistics.stopTimer(patmatAnaDPLL, start)
-
- satisfiableWithModel
- }
- }
}
-trait ScalaLogic extends Logic { self: PatternMatching =>
+trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
trait TreesAndTypesDomain extends PropositionalLogic with CheckableTreeAndTypeAnalysis {
type Type = global.Type
type Tree = global.Tree
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
index 86eb3c9666..3ee75df6c4 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
@@ -125,23 +125,15 @@ trait TreeAndTypeAnalysis extends Debugging {
}
}
-trait MatchAnalysis extends TreeAndTypeAnalysis { self: PatternMatching =>
- import PatternMatchingStats._
- import global.{Tree, Type, Symbol, CaseDef, atPos,
- Select, Block, ThisType, SingleType, NoPrefix, NoType, definitions, needsOuterTest,
- ConstantType, Literal, Constant, gen, This, analyzer, EmptyTree, map2, NoSymbol, Traverser,
- Function, Typed, treeInfo, DefTree, ValDef, nme, appliedType, Name, WildcardType, Ident, TypeRef,
- UniqueType, RefinedType, currentUnit, SingletonType, singleType, ModuleClassSymbol,
- nestedMemberType, TypeMap, EmptyScope, Apply, If, Bind, lub, Alternative, deriveCaseDef, Match, MethodType, LabelDef, TypeTree, Throw, newTermName}
-
- import definitions._
- import analyzer.{Typer, ErrorUtils, formalTypes}
+trait MatchApproximation extends TreeAndTypeAnalysis with ScalaLogic with MatchTreeMaking {
+ import global.{Tree, Type, NoType, Symbol, NoSymbol, ConstantType, Literal, Constant, Ident, UniqueType, RefinedType, EmptyScope}
+ import global.definitions.{ListClass, NilModule}
/**
* Represent a match as a formula in propositional logic that encodes whether the match matches (abstractly: we only consider types)
*
*/
- trait TreeMakerApproximation extends TreeMakers with PropositionalLogic with TreesAndTypesDomain with CheckableTreeAndTypeAnalysis { self: CodegenCore =>
+ trait MatchApproximator extends TreeMakers with TreesAndTypesDomain {
object Test {
var currId = 0
}
@@ -348,7 +340,14 @@ trait MatchAnalysis extends TreeAndTypeAnalysis { self: PatternMatching =>
}
}
- trait SymbolicMatchAnalysis extends TreeMakerApproximation { self: CodegenCore =>
+}
+
+trait MatchAnalysis extends MatchApproximation {
+ import PatternMatchingStats._
+ import global.{Tree, Type, Symbol, NoSymbol, Ident, Select}
+ import global.definitions.{isPrimitiveValueClass, ConsClass, isTupleSymbol}
+
+ trait MatchAnalyzer extends MatchApproximator {
def uncheckedWarning(pos: Position, msg: String) = global.currentUnit.uncheckedWarning(pos, msg)
def warn(pos: Position, ex: AnalysisBudget.Exception, kind: String) = uncheckedWarning(pos, s"Cannot check match for $kind.\n${ex.advice}")
@@ -498,7 +497,7 @@ trait MatchAnalysis extends TreeAndTypeAnalysis { self: PatternMatching =>
// a way to construct a value that will make the match fail: a constructor invocation, a constant, an object of some type)
class CounterExample {
- protected[SymbolicMatchAnalysis] def flattenConsArgs: List[CounterExample] = Nil
+ protected[MatchAnalyzer] def flattenConsArgs: List[CounterExample] = Nil
def coveredBy(other: CounterExample): Boolean = this == other || other == WildcardExample
}
case class ValueExample(c: ValueConst) extends CounterExample { override def toString = c.toString }
@@ -513,11 +512,11 @@ trait MatchAnalysis extends TreeAndTypeAnalysis { self: PatternMatching =>
}
}
case class ListExample(ctorArgs: List[CounterExample]) extends CounterExample {
- protected[SymbolicMatchAnalysis] override def flattenConsArgs: List[CounterExample] = ctorArgs match {
+ protected[MatchAnalyzer] override def flattenConsArgs: List[CounterExample] = ctorArgs match {
case hd :: tl :: Nil => hd :: tl.flattenConsArgs
case _ => Nil
}
- protected[SymbolicMatchAnalysis] lazy val elems = flattenConsArgs
+ protected[MatchAnalyzer] lazy val elems = flattenConsArgs
override def coveredBy(other: CounterExample): Boolean =
other match {
@@ -691,5 +690,18 @@ trait MatchAnalysis extends TreeAndTypeAnalysis { self: PatternMatching =>
// this is the variable we want a counter example for
VariableAssignment(scrutVar).toCounterExample()
}
+
+ def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, suppression: Suppression): Unit = {
+ if (!suppression.unreachable) {
+ unreachableCase(prevBinder, cases, pt) foreach { caseIndex =>
+ reportUnreachable(cases(caseIndex).last.pos)
+ }
+ }
+ if (!suppression.exhaustive) {
+ val counterExamples = exhaustive(prevBinder, cases, pt)
+ if (counterExamples.nonEmpty)
+ reportMissingCases(prevBinder.pos, counterExamples)
+ }
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala
index 273aebd71e..416bdf50f0 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala
@@ -17,7 +17,7 @@ import scala.reflect.internal.util.NoPosition
* We have two modes in which to emit trees: optimized (the default)
* and pure (aka "virtualized": match is parametric in its monad).
*/
-trait MatchCodeGen { self: PatternMatching =>
+trait MatchCodeGen extends Interface {
import PatternMatchingStats._
import global.{nme, treeInfo, definitions, gen, Tree, Type, Symbol, NoSymbol,
appliedType, NoType, MethodType, newTermName, Name,
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala
index c14b4ebc3b..dcf2413b15 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala
@@ -19,7 +19,7 @@ import scala.reflect.internal.util.NoPosition
*
* TODO: split out match analysis
*/
-trait MatchOptimization { self: PatternMatching =>
+trait MatchOptimization extends MatchTreeMaking with MatchAnalysis {
import PatternMatchingStats._
import global.{Tree, Type, Symbol, NoSymbol, CaseDef, atPos,
ConstantType, Literal, Constant, gen, EmptyTree,
@@ -30,7 +30,7 @@ trait MatchOptimization { self: PatternMatching =>
////
- trait CommonSubconditionElimination extends TreeMakerApproximation { self: OptimizedCodegen =>
+ trait CommonSubconditionElimination extends OptimizedCodegen with MatchApproximator {
/** a flow-sensitive, generalised, common sub-expression elimination
* reuse knowledge from performed tests
* the only sub-expressions we consider are the conditions and results of the three tests (type, type&equality, equality)
@@ -205,19 +205,19 @@ trait MatchOptimization { self: PatternMatching =>
//// DCE
- trait DeadCodeElimination extends TreeMakers { self: CodegenCore =>
- // TODO: non-trivial dead-code elimination
- // e.g., the following match should compile to a simple instanceof:
- // case class Ident(name: String)
- // for (Ident(name) <- ts) println(name)
- def doDCE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = {
- // do minimal DCE
- cases
- }
- }
+// trait DeadCodeElimination extends TreeMakers {
+// // TODO: non-trivial dead-code elimination
+// // e.g., the following match should compile to a simple instanceof:
+// // case class Ident(name: String)
+// // for (Ident(name) <- ts) println(name)
+// def doDCE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = {
+// // do minimal DCE
+// cases
+// }
+// }
//// SWITCHES -- TODO: operate on Tests rather than TreeMakers
- trait SwitchEmission extends TreeMakers with OptimizedMatchMonadInterface { self: CodegenCore =>
+ trait SwitchEmission extends TreeMakers with OptimizedMatchMonadInterface {
import treeInfo.isGuardedCase
abstract class SwitchMaker {
@@ -589,26 +589,12 @@ trait MatchOptimization { self: PatternMatching =>
}
}
-
-
-
- trait MatchOptimizations extends CommonSubconditionElimination
- with DeadCodeElimination
- with SwitchEmission
- with OptimizedCodegen
- with SymbolicMatchAnalysis
- with DPLLSolver { self: TreeMakers =>
- override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): (List[List[TreeMaker]], List[Tree]) = {
- unreachableCase(prevBinder, cases, pt) foreach { caseIndex =>
- reportUnreachable(cases(caseIndex).last.pos)
- }
- if (!unchecked) {
- val counterExamples = exhaustive(prevBinder, cases, pt)
- if (counterExamples.nonEmpty)
- reportMissingCases(prevBinder.pos, counterExamples)
- }
-
- val optCases = doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt)
+ trait MatchOptimizer extends OptimizedCodegen
+ with SwitchEmission
+ with CommonSubconditionElimination {
+ override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = {
+ // TODO: do CSE on result of doDCE(prevBinder, cases, pt)
+ val optCases = doCSE(prevBinder, cases, pt)
val toHoist = (
for (treeMakers <- optCases)
yield treeMakers.collect{case tm: ReusedCondTreeMaker => tm.treesToHoist}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
index 3ee9009116..23b33e9be6 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
@@ -24,7 +24,7 @@ trait MatchTranslation { self: PatternMatching =>
repeatedToSeq, isRepeatedParamType, getProductArgs}
import global.analyzer.{ErrorUtils, formalTypes}
- trait MatchTranslator extends MatchMonadInterface { self: TreeMakers with CodegenCore =>
+ trait MatchTranslator extends TreeMakers {
import typer.context
// Why is it so difficult to say "here's a name and a context, give me any
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
index c9285f9229..202f3444f8 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
@@ -18,21 +18,26 @@ import scala.reflect.internal.util.NoPosition
* The IR is mostly concerned with sequencing, substitution, and rendering all necessary conditions,
* mostly agnostic to whether we're in optimized/pure (virtualized) mode.
*/
-trait MatchTreeMaking { self: PatternMatching =>
+trait MatchTreeMaking extends MatchCodeGen with Debugging {
import PatternMatchingStats._
import global.{Tree, Type, Symbol, CaseDef, atPos, settings,
Select, Block, ThisType, SingleType, NoPrefix, NoType, needsOuterTest,
ConstantType, Literal, Constant, gen, This, EmptyTree, map2, NoSymbol, Traverser,
- Function, Typed, treeInfo, TypeRef, DefTree}
+ Function, Typed, treeInfo, TypeRef, DefTree, Ident, nme}
import global.definitions.{SomeClass, AnyRefClass, UncheckedClass, BooleanClass}
+ final case class Suppression(exhaustive: Boolean, unreachable: Boolean)
+ object Suppression {
+ val NoSuppression = Suppression(false, false)
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// the making of the trees
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- trait TreeMakers extends TypedSubstitution { self: CodegenCore =>
- def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, unchecked: Boolean): (List[List[TreeMaker]], List[Tree]) =
- (cases, Nil)
+ trait TreeMakers extends TypedSubstitution with CodegenCore {
+ def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree])
+ def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, suppression: Suppression): Unit
def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree], unchecked: Boolean): Option[Tree] =
None
@@ -522,18 +527,24 @@ trait MatchTreeMaking { self: PatternMatching =>
def matchFailGen = (matchFailGenOverride orElse Some(CODE.MATCHERROR(_: Tree)))
debug.patmat("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}")))
- val (unchecked, requireSwitch) =
- if (settings.XnoPatmatAnalysis.value) (true, false)
+ val (suppression, requireSwitch): (Suppression, Boolean) =
+ if (settings.XnoPatmatAnalysis.value) (Suppression.NoSuppression, false)
else scrut match {
- case Typed(_, tpt) =>
- (tpt.tpe hasAnnotation UncheckedClass,
- // matches with two or fewer cases need not apply for switchiness (if-then-else will do)
- treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0)
+ case Typed(tree, tpt) =>
+ val suppressExhaustive = tpt.tpe hasAnnotation UncheckedClass
+ val supressUnreachable = tree match {
+ case Ident(name) if name startsWith nme.CHECK_IF_REFUTABLE_STRING => true // SI-7183 don't warn for withFilter's that turn out to be irrefutable.
+ case _ => false
+ }
+ val suppression = Suppression(suppressExhaustive, supressUnreachable)
+ // matches with two or fewer cases need not apply for switchiness (if-then-else will do)
+ val requireSwitch = treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0
+ (suppression, requireSwitch)
case _ =>
- (false, false)
+ (Suppression.NoSuppression, false)
}
- emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, unchecked).getOrElse{
+ emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{
if (requireSwitch) typer.context.unit.warning(scrut.pos, "could not emit switch for @switch annotated match")
if (casesNoSubstOnly nonEmpty) {
@@ -550,7 +561,9 @@ trait MatchTreeMaking { self: PatternMatching =>
}) None
else matchFailGen
- val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt, unchecked)
+ analyzeCases(scrutSym, casesNoSubstOnly, pt, suppression)
+
+ val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt)
val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases map combineExtractors, synthCatchAll)
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
index 38d148c915..8be8b72130 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
@@ -34,13 +34,14 @@ import scala.reflect.internal.util.Position
* - recover GADT typing by locally inserting implicit witnesses to type equalities derived from the current case, and considering these witnesses during subtyping (?)
* - recover exhaustivity/unreachability of user-defined extractors by partitioning the types they match on using an HList or similar type-level structure
*/
-trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
+trait PatternMatching extends Transform with TypingTransformers
with Debugging
with Interface
with MatchTranslation
with MatchTreeMaking
with MatchCodeGen
with ScalaLogic
+ with Solving
with MatchAnalysis
with MatchOptimization {
import global._
@@ -76,15 +77,20 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
}
}
- class PureMatchTranslator(val typer: analyzer.Typer, val matchStrategy: Tree) extends MatchTranslator with TreeMakers with PureCodegen
- class OptimizingMatchTranslator(val typer: analyzer.Typer) extends MatchTranslator with TreeMakers with MatchOptimizations
+ class PureMatchTranslator(val typer: analyzer.Typer, val matchStrategy: Tree) extends MatchTranslator with PureCodegen {
+ def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type) = (cases, Nil)
+ def analyzeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, suppression: Suppression): Unit = {}
+ }
+
+ class OptimizingMatchTranslator(val typer: analyzer.Typer) extends MatchTranslator
+ with MatchOptimizer
+ with MatchAnalyzer
+ with Solver
}
-trait HasGlobal {
+trait Debugging {
val global: Global
-}
-trait Debugging extends HasGlobal {
// TODO: the inliner fails to inline the closures to debug.patmat unless the method is nested in an object
object debug {
val printPatmat = global.settings.Ypatmatdebug.value
@@ -92,7 +98,7 @@ trait Debugging extends HasGlobal {
}
}
-trait Interface { self: ast.TreeDSL with HasGlobal =>
+trait Interface extends ast.TreeDSL {
import global.{newTermName, analyzer, Type, ErrorType, Symbol, Tree}
import analyzer.Typer
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala
new file mode 100644
index 0000000000..34cdbeba8e
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala
@@ -0,0 +1,242 @@
+/* NSC -- new Scala compiler
+ *
+ * Copyright 2011-2013 LAMP/EPFL
+ * @author Adriaan Moors
+ */
+
+package scala.tools.nsc.transform.patmat
+
+import scala.collection.mutable
+import scala.reflect.internal.util.Statistics
+
+// naive CNF translation and simple DPLL solver
+trait Solving extends Logic {
+ import PatternMatchingStats._
+ trait CNF extends PropositionalLogic {
+
+ /** Override Array creation for efficiency (to not go through reflection). */
+ private implicit val clauseTag: scala.reflect.ClassTag[Clause] = new scala.reflect.ClassTag[Clause] {
+ def runtimeClass: java.lang.Class[Clause] = classOf[Clause]
+ final override def newArray(len: Int): Array[Clause] = new Array[Clause](len)
+ }
+
+ import scala.collection.mutable.ArrayBuffer
+ type FormulaBuilder = ArrayBuffer[Clause]
+ def formulaBuilder = ArrayBuffer[Clause]()
+ def formulaBuilderSized(init: Int) = new ArrayBuffer[Clause](init)
+ def addFormula(buff: FormulaBuilder, f: Formula): Unit = buff ++= f
+ def toFormula(buff: FormulaBuilder): Formula = buff
+
+ // CNF: a formula is a conjunction of clauses
+ type Formula = FormulaBuilder
+ def formula(c: Clause*): Formula = ArrayBuffer(c: _*)
+
+ type Clause = Set[Lit]
+ // a clause is a disjunction of distinct literals
+ def clause(l: Lit*): Clause = l.toSet
+
+ type Lit
+ def Lit(sym: Sym, pos: Boolean = true): Lit
+
+ def andFormula(a: Formula, b: Formula): Formula = a ++ b
+ def simplifyFormula(a: Formula): Formula = a.distinct
+
+ private def merge(a: Clause, b: Clause) = a ++ b
+
+ // throws an AnalysisBudget.Exception when the prop results in a CNF that's too big
+ // TODO: be smarter/more efficient about this (http://lara.epfl.ch/w/sav09:tseitin_s_encoding)
+ def eqFreePropToSolvable(p: Prop): Formula = {
+ def negationNormalFormNot(p: Prop, budget: Int): Prop =
+ if (budget <= 0) throw AnalysisBudget.exceeded
+ else p match {
+ case And(a, b) => Or(negationNormalFormNot(a, budget - 1), negationNormalFormNot(b, budget - 1))
+ case Or(a, b) => And(negationNormalFormNot(a, budget - 1), negationNormalFormNot(b, budget - 1))
+ case Not(p) => negationNormalForm(p, budget - 1)
+ case True => False
+ case False => True
+ case s: Sym => Not(s)
+ }
+
+ def negationNormalForm(p: Prop, budget: Int = AnalysisBudget.max): Prop =
+ if (budget <= 0) throw AnalysisBudget.exceeded
+ else p match {
+ case And(a, b) => And(negationNormalForm(a, budget - 1), negationNormalForm(b, budget - 1))
+ case Or(a, b) => Or(negationNormalForm(a, budget - 1), negationNormalForm(b, budget - 1))
+ case Not(negated) => negationNormalFormNot(negated, budget - 1)
+ case True
+ | False
+ | (_ : Sym) => p
+ }
+
+ val TrueF = formula()
+ val FalseF = formula(clause())
+ def lit(s: Sym) = formula(clause(Lit(s)))
+ def negLit(s: Sym) = formula(clause(Lit(s, pos = false)))
+
+ def conjunctiveNormalForm(p: Prop, budget: Int = AnalysisBudget.max): Formula = {
+ def distribute(a: Formula, b: Formula, budget: Int): Formula =
+ if (budget <= 0) throw AnalysisBudget.exceeded
+ else
+ (a, b) match {
+ // true \/ _ = true
+ // _ \/ true = true
+ case (trueA, trueB) if trueA.size == 0 || trueB.size == 0 => TrueF
+ // lit \/ lit
+ case (a, b) if a.size == 1 && b.size == 1 => formula(merge(a(0), b(0)))
+ // (c1 /\ ... /\ cn) \/ d = ((c1 \/ d) /\ ... /\ (cn \/ d))
+ // d \/ (c1 /\ ... /\ cn) = ((d \/ c1) /\ ... /\ (d \/ cn))
+ case (cs, ds) =>
+ val (big, small) = if (cs.size > ds.size) (cs, ds) else (ds, cs)
+ big flatMap (c => distribute(formula(c), small, budget - (big.size*small.size)))
+ }
+
+ if (budget <= 0) throw AnalysisBudget.exceeded
+
+ p match {
+ case True => TrueF
+ case False => FalseF
+ case s: Sym => lit(s)
+ case Not(s: Sym) => negLit(s)
+ case And(a, b) =>
+ val cnfA = conjunctiveNormalForm(a, budget - 1)
+ val cnfB = conjunctiveNormalForm(b, budget - cnfA.size)
+ cnfA ++ cnfB
+ case Or(a, b) =>
+ val cnfA = conjunctiveNormalForm(a)
+ val cnfB = conjunctiveNormalForm(b)
+ distribute(cnfA, cnfB, budget - (cnfA.size + cnfB.size))
+ }
+ }
+
+ val start = if (Statistics.canEnable) Statistics.startTimer(patmatCNF) else null
+ val res = conjunctiveNormalForm(negationNormalForm(p))
+
+ if (Statistics.canEnable) Statistics.stopTimer(patmatCNF, start)
+
+ //
+ if (Statistics.canEnable) patmatCNFSizes(res.size).value += 1
+
+// debug.patmat("cnf for\n"+ p +"\nis:\n"+cnfString(res))
+ res
+ }
+ }
+
+ // simple solver using DPLL
+ trait Solver extends CNF {
+ // a literal is a (possibly negated) variable
+ def Lit(sym: Sym, pos: Boolean = true) = new Lit(sym, pos)
+ class Lit(val sym: Sym, val pos: Boolean) {
+ override def toString = if (!pos) "-"+ sym.toString else sym.toString
+ override def equals(o: Any) = o match {
+ case o: Lit => (o.sym eq sym) && (o.pos == pos)
+ case _ => false
+ }
+ override def hashCode = sym.hashCode + pos.hashCode
+
+ def unary_- = Lit(sym, !pos)
+ }
+
+ def cnfString(f: Formula) = alignAcrossRows(f map (_.toList) toList, "\\/", " /\\\n")
+
+ // adapted from http://lara.epfl.ch/w/sav10:simple_sat_solver (original by Hossein Hojjat)
+ val EmptyModel = Map.empty[Sym, Boolean]
+ val NoModel: Model = null
+
+ // returns all solutions, if any (TODO: better infinite recursion backstop -- detect fixpoint??)
+ def findAllModelsFor(f: Formula): List[Model] = {
+ val vars: Set[Sym] = f.flatMap(_ collect {case l: Lit => l.sym}).toSet
+ // debug.patmat("vars "+ vars)
+ // the negation of a model -(S1=True/False /\ ... /\ SN=True/False) = clause(S1=False/True, ...., SN=False/True)
+ def negateModel(m: Model) = clause(m.toSeq.map{ case (sym, pos) => Lit(sym, !pos) } : _*)
+
+ def findAllModels(f: Formula, models: List[Model], recursionDepthAllowed: Int = 10): List[Model]=
+ if (recursionDepthAllowed == 0) models
+ else {
+ debug.patmat("find all models for\n"+ cnfString(f))
+ val model = findModelFor(f)
+ // if we found a solution, conjunct the formula with the model's negation and recurse
+ if (model ne NoModel) {
+ val unassigned = (vars -- model.keySet).toList
+ debug.patmat("unassigned "+ unassigned +" in "+ model)
+ def force(lit: Lit) = {
+ val model = withLit(findModelFor(dropUnit(f, lit)), lit)
+ if (model ne NoModel) List(model)
+ else Nil
+ }
+ val forced = unassigned flatMap { s =>
+ force(Lit(s, pos = true)) ++ force(Lit(s, pos = false))
+ }
+ debug.patmat("forced "+ forced)
+ val negated = negateModel(model)
+ findAllModels(f :+ negated, model :: (forced ++ models), recursionDepthAllowed - 1)
+ }
+ else models
+ }
+
+ findAllModels(f, Nil)
+ }
+
+ private def withLit(res: Model, l: Lit): Model = if (res eq NoModel) NoModel else res + (l.sym -> l.pos)
+ private def dropUnit(f: Formula, unitLit: Lit): Formula = {
+ val negated = -unitLit
+ // drop entire clauses that are trivially true
+ // (i.e., disjunctions that contain the literal we're making true in the returned model),
+ // and simplify clauses by dropping the negation of the literal we're making true
+ // (since False \/ X == X)
+ val dropped = formulaBuilderSized(f.size)
+ for {
+ clause <- f
+ if !(clause contains unitLit)
+ } dropped += (clause - negated)
+ dropped
+ }
+
+ def findModelFor(f: Formula): Model = {
+ @inline def orElse(a: Model, b: => Model) = if (a ne NoModel) a else b
+
+ debug.patmat("DPLL\n"+ cnfString(f))
+
+ val start = if (Statistics.canEnable) Statistics.startTimer(patmatAnaDPLL) else null
+
+ val satisfiableWithModel: Model =
+ if (f isEmpty) EmptyModel
+ else if(f exists (_.isEmpty)) NoModel
+ else f.find(_.size == 1) match {
+ case Some(unitClause) =>
+ val unitLit = unitClause.head
+ // debug.patmat("unit: "+ unitLit)
+ withLit(findModelFor(dropUnit(f, unitLit)), unitLit)
+ case _ =>
+ // partition symbols according to whether they appear in positive and/or negative literals
+ val pos = new mutable.HashSet[Sym]()
+ val neg = new mutable.HashSet[Sym]()
+ f.foreach{_.foreach{ lit =>
+ if (lit.pos) pos += lit.sym else neg += lit.sym
+ }}
+ // appearing in both positive and negative
+ val impures = pos intersect neg
+ // appearing only in either positive/negative positions
+ val pures = (pos ++ neg) -- impures
+
+ if (pures nonEmpty) {
+ val pureSym = pures.head
+ // turn it back into a literal
+ // (since equality on literals is in terms of equality
+ // of the underlying symbol and its positivity, simply construct a new Lit)
+ val pureLit = Lit(pureSym, pos(pureSym))
+ // debug.patmat("pure: "+ pureLit +" pures: "+ pures +" impures: "+ impures)
+ val simplified = f.filterNot(_.contains(pureLit))
+ withLit(findModelFor(simplified), pureLit)
+ } else {
+ val split = f.head.head
+ // debug.patmat("split: "+ split)
+ orElse(findModelFor(f :+ clause(split)), findModelFor(f :+ clause(-split)))
+ }
+ }
+
+ if (Statistics.canEnable) Statistics.stopTimer(patmatAnaDPLL, start)
+
+ satisfiableWithModel
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 1781dc1932..959c5a0eb8 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -229,8 +229,13 @@ trait Typers extends Adaptations with Tags {
new SubstWildcardMap(tparams).apply(tp)
case TypeRef(_, sym, _) if sym.isAliasType =>
val tp0 = tp.dealias
- val tp1 = dropExistential(tp0)
- if (tp1 eq tp0) tp else tp1
+ if (tp eq tp0) {
+ debugwarn(s"dropExistential did not progress dealiasing $tp, see SI-7126")
+ tp
+ } else {
+ val tp1 = dropExistential(tp0)
+ if (tp1 eq tp0) tp else tp1
+ }
case _ => tp
}
@@ -1017,7 +1022,7 @@ trait Typers extends Adaptations with Tags {
val pre =
if (owner.isPackageClass) owner.thisType
else if (owner.isClass) context.enclosingSubClassContext(owner).prefix
- else NoPrefix
+ else NoPrefix
stabilize0(pre)
case Select(qualqual, _) =>
stabilize0(qualqual.tpe)
@@ -1572,19 +1577,19 @@ trait Typers extends Adaptations with Tags {
* from one of the parent types. Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`.
*/
private def typedPrimaryConstrBody(templ: Template)(actualSuperCall: => Tree): Tree =
- treeInfo.firstConstructor(templ.body) match {
+ treeInfo.firstConstructor(templ.body) match {
case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) =>
- val (preSuperStats, superCall) = {
- val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x))
- (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate)
- }
+ val (preSuperStats, superCall) = {
+ val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x))
+ (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate)
+ }
val superCall1 = (superCall match {
case global.pendingSuperCall => actualSuperCall
case EmptyTree => EmptyTree
}) orElse cunit
val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1)
val clazz = context.owner
- assert(clazz != NoSymbol, templ)
+ assert(clazz != NoSymbol, templ)
val cscope = context.outer.makeNewScope(ctor, context.outer.owner)
val cbody2 = { // called both during completion AND typing.
val typer1 = newTyper(cscope)
@@ -1592,12 +1597,12 @@ trait Typers extends Adaptations with Tags {
clazz.unsafeTypeParams foreach (sym => typer1.context.scope.enter(sym))
typer1.namer.enterValueParams(vparamss map (_.map(_.duplicate)))
typer1.typed(cbody1)
- }
+ }
- val preSuperVals = treeInfo.preSuperFields(templ.body)
- if (preSuperVals.isEmpty && preSuperStats.nonEmpty)
+ val preSuperVals = treeInfo.preSuperFields(templ.body)
+ if (preSuperVals.isEmpty && preSuperStats.nonEmpty)
devWarning("Wanted to zip empty presuper val list with " + preSuperStats)
- else
+ else
map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt setType ldef.symbol.tpe)
if (superCall1 == cunit) EmptyTree
@@ -1605,13 +1610,13 @@ trait Typers extends Adaptations with Tags {
case Block(_, expr) => expr
case tree => tree
}
- case _ =>
+ case _ =>
EmptyTree
- }
+ }
/** Makes sure that the first type tree in the list of parent types is always a class.
* If the first parent is a trait, prepend its supertype to the list until it's a class.
- */
+*/
private def normalizeFirstParent(parents: List[Tree]): List[Tree] = parents match {
case first :: rest if treeInfo.isTraitRef(first) =>
def explode(supertpt: Tree, acc: List[Tree]): List[Tree] = {
@@ -1638,14 +1643,14 @@ trait Typers extends Adaptations with Tags {
* So we strip the duplicates before typer.
*/
private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match {
- case Nil => Nil
- case x :: xs =>
- val sym = x.symbol
+ case Nil => Nil
+ case x :: xs =>
+ val sym = x.symbol
x :: fixDuplicateSyntheticParents(
- if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym)
- else xs
- )
- }
+ if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym)
+ else xs
+ )
+ }
def typedParentTypes(templ: Template): List[Tree] = templ.parents match {
case Nil => List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe)))
@@ -1666,16 +1671,16 @@ trait Typers extends Adaptations with Tags {
supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt))
} catch {
- case ex: TypeError =>
- // fallback in case of cyclic errors
- // @H none of the tests enter here but I couldn't rule it out
+ case ex: TypeError =>
+ // fallback in case of cyclic errors
+ // @H none of the tests enter here but I couldn't rule it out
// upd. @E when a definition inherits itself, we end up here
// because `typedParentType` triggers `initialize` for parent types symbols
- log("Type error calculating parents in template " + templ)
- log("Error: " + ex)
- ParentTypesError(templ, ex)
- List(TypeTree(AnyRefClass.tpe))
- }
+ log("Type error calculating parents in template " + templ)
+ log("Error: " + ex)
+ ParentTypesError(templ, ex)
+ List(TypeTree(AnyRefClass.tpe))
+ }
}
/** <p>Check that</p>
@@ -1921,7 +1926,7 @@ trait Typers extends Adaptations with Tags {
assert(clazz.info.decls != EmptyScope, clazz)
enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body)
if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore
- validateParentClasses(parents1, selfType)
+ validateParentClasses(parents1, selfType)
if (clazz.isCase)
validateNoCaseAncestor(clazz)
if (clazz.isTrait && hasSuperArgs(parents1.head))
@@ -1934,9 +1939,9 @@ trait Typers extends Adaptations with Tags {
checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])
val body = {
- val body =
- if (isPastTyper || reporter.hasErrors) templ.body
- else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
+ val body =
+ if (isPastTyper || reporter.hasErrors) templ.body
+ else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
val primaryCtor = treeInfo.firstConstructor(body)
val primaryCtor1 = primaryCtor match {
case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) =>
@@ -1999,7 +2004,7 @@ trait Typers extends Adaptations with Tags {
checkNonCyclic(vdef, tpt1)
if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable)
- VolatileValueError(vdef)
+ VolatileValueError(vdef)
val rhs1 =
if (vdef.rhs.isEmpty) {
@@ -2161,7 +2166,7 @@ trait Typers extends Adaptations with Tags {
foreachWithIndex(paramssTypes(meth.tpe)) { (paramList, listIdx) =>
foreachWithIndex(paramList) { (paramType, paramIdx) =>
- val sym = paramType.typeSymbol
+ val sym = paramType.typeSymbol
def paramPos = nthParamPos(listIdx, paramIdx)
/** Not enough to look for abstract types; have to recursively check the bounds
@@ -2176,17 +2181,17 @@ trait Typers extends Adaptations with Tags {
|| (!sym.hasTransOwner(meth) && failStruct(paramPos, "a type member of that refinement", what))
|| checkAbstract(sym.info.bounds.hi, "Type bound")
)
- }
+ }
tp0.dealiasWidenChain forall (t => check(t.typeSymbol))
}
checkAbstract(paramType, "Parameter type")
- if (sym.isDerivedValueClass)
- failStruct(paramPos, "a user-defined value class")
- if (paramType.isInstanceOf[ThisType] && sym == meth.owner)
- failStruct(paramPos, "the type of that refinement (self type)")
+ if (sym.isDerivedValueClass)
+ failStruct(paramPos, "a user-defined value class")
+ if (paramType.isInstanceOf[ThisType] && sym == meth.owner)
+ failStruct(paramPos, "the type of that refinement (self type)")
+ }
}
- }
if (resultType.typeSymbol.isDerivedValueClass)
failStruct(ddef.tpt.pos, "a user-defined value class", where = "Result type")
}
@@ -3442,7 +3447,7 @@ trait Typers extends Adaptations with Tags {
else None
case _ => None
- }
+ }
}
/**
@@ -3630,7 +3635,7 @@ trait Typers extends Adaptations with Tags {
val Function(arg :: Nil, rhs) = typed(func, mode, funcType)
rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil)
- }
+ }
def annInfo(t: Tree): AnnotationInfo = t match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
@@ -3658,7 +3663,7 @@ trait Typers extends Adaptations with Tags {
if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) ErroneousAnnotation
else annInfo(typedAnn)
})
- }
+ }
def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type?
sym.isTypeParameter && sym.owner.isJavaDefined
@@ -5008,8 +5013,8 @@ trait Typers extends Adaptations with Tags {
def unbound(t: Tree) = t.symbol == null || t.symbol == NoSymbol
cdef.pat match {
case Bind(name, i @ Ident(_)) if unbound(i) => warn(name)
- case i @ Ident(name) if unbound(i) => warn(name)
- case _ =>
+ case i @ Ident(name) if unbound(i) => warn(name)
+ case _ =>
}
}
diff --git a/src/library/scala/collection/mutable/ArrayOps.scala b/src/library/scala/collection/mutable/ArrayOps.scala
index fcbfd27738..4c996bfb88 100644
--- a/src/library/scala/collection/mutable/ArrayOps.scala
+++ b/src/library/scala/collection/mutable/ArrayOps.scala
@@ -90,18 +90,21 @@ trait ArrayOps[T] extends Any with ArrayLike[T, Array[T]] with CustomParalleliza
* @return An array obtained by replacing elements of this arrays with rows the represent.
*/
def transpose[U](implicit asArray: T => Array[U]): Array[Array[U]] = {
- def mkRowBuilder() = Array.newBuilder(ClassTag[U](arrayElementClass(elementClass)))
- val bs = asArray(head) map (_ => mkRowBuilder())
- for (xs <- this) {
- var i = 0
- for (x <- asArray(xs)) {
- bs(i) += x
- i += 1
+ val bb: Builder[Array[U], Array[Array[U]]] = Array.newBuilder(ClassTag[Array[U]](elementClass))
+ if (isEmpty) bb.result()
+ else {
+ def mkRowBuilder() = Array.newBuilder(ClassTag[U](arrayElementClass(elementClass)))
+ val bs = asArray(head) map (_ => mkRowBuilder())
+ for (xs <- this) {
+ var i = 0
+ for (x <- asArray(xs)) {
+ bs(i) += x
+ i += 1
+ }
}
+ for (b <- bs) bb += b.result()
+ bb.result()
}
- val bb: Builder[Array[U], Array[Array[U]]] = Array.newBuilder(ClassTag[Array[U]](elementClass))
- for (b <- bs) bb += b.result
- bb.result()
}
def seq = thisCollection
diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala
index c7d2fa42d3..e96fcc90df 100644
--- a/src/reflect/scala/reflect/internal/TreeInfo.scala
+++ b/src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -101,7 +101,8 @@ abstract class TreeInfo {
// However, before typing, applications of nullary functional values are also
// Apply(function, Nil) trees. To prevent them from being treated as pure,
// we check that the callee is a method.
- fn.symbol.isMethod && !fn.symbol.isLazy && isExprSafeToInline(fn)
+ // The callee might also be a Block, which has a null symbol, so we guard against that (SI-7185)
+ fn.symbol != null && fn.symbol.isMethod && !fn.symbol.isLazy && isExprSafeToInline(fn)
case Typed(expr, _) =>
isExprSafeToInline(expr)
case Block(stats, expr) =>
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 365e9a1682..f4befb1470 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -3161,12 +3161,19 @@ trait Types extends api.Types { self: SymbolTable =>
* ?TC[?T] <: Any
* }}}
*/
- def unifySimple = (
- (params.isEmpty || tp.typeSymbol == NothingClass || tp.typeSymbol == AnyClass) && {
+ def unifySimple = {
+ val sym = tp.typeSymbol
+ if (sym == NothingClass || sym == AnyClass) { // kind-polymorphic
+ // SI-7126 if we register some type alias `T=Any`, we can later end
+ // with malformed types like `T[T]` during type inference in
+ // `handlePolymorphicCall`. No such problem if we register `Any`.
+ addBound(sym.tpe)
+ true
+ } else if (params.isEmpty) {
addBound(tp)
true
- }
- )
+ } else false
+ }
/** Full case: involving a check of the form
* {{{
@@ -5257,7 +5264,9 @@ trait Types extends api.Types { self: SymbolTable =>
case _ =>
NoType
}
- patType match {
+ // See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest`
+ // generates an outer test based on `patType.prefix` with automatically dealises.
+ patType.dealias match {
case TypeRef(pre, sym, args) =>
val pre1 = maybeCreateDummyClone(pre, sym)
(pre1 ne NoType) && isPopulated(copyTypeRef(patType, pre1, sym, args), selType)
diff --git a/test/files/neg/t7185.check b/test/files/neg/t7185.check
new file mode 100644
index 0000000000..46f2cc797e
--- /dev/null
+++ b/test/files/neg/t7185.check
@@ -0,0 +1,7 @@
+t7185.scala:2: error: overloaded method value apply with alternatives:
+ (f: scala.xml.Node => Boolean)scala.xml.NodeSeq <and>
+ (i: Int)scala.xml.Node
+ cannot be applied to ()
+ <e></e>()
+ ^
+one error found
diff --git a/test/files/neg/t7185.scala b/test/files/neg/t7185.scala
new file mode 100644
index 0000000000..2f9284bc5f
--- /dev/null
+++ b/test/files/neg/t7185.scala
@@ -0,0 +1,3 @@
+object Test {
+ <e></e>()
+}
diff --git a/test/files/pos/t7126.scala b/test/files/pos/t7126.scala
new file mode 100644
index 0000000000..6720511e08
--- /dev/null
+++ b/test/files/pos/t7126.scala
@@ -0,0 +1,11 @@
+import language._
+
+object Test {
+ type T = Any
+ boom(???): Option[T] // SOE
+ def boom[CC[U]](t : CC[T]): Option[CC[T]] = None
+
+ // okay
+ foo(???): Option[Any]
+ def foo[CC[U]](t : CC[Any]): Option[CC[Any]] = None
+} \ No newline at end of file
diff --git a/test/files/pos/t7183.flags b/test/files/pos/t7183.flags
new file mode 100644
index 0000000000..e8fb65d50c
--- /dev/null
+++ b/test/files/pos/t7183.flags
@@ -0,0 +1 @@
+-Xfatal-warnings \ No newline at end of file
diff --git a/test/files/pos/t7183.scala b/test/files/pos/t7183.scala
new file mode 100644
index 0000000000..7647c1634b
--- /dev/null
+++ b/test/files/pos/t7183.scala
@@ -0,0 +1,13 @@
+class A
+object A {
+ def unapply(a: A): Some[A] = Some(a) // Change return type to Option[A] and the warning is gone
+}
+
+object Test {
+ for (A(a) <- List(new A)) yield a // spurious dead code warning.
+}
+
+// List(new A()).withFilter(((check$ifrefutable$2) => check$ifrefutable$2: @scala.unchecked match {
+// case A((a @ _)) => true
+// case _ => false // this is dead code, but it's compiler generated.
+// }))
diff --git a/test/files/pos/t7190.scala b/test/files/pos/t7190.scala
new file mode 100644
index 0000000000..f7ccded1b4
--- /dev/null
+++ b/test/files/pos/t7190.scala
@@ -0,0 +1,26 @@
+import scala.language.experimental.macros
+import scala.reflect.macros._
+
+trait A[T] {
+ def min[U >: T](implicit ord: Numeric[U]): T = macro A.min[T, U]
+}
+
+object A {
+ def min[T: c.WeakTypeTag, U >: T: c.WeakTypeTag](c: Context)(ord: c.Expr[Numeric[U]]): c.Expr[T] = {
+ c.universe.reify {
+ ord.splice.zero.asInstanceOf[T]
+ }
+ }
+}
+
+class B extends A[Int] {
+ override def min[U >: Int](implicit ord: Numeric[U]): Int = macro B.min[U]
+}
+
+object B {
+ def min[U >: Int: c.WeakTypeTag](c: Context)(ord: c.Expr[Numeric[U]]): c.Expr[Int] = {
+ c.universe.reify {
+ ord.splice.zero.asInstanceOf[Int]
+ }
+ }
+} \ No newline at end of file
diff --git a/test/files/presentation/doc.check b/test/files/presentation/doc.check
index e33756773d..5a3ff13151 100644
--- a/test/files/presentation/doc.check
+++ b/test/files/presentation/doc.check
@@ -1,49 +1 @@
-reload: Test.scala
-body:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This is a test comment), Text(.)))), Text(
-))))))
-@example:Body(List(Paragraph(Chain(List(Summary(Monospace(Text("abb".permutations = Iterator(abb, bab, bba)))))))))
-@version:
-@since:
-@todo:
-@note:
-@see:
-body:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This is a test comment), Text(.)))), Text(
-))))))
-@example:Body(List(Paragraph(Chain(List(Summary(Monospace(Text("abb".permutations = Iterator(abb, bab, bba)))))))))
-@version:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(1), Text(.)))), Text(0, 09/07/2012))))))
-@since:
-@todo:
-@note:
-@see:
-body:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This is a test comment), Text(.)))), Text(
-))))))
-@example:Body(List(Paragraph(Chain(List(Summary(Monospace(Text("abb".permutations = Iterator(abb, bab, bba)))))))))
-@version:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(1), Text(.)))), Text(0, 09/07/2012))))))
-@since:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(2), Text(.)))), Text(10))))))
-@todo:
-@note:
-@see:
-body:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This is a test comment), Text(.)))), Text(
-))))))
-@example:Body(List(Paragraph(Chain(List(Summary(Monospace(Text("abb".permutations = Iterator(abb, bab, bba)))))))))
-@version:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(1), Text(.)))), Text(0, 09/07/2012))))))
-@since:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(2), Text(.)))), Text(10))))))
-@todo:Body(List(Paragraph(Chain(List(Summary(Text(this method is unsafe)))))))
-@note:
-@see:
-body:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This is a test comment), Text(.)))), Text(
-))))))
-@example:Body(List(Paragraph(Chain(List(Summary(Monospace(Text("abb".permutations = Iterator(abb, bab, bba)))))))))
-@version:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(1), Text(.)))), Text(0, 09/07/2012))))))
-@since:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(2), Text(.)))), Text(10))))))
-@todo:Body(List(Paragraph(Chain(List(Summary(Text(this method is unsafe)))))))
-@note:Body(List(Paragraph(Chain(List(Summary(Text(Don't inherit!)))))))
-@see:
-body:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This is a test comment), Text(.)))), Text(
-))))))
-@example:Body(List(Paragraph(Chain(List(Summary(Monospace(Text("abb".permutations = Iterator(abb, bab, bba)))))))))
-@version:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(1), Text(.)))), Text(0, 09/07/2012))))))
-@since:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(2), Text(.)))), Text(10))))))
-@todo:Body(List(Paragraph(Chain(List(Summary(Text(this method is unsafe)))))))
-@note:Body(List(Paragraph(Chain(List(Summary(Text(Don't inherit!)))))))
-@see:Body(List(Paragraph(Chain(List(Summary(Text(some other method)))))))
+reload: Base.scala, Class.scala, Derived.scala
diff --git a/test/files/presentation/doc/doc.scala b/test/files/presentation/doc/doc.scala
index f9ebd09876..d198f4c324 100755
--- a/test/files/presentation/doc/doc.scala
+++ b/test/files/presentation/doc/doc.scala
@@ -4,27 +4,40 @@ import scala.tools.nsc.doc.base.comment._
import scala.tools.nsc.interactive._
import scala.tools.nsc.interactive.tests._
import scala.tools.nsc.util._
-import scala.tools.nsc.io._
object Test extends InteractiveTest {
val tags = Seq(
"@example `\"abb\".permutations = Iterator(abb, bab, bba)`",
"@version 1.0, 09/07/2012",
"@since 2.10",
- "@todo this method is unsafe",
+ "@todo this is unsafe!",
"@note Don't inherit!",
- "@see some other method"
+ "@see something else"
)
- val comment = "This is a test comment."
- val caret = "<caret>"
+ val names = Seq("Class", "Def", "Val", "Var", "AbstracType", "TypeAlias", "Trait", "InnerClass")
+ val bareText =
+ """abstract class %s {
+ | def %s = ""
+ | val %s = ""
+ | var %s: String = _
+ | type %s
+ | type %s = String
+ | class %s
+ |}
+ |trait %s""".stripMargin.format(names: _*)
+
+ def docComment(nTags: Int) = "/**\n%s*/".format(tags.take(nTags).mkString("\n"))
+
+ def text(name: String, nTags: Int) = {
+ val nameIndex = bareText.indexOf(name)
+ val (pre, post) = bareText.splitAt(nameIndex)
+ val crIndex = pre.lastIndexOf("\n")
+ val (prepre, prepost) = pre.splitAt(crIndex)
+ prepre + docComment(nTags) + prepost + post
+ }
+
- def text(nTags: Int) =
- """|/** %s
- |
- | * %s */
- |trait Commented {}
- |class User(c: %sCommented)""".stripMargin.format(comment, tags take nTags mkString "\n", caret)
override lazy val compiler = {
prepareSettings(settings)
@@ -44,9 +57,9 @@ object Test extends InteractiveTest {
override def forScaladoc = true
- def getComment(sym: Symbol, source: SourceFile) = {
+ def getComment(sym: Symbol, source: SourceFile, fragments: List[(Symbol,SourceFile)]): Option[Comment] = {
val docResponse = new Response[(String, String, Position)]
- askDocComment(sym, sym.owner, source, docResponse)
+ askDocComment(sym, source, sym.owner, fragments, docResponse)
docResponse.get.left.toOption flatMap {
case (expanded, raw, pos) =>
if (expanded.isEmpty)
@@ -59,37 +72,74 @@ object Test extends InteractiveTest {
}
override def runDefaultTests() {
- for (i <- 1 to tags.length) {
- val markedText = text(i)
- val idx = markedText.indexOf(caret)
- val fileText = markedText.substring(0, idx) + markedText.substring(idx + caret.length)
- val source = sourceFiles(0)
- val batch = new BatchSourceFile(source.file, fileText.toCharArray)
+ import compiler._
+ def findSource(name: String) = sourceFiles.find(_.file.name == name).get
+
+ val className = names.head
+ for (name <- names;
+ i <- 1 to tags.length) {
+ val newText = text(name, i)
+ val source = findSource("Class.scala")
+ val batch = new BatchSourceFile(source.file, newText.toCharArray)
val reloadResponse = new Response[Unit]
compiler.askReload(List(batch), reloadResponse)
reloadResponse.get.left.toOption match {
case None =>
- reporter.println("Couldn't reload")
+ println("Couldn't reload")
case Some(_) =>
- val treeResponse = new compiler.Response[compiler.Tree]
- val pos = compiler.rangePos(batch, idx, idx, idx)
- compiler.askTypeAt(pos, treeResponse)
- treeResponse.get.left.toOption match {
- case Some(tree) =>
- val sym = tree.tpe.typeSymbol
- compiler.getComment(sym, batch) match {
- case None => println("Got no doc comment")
+ val parseResponse = new Response[Tree]
+ askParsedEntered(batch, true, parseResponse)
+ parseResponse.get.left.toOption match {
+ case None =>
+ println("Couldn't parse")
+ case Some(_) =>
+ val sym = compiler.ask { () =>
+ val toplevel = definitions.EmptyPackage.info.decl(newTypeName(name))
+ if (toplevel eq NoSymbol) {
+ val clazz = definitions.EmptyPackage.info.decl(newTypeName(className))
+
+ val term = clazz.info.decl(newTermName(name))
+ if (term eq NoSymbol) clazz.info.decl(newTypeName(name)) else
+ if (term.isAccessor) term.accessed else term
+ } else toplevel
+ }
+
+ getComment(sym, batch, (sym,batch)::Nil) match {
+ case None => println(s"Got no doc comment for $name")
case Some(comment) =>
import comment._
- val tags: List[(String, Iterable[Body])] =
- List(("@example", example), ("@version", version), ("@since", since.toList), ("@todo", todo), ("@note", note), ("@see", see))
- val str = ("body:" + body + "\n") +
- tags.map{ case (name, bodies) => name + ":" + bodies.mkString("\n") }.mkString("\n")
- println(str)
+ def cnt(bodies: Iterable[Body]) = bodies.size
+ val actual = cnt(example) + cnt(version) + cnt(since) + cnt(todo) + cnt(note) + cnt(see)
+ if (actual != i)
+ println(s"Got docComment with $actual tags instead of $i, file text:\n$newText")
}
- case None => println("Couldn't find a typedTree")
}
}
}
+
+ // Check inter-classes documentation one-time retrieved ok.
+ val baseSource = findSource("Base.scala")
+ val derivedSource = findSource("Derived.scala")
+ def existsText(where: Any, text: String): Boolean = where match {
+ case `text` => true
+ case s: Seq[_] => s exists (existsText(_, text))
+ case p: Product => p.productIterator exists (existsText(_, text))
+ }
+ val (derived, base) = compiler.ask { () =>
+ val derived = definitions.RootPackage.info.decl(newTermName("p")).info.decl(newTypeName("Derived"))
+ (derived, derived.ancestors(0))
+ }
+ val cmt1 = getComment(derived, derivedSource, (base, baseSource)::(derived, derivedSource)::Nil)
+ if (!existsText(cmt1, "Derived comment."))
+ println("Unexpected Derived class comment:"+cmt1)
+
+ val (fooDerived, fooBase) = compiler.ask { () =>
+ val decl = derived.tpe.decl(newTermName("foo"))
+ (decl, decl.allOverriddenSymbols(0))
+ }
+
+ val cmt2 = getComment(fooDerived, derivedSource, (fooBase, baseSource)::(fooDerived, derivedSource)::Nil)
+ if (!existsText(cmt2, "Base method has documentation."))
+ println("Unexpected foo method comment:"+cmt2)
}
}
diff --git a/test/files/presentation/doc/src/Class.scala b/test/files/presentation/doc/src/Class.scala
new file mode 100755
index 0000000000..a974bd6f5c
--- /dev/null
+++ b/test/files/presentation/doc/src/Class.scala
@@ -0,0 +1 @@
+object Class \ No newline at end of file
diff --git a/test/files/presentation/doc/src/Test.scala b/test/files/presentation/doc/src/Test.scala
deleted file mode 100755
index fcc1554994..0000000000
--- a/test/files/presentation/doc/src/Test.scala
+++ /dev/null
@@ -1 +0,0 @@
-object Test \ No newline at end of file
diff --git a/test/files/presentation/doc/src/p/Base.scala b/test/files/presentation/doc/src/p/Base.scala
new file mode 100755
index 0000000000..9031de3e3e
--- /dev/null
+++ b/test/files/presentation/doc/src/p/Base.scala
@@ -0,0 +1,11 @@
+package p
+
+/**
+ * @define BaseComment $BaseVar comment.
+ */
+trait Base {
+ /**
+ * Base method has documentation.
+ */
+ def foo: String
+}
diff --git a/test/files/presentation/doc/src/p/Derived.scala b/test/files/presentation/doc/src/p/Derived.scala
new file mode 100755
index 0000000000..1a9c9a26d1
--- /dev/null
+++ b/test/files/presentation/doc/src/p/Derived.scala
@@ -0,0 +1,9 @@
+package p
+
+/**
+ * $BaseComment
+ * @define BaseVar Derived
+ */
+class Derived extends Base {
+ def foo = ""
+}
diff --git a/test/files/run/t7185.check b/test/files/run/t7185.check
new file mode 100644
index 0000000000..f95910ba5f
--- /dev/null
+++ b/test/files/run/t7185.check
@@ -0,0 +1,34 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> import scala.tools.reflect.ToolBox
+import scala.tools.reflect.ToolBox
+
+scala> import scala.reflect.runtime.universe._
+import scala.reflect.runtime.universe._
+
+scala> object O { def apply() = 0 }
+defined object O
+
+scala> val ORef = reify { O }.tree
+ORef: reflect.runtime.universe.Tree = $read.O
+
+scala> val tree = Apply(Block(Nil, Block(Nil, ORef)), Nil)
+tree: reflect.runtime.universe.Apply =
+{
+ {
+ $read.O
+ }
+}()
+
+scala> {val tb = reflect.runtime.currentMirror.mkToolBox(); tb.typeCheck(tree): Any}
+res0: Any =
+{
+ {
+ $read.O.apply()
+ }
+}
+
+scala>
diff --git a/test/files/run/t7185.scala b/test/files/run/t7185.scala
new file mode 100644
index 0000000000..d9d913e78a
--- /dev/null
+++ b/test/files/run/t7185.scala
@@ -0,0 +1,12 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ override def code = """
+import scala.tools.reflect.ToolBox
+import scala.reflect.runtime.universe._
+object O { def apply() = 0 }
+val ORef = reify { O }.tree
+val tree = Apply(Block(Nil, Block(Nil, ORef)), Nil)
+{val tb = reflect.runtime.currentMirror.mkToolBox(); tb.typeCheck(tree): Any}
+"""
+}
diff --git a/test/files/run/t7214.scala b/test/files/run/t7214.scala
new file mode 100644
index 0000000000..ff1ea8082d
--- /dev/null
+++ b/test/files/run/t7214.scala
@@ -0,0 +1,57 @@
+// pattern matcher crashes here trying to synthesize an uneeded outer test.
+// no-symbol does not have an owner
+// at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)
+// at scala.tools.nsc.Global.abort(Global.scala:253)
+// at scala.reflect.internal.Symbols$NoSymbol.owner(Symbols.scala:3248)
+// at scala.reflect.internal.Symbols$Symbol.effectiveOwner(Symbols.scala:678)
+// at scala.reflect.internal.Symbols$Symbol.isDefinedInPackage(Symbols.scala:664)
+// at scala.reflect.internal.TreeGen.mkAttributedSelect(TreeGen.scala:188)
+// at scala.reflect.internal.TreeGen.mkAttributedRef(TreeGen.scala:124)
+// at scala.tools.nsc.ast.TreeDSL$CODE$.REF(TreeDSL.scala:308)
+// at scala.tools.nsc.typechecker.PatternMatching$TreeMakers$TypeTestTreeMaker$treeCondStrategy$.outerTest(PatternMatching.scala:1209)
+class Crash {
+ type Alias = C#T
+
+ val c = new C
+ val t = new c.T
+
+ // Crash via a Typed Pattern...
+ (t: Any) match {
+ case e: Alias =>
+ }
+
+ // ... or via a Typed Extractor Pattern.
+ object Extractor {
+ def unapply(a: Alias): Option[Any] = None
+ }
+ (t: Any) match {
+ case Extractor() =>
+ case _ =>
+ }
+
+ // checking that correct outer tests are applied when
+ // aliases for path dependent types are involved.
+ val c2 = new C
+ type CdotT = c.T
+ type C2dotT = c2.T
+
+ val outerField = t.getClass.getDeclaredFields.find(_.getName contains ("outer")).get
+ outerField.setAccessible(true)
+
+ (t: Any) match {
+ case _: C2dotT =>
+ println(s"!!! wrong match. t.outer=${outerField.get(t)} / c2 = $c2") // this matches on 2.10.0
+ case _: CdotT =>
+ case _ =>
+ println(s"!!! wrong match. t.outer=${outerField.get(t)} / c = $c")
+ }
+}
+
+class C {
+ class T
+}
+
+object Test extends App {
+ new Crash
+}
+
diff --git a/test/files/run/t7215.scala b/test/files/run/t7215.scala
new file mode 100644
index 0000000000..c93e97f9c8
--- /dev/null
+++ b/test/files/run/t7215.scala
@@ -0,0 +1,6 @@
+object Test extends App {
+ List[List[Any]]().transpose.isEmpty
+ Array[Array[Any]]().transpose.isEmpty
+ Vector[Vector[Any]]().transpose.isEmpty
+ Stream[Stream[Any]]().transpose.isEmpty
+}