diff options
67 files changed, 1126 insertions, 560 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index f559d673f0..4794666721 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -154,6 +154,10 @@ filter { { matchName="scala.reflect.internal.util.Statistics#RelCounter.scala$reflect$internal$util$Statistics$RelCounter$$super$prefix" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Names#NameOps.name" + problemName=MissingFieldProblem } ] } @@ -22,7 +22,7 @@ END-USER TARGETS <target name="clean" depends="quick.clean" description="Removes binaries of compiler and library. Distributions are untouched."/> - <target name="test" depends="test.done, osgi.test, bc.run" + <target name="test" depends="test.done" description="Runs test suite and bootstrapping test on Scala compiler and library."/> <target name="test-opt" @@ -2643,7 +2643,7 @@ BOOTRAPING TEST AND TEST SUITE </partest> </target> - <target name="test.done" depends="test.suite, test.continuations.suite, test.scaladoc, test.stability, test.sbt"/> + <target name="test.done" depends="test.suite, test.continuations.suite, test.scaladoc, test.stability, test.sbt, osgi.test, bc.run"/> <!-- =========================================================================== @@ -2656,7 +2656,7 @@ Binary compatibility testing <mkdir dir="${bc-build.dir}"/> <!-- Pull down MIMA --> <artifact:dependencies pathId="mima.classpath"> - <dependency groupId="com.typesafe" artifactId="mima-reporter_2.9.2" version="0.1.5-SNAPSHOT"/> + <dependency groupId="com.typesafe" artifactId="mima-reporter_2.9.2" version="0.1.5"/> </artifact:dependencies> <artifact:dependencies pathId="old.bc.classpath"> <dependency groupId="org.scala-lang" artifactId="scala-swing" version="2.10.0"/> @@ -2665,7 +2665,7 @@ Binary compatibility testing </artifact:dependencies> </target> - <target name="bc.run" depends="bc.init, pack.lib"> + <target name="bc.run" depends="bc.init, pack.done"> <java fork="true" failonerror="true" diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 7c66d5b9eb..2e57bc59a8 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -27,6 +27,11 @@ trait Errors { throw new ReificationException(defaultErrorPosition, msg) } + def CannotReifyCompoundTypeTreeWithNonEmptyBody(ctt: CompoundTypeTree) = { + val msg = "implementation restriction: cannot reify refinement type trees with non-empty bodies" + throw new ReificationException(ctt.pos, msg) + } + def CannotReifyWeakType(details: Any) = { val msg = "cannot create a TypeTag" + details + ": use WeakTypeTag instead" throw new ReificationException(defaultErrorPosition, msg) diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index 7406f5d02d..a320718084 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -181,6 +181,7 @@ trait Reshape { private def toPreTyperCompoundTypeTree(ctt: CompoundTypeTree): Tree = { val CompoundTypeTree(tmpl @ Template(parents, self, stats)) = ctt + if (stats.nonEmpty) CannotReifyCompoundTypeTreeWithNonEmptyBody(ctt) assert(self eq emptyValDef, self) val att = tmpl.attachments.get[CompoundTypeTreeOriginalAttachment] val CompoundTypeTreeOriginalAttachment(parents1, stats1) = att.getOrElse(CompoundTypeTreeOriginalAttachment(parents, stats)) diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index e635c5e87d..6e39fc9aa1 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -56,6 +56,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/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index 73738ebd21..af82957a2e 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -164,17 +164,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. @@ -390,9 +395,9 @@ trait CompilerControl { self: Global => response raise new MissingResponse } - case class AskDocCommentItem(val sym: Symbol, val site: Symbol, val 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(val sym: Symbol, val source: SourceFile, val site: Symbol, val 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 4c2c3e35f8..1f2245abb5 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -704,8 +704,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` */ @@ -713,7 +713,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) @@ -763,14 +763,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. */ @@ -834,50 +843,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 84cb03c140..2b389158c3 100644 --- a/src/compiler/scala/tools/nsc/interactive/Picklers.scala +++ b/src/compiler/scala/tools/nsc/interactive/Picklers.scala @@ -166,8 +166,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/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala index c4a672ac37..827ebe1678 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala @@ -57,15 +57,13 @@ trait ExprTyper { // Typing it with a lazy val would give us the right type, but runs // into compiler bugs with things like existentials, so we compile it // behind a def and strip the NullaryMethodType which wraps the expr. - val line = "def " + name + " = {\n" + code + "\n}" + val line = "def " + name + " = " + code interpretSynthetic(line) match { case IR.Success => val sym0 = symbolOfTerm(name) // drop NullaryMethodType - val sym = sym0.cloneSymbol setInfo afterTyper(sym0.info.finalResultType) - if (sym.info.typeSymbol eq UnitClass) NoSymbol - else sym + sym0.cloneSymbol setInfo afterTyper(sym0.info.finalResultType) case _ => NoSymbol } } @@ -82,7 +80,11 @@ trait ExprTyper { case _ => NoSymbol } } - beQuietDuring(asExpr()) orElse beQuietDuring(asDefn()) + def asError(): Symbol = { + interpretSynthetic(code) + NoSymbol + } + beSilentDuring(asExpr()) orElse beSilentDuring(asDefn()) orElse asError() } private var typeOfExpressionDepth = 0 diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 889d309ba9..ead6ef288c 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -477,6 +477,7 @@ abstract class Erasure extends AddInterfaces def checkPair(member: Symbol, other: Symbol) { val otpe = specialErasure(root)(other.tpe) val bridgeNeeded = afterErasure ( + !member.isMacro && !(other.tpe =:= member.tpe) && !(deconstMap(other.tpe) =:= deconstMap(member.tpe)) && { var e = bridgesScope.lookupEntry(member.name) @@ -780,7 +781,7 @@ abstract class Erasure extends AddInterfaces else if (tree.symbol == Any_isInstanceOf) adaptMember(atPos(tree.pos)(Select(qual, Object_isInstanceOf))) else if (tree.symbol.owner == AnyClass) - adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name)))) + adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, tree.symbol.name)))) else { var qual1 = typedQualifier(qual) if ((isPrimitiveValueType(qual1.tpe) && !isPrimitiveValueMember(tree.symbol)) || diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 9af4800a70..22eabb6d6f 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, 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, true)) ++ force(Lit(s, 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 ed990105fd..d9f93f27b6 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 ce19d9cba8..57fab4eafa 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 5d11fb6459..90c52e3eb6 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 07eed2cd94..df4e699620 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._ @@ -78,15 +79,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 @@ -94,7 +100,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..843f831ea1 --- /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, 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, true)) ++ force(Lit(s, 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/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index bbba7e0435..57b9dfe3e4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -101,6 +101,7 @@ trait EtaExpansion { self: Analyzer => case TypeApply(fn, args) => treeCopy.TypeApply(tree, liftoutPrefix(fn), args) setType null case Select(qual, name) => + val name = tree.symbol.name // account for renamed imports, SI-7233 treeCopy.Select(tree, liftout(qual, false), name) setSymbol NoSymbol setType null case Ident(name) => tree diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 2340c78f8c..1c60f0a79d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -273,22 +273,25 @@ trait NamesDefaults { self: Analyzer => */ def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[Option[ValDef]] = { val context = blockTyper.context - val symPs = map2(args, paramTypes)((arg, tpe) => arg match { + val symPs = map2(args, paramTypes)((arg, paramTpe) => arg match { case Ident(nme.SELECTOR_DUMMY) => None // don't create a local ValDef if the argument is <unapply-selector> case _ => - val byName = isByNameParamType(tpe) - val repeated = isScalaRepeatedParamType(tpe) + val byName = isByNameParamType(paramTpe) + val repeated = isScalaRepeatedParamType(paramTpe) val argTpe = ( if (repeated) arg match { case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => expr.tpe case _ => seqType(arg.tpe) } - else arg.tpe - ).widen // have to widen or types inferred from literal defaults will be singletons + else + // Note stabilizing can lead to a non-conformant argument when existentials are involved, e.g. neg/t3507-old.scala, hence the filter. + // We have to deconst or types inferred from literal arguments will be Constant(_), e.g. pos/z1730.scala. + gen.stableTypeFor(arg).filter(_ <:< paramTpe).getOrElse(arg.tpe).deconst + ) val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo ( - if (byName) functionType(Nil, argTpe) else argTpe - ) + if (byName) functionType(Nil, argTpe) else argTpe + ) Some((context.scope.enter(s), byName, repeated)) }) map2(symPs, args) { @@ -329,11 +332,10 @@ trait NamesDefaults { self: Analyzer => // type the application without names; put the arguments in definition-site order val typedApp = doTypedApply(tree, funOnly, reorderArgs(namelessArgs, argPos), mode, pt) - if (typedApp.isErrorTyped) tree - else typedApp match { + typedApp match { // Extract the typed arguments, restore the call-site evaluation order (using // ValDef's in the block), change the arguments to these local values. - case Apply(expr, typedArgs) => + case Apply(expr, typedArgs) if !(typedApp :: typedArgs).exists(_.isErrorTyped) => // bail out with erroneous args, see SI-7238 // typedArgs: definition-site order val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, removeByName = false, removeRepeated = false) // valDefs: call-site order @@ -361,6 +363,7 @@ trait NamesDefaults { self: Analyzer => context.namedApplyBlockInfo = Some((block, NamedApplyInfo(qual, targs, vargss :+ refArgs, blockTyper))) block + case _ => tree } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9dde952d25..d8493d2312 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -222,8 +222,13 @@ trait Typers extends Modes with 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 } @@ -1571,7 +1576,7 @@ trait Typers extends Modes with Adaptations with Tags { if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) ConstrArgsInTraitParentTpeError(sarg, firstParent) if (!supertparams.isEmpty) - supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus + supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos case _ => if (!supertparams.isEmpty) MissingTypeArgumentsParentTpeError(supertpt) @@ -1818,7 +1823,9 @@ trait Typers extends Modes with Adaptations with Tags { def pkgObjectWarning(m : Symbol, mdef : ModuleDef, restricted : String) = { val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString - context.warning(if (m.pos.isDefined) m.pos else mdef.pos, s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") + val pos = if (m.pos.isDefined) m.pos else mdef.pos + debugwarn(s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") + debugwarn(pos.lineContent + (if (pos.isDefined) " " * (pos.column - 1) + "^" else "")) } } @@ -5285,8 +5292,7 @@ trait Typers extends Modes with Adaptations with Tags { def typedDocDef(docdef: DocDef) = { if (forScaladoc && (sym ne null) && (sym ne NoSymbol)) { val comment = docdef.comment - docComments(sym) = comment - comment.defineVariables(sym) + fillDocComment(sym, comment) val typer1 = newTyper(context.makeNewScope(tree, context.owner)) for (useCase <- comment.useCases) { typer1.silent(_.typedUseCase(useCase)) match { diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala index 86cd845c54..ae13cc561f 100644 --- a/src/compiler/scala/tools/reflect/MacroImplementations.scala +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -118,7 +118,8 @@ abstract class MacroImplementations { if (!strIsEmpty) { val len = str.length while (idx < len) { - if (str(idx) == '%') { + def notPercentN = str(idx) != '%' || (idx + 1 < len && str(idx + 1) != 'n') + if (str(idx) == '%' && notPercentN) { bldr append (str substring (start, idx)) append "%%" start = idx + 1 } diff --git a/src/library/scala/collection/mutable/ArrayOps.scala b/src/library/scala/collection/mutable/ArrayOps.scala index bb938a7aeb..25ba7e4ce6 100644 --- a/src/library/scala/collection/mutable/ArrayOps.scala +++ b/src/library/scala/collection/mutable/ArrayOps.scala @@ -76,18 +76,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 3040486076..3e74b5d22d 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -98,7 +98,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 546df8a207..a27b37dae5 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3052,6 +3052,12 @@ trait Types extends api.Types { self: SymbolTable => val origin: Type, var constr: TypeConstraint ) extends Type { + + // We don't want case class equality/hashing as TypeVar-s are mutable, + // and TypeRefs based on them get wrongly `uniqued` otherwise. See SI-7226. + override def hashCode(): Int = System.identityHashCode(this) + override def equals(other: Any): Boolean = this eq other.asInstanceOf[AnyRef] + def untouchable = false // by other typevars override def params: List[Symbol] = Nil override def typeArgs: List[Type] = Nil @@ -3183,12 +3189,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 * {{{ @@ -5358,7 +5371,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/t5954.check b/test/files/neg/t5954.check deleted file mode 100644 index ed10658b24..0000000000 --- a/test/files/neg/t5954.check +++ /dev/null @@ -1,16 +0,0 @@ -t5954.scala:36: error: class D should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - case class D() - ^ -t5954.scala:35: error: object C should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - object C - ^ -t5954.scala:34: error: trait C should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - trait C - ^ -t5954.scala:33: error: object B should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - object B - ^ -t5954.scala:32: error: class B should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. - class B - ^ -5 errors found diff --git a/test/files/neg/t5954.flags b/test/files/neg/t5954.flags deleted file mode 100644 index 85d8eb2ba2..0000000000 --- a/test/files/neg/t5954.flags +++ /dev/null @@ -1 +0,0 @@ --Xfatal-warnings diff --git a/test/files/neg/t5954.scala b/test/files/neg/t5954.scala deleted file mode 100644 index 3ccb5ed3ff..0000000000 --- a/test/files/neg/t5954.scala +++ /dev/null @@ -1,46 +0,0 @@ -// if you ever think you've fixed the underlying reason for the warning -// imposed by SI-5954, then here's a test that should pass with two "succes"es -// -//import scala.tools.partest._ -// -//object Test extends DirectTest { -// def code = ??? -// -// def problemCode = """ -// package object A { -// class B -// object B -// case class C() -// } -// """ -// -// def compileProblemCode() = { -// val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") -// compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(problemCode) -// } -// -// def show() : Unit = { -// for (i <- 0 until 2) { -// compileProblemCode() -// println(s"success ${i + 1}") -// } -// } -//} - -package object A { - // these should be prevented by the implementation restriction - class B - object B - trait C - object C - case class D() - // all the rest of these should be ok - class E - object F - val g = "omg" - var h = "wtf" - def i = "lol" - type j = String - class K(val k : Int) extends AnyVal - implicit class L(val l : Int) -} 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/neg/t7235.check b/test/files/neg/t7235.check new file mode 100644 index 0000000000..357a3dfd83 --- /dev/null +++ b/test/files/neg/t7235.check @@ -0,0 +1,4 @@ +t7235.scala:9: error: implementation restriction: cannot reify refinement type trees with non-empty bodies + val Block(List(ValDef(_, _, tpt: CompoundTypeTree, _)), _) = reify{ val x: C { def x: Int } = ??? }.tree + ^ +one error found diff --git a/test/files/neg/t7235.scala b/test/files/neg/t7235.scala new file mode 100644 index 0000000000..cfebad3fae --- /dev/null +++ b/test/files/neg/t7235.scala @@ -0,0 +1,14 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{universe => ru} +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox + +class C + +object Test extends App { + val Block(List(ValDef(_, _, tpt: CompoundTypeTree, _)), _) = reify{ val x: C { def x: Int } = ??? }.tree + println(tpt) + println(tpt.templ.parents) + println(tpt.templ.self) + println(tpt.templ.body) +} diff --git a/test/files/neg/t7238.check b/test/files/neg/t7238.check new file mode 100644 index 0000000000..b87f83ff65 --- /dev/null +++ b/test/files/neg/t7238.check @@ -0,0 +1,6 @@ +t7238.scala:6: error: type mismatch; + found : Seq[Any] + required: Seq[String] + c.c()(Seq[Any](): _*) + ^ +one error found diff --git a/test/files/neg/t7238.scala b/test/files/neg/t7238.scala new file mode 100644 index 0000000000..d42dc8d385 --- /dev/null +++ b/test/files/neg/t7238.scala @@ -0,0 +1,7 @@ +trait Main { + trait C { + def c(x: Any = 0)(bs: String*) + } + def c: C + c.c()(Seq[Any](): _*) +} 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/pos/t7226.scala b/test/files/pos/t7226.scala new file mode 100644 index 0000000000..06f0c95dc4 --- /dev/null +++ b/test/files/pos/t7226.scala @@ -0,0 +1,26 @@ +trait HK { + type Rep[X] + + // okay + def unzip2[A, B](ps: Rep[List[(A, B)]]) + unzip2(null.asInstanceOf[Rep[List[(Int, String)]]]) + + // okay + def unzipHK[A, B, C[_]](ps: Rep[C[(A, B)]]) + unzipHK(null.asInstanceOf[Rep[List[(Int, String)]]]) + + def unzipHKRet0[A, C[_]](ps: C[A]): C[Int] + def ls: List[String] + unzipHKRet0(ls) + + // fail + def unzipHKRet[A, C[_]](ps: Rep[C[A]]): Rep[C[Int]] + def rls: Rep[List[String]] + unzipHKRet(rls) +} + +trait HK1 { + type Rep[A] + def unzip1[A, B, C[_]](ps: Rep[C[(A, B)]]): (Rep[C[A]], Rep[C[B]]) + def doUnzip1[A, B](ps: Rep[List[(A, B)]]) = unzip1(ps) +} diff --git a/test/files/pos/t7233.scala b/test/files/pos/t7233.scala new file mode 100644 index 0000000000..ae15c08c35 --- /dev/null +++ b/test/files/pos/t7233.scala @@ -0,0 +1,14 @@ +object Foo { + def bar(i: Int) = i + + def ol(i: Int) = i + def ol(i: String) = i +} +object Test { + import Foo.{ bar => quux, toString => bar, ol => olRenamed} + + val f1 = quux _ + val f1Typed: (Int => Int) = f1 + + val f2: String => String = olRenamed _ +} diff --git a/test/files/pos/t7233b.scala b/test/files/pos/t7233b.scala new file mode 100644 index 0000000000..927c7fcfd1 --- /dev/null +++ b/test/files/pos/t7233b.scala @@ -0,0 +1,8 @@ +object Test { + // crash + def foo(a: Any) = { import a.{toString => toS}; toS } + + // okay + def ok1(a: String) = { import a.{isInstanceOf => iio}; iio[String] } + def ok2(a: Int) = { import a.{toInt => ti}; ti } +} diff --git a/test/files/pos/t7234.scala b/test/files/pos/t7234.scala new file mode 100644 index 0000000000..59a233d835 --- /dev/null +++ b/test/files/pos/t7234.scala @@ -0,0 +1,15 @@ +trait Main { + trait A { + type B + } + trait C { + def c(a: A, x: Int = 0)(b: a.B) + } + def c: C + def d(a: A, x: Int = 0)(b: a.B) + + def ok1(a: A)(b: a.B) = c.c(a, 42)(b) + def ok2(a: A)(b: a.B) = d(a)(b) + + def fail(a: A)(b: a.B) = c.c(a)(b) +} diff --git a/test/files/pos/t7234b.scala b/test/files/pos/t7234b.scala new file mode 100644 index 0000000000..fee98e87a8 --- /dev/null +++ b/test/files/pos/t7234b.scala @@ -0,0 +1,20 @@ +trait Main { + trait A { + type B + def b: B + } + trait C { + def c(a: A, x: Int = 0)(b: => a.B, bs: a.B*) + def d(a: A = null, x: Int = 0)(b1: => a.B = a.b, b2: a.B = a.b) + } + def c: C + def ok(a: A)(b: a.B) = c.c(a, 42)(b) + def fail(a: A)(b: a.B) = c.c(a)(b) + def fail2(a: A)(b: a.B) = c.c(a)(b, b) + def fail3(a: A)(b: a.B) = c.c(a)(b, Seq[a.B](b): _*) + + def fail4(a: A)(b: a.B) = c.d(a)() + def fail5(a: A)(b: a.B) = c.d(a)(b1 = a.b) + def fail6(a: A)(b: a.B) = c.d(a)(b2 = a.b) + def fail7(a: A)(b: a.B) = c.d()() +} 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 5855f488b8..371b825026 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) @@ -38,9 +51,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) @@ -53,37 +66,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/presentation/hyperlinks.check b/test/files/presentation/hyperlinks.check index 85d295dd7d..1051b67e85 100644 --- a/test/files/presentation/hyperlinks.check +++ b/test/files/presentation/hyperlinks.check @@ -1,4 +1,4 @@ -reload: NameDefaultTests.scala, PatMatTests.scala +reload: NameDefaultTests.scala, PatMatTests.scala, SuperTypes.scala askHyperlinkPos for `someOtherInt` at (14,24) NameDefaultTests.scala ================================================================================ @@ -44,3 +44,138 @@ askHyperlinkPos for `y` at (25,21) PatMatTests.scala ================================================================================ [response] found askHyperlinkPos for `y` at (23,13) PatMatTests.scala ================================================================================ + +askHyperlinkPos for `BadPos` at (10,26) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `BadPos` at (2,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `BadPos` at (11,26) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `BadPos` at (2,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `Trait` at (12,25) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `Trait` at (6,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `SubTrait` at (13,28) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `SubTrait` at (7,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `Trait` at (14,25) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `Trait` at (6,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `LateralTrait` at (14,48) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `LateralTrait` at (8,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `Base` at (15,24) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `Base` at (4,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `Trait` at (15,40) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `Trait` at (6,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `LateralTrait` at (15,63) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `LateralTrait` at (8,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PBase` at (19,29) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PBase` at (17,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PTrait` at (20,33) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PTrait` at (19,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PBase` at (21,36) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PBase` at (17,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PTrait` at (23,27) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PTrait` at (19,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PSubTrait` at (24,30) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PSubTrait` at (20,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PTrait` at (25,27) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PTrait` at (19,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PLateralTrait` at (25,56) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PLateralTrait` at (21,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PBase` at (26,26) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PBase` at (17,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PTrait` at (26,48) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PTrait` at (19,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PLateralTrait` at (26,77) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PLateralTrait` at (21,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `BadPos` at (28,23) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `BadPos` at (2,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PTrait` at (29,23) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PTrait` at (19,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PSubTrait` at (30,26) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PSubTrait` at (20,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PTrait` at (31,23) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PTrait` at (19,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PLateralTrait` at (31,52) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PLateralTrait` at (21,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PBase` at (32,22) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PBase` at (17,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PTrait` at (32,44) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PTrait` at (19,7) SuperTypes.scala +================================================================================ + +askHyperlinkPos for `PLateralTrait` at (32,73) SuperTypes.scala +================================================================================ +[response] found askHyperlinkPos for `PLateralTrait` at (21,7) SuperTypes.scala +================================================================================ diff --git a/test/files/presentation/hyperlinks/src/SuperTypes.scala b/test/files/presentation/hyperlinks/src/SuperTypes.scala new file mode 100644 index 0000000000..15d16069fd --- /dev/null +++ b/test/files/presentation/hyperlinks/src/SuperTypes.scala @@ -0,0 +1,32 @@ +/** This tests that hyperlinking works for super types. See SI-7224 */ +class BadPos[A](a: A) + +class Base + +trait Trait extends Base +trait SubTrait extends Trait +trait LateralTrait extends Base + +object obj1 extends BadPos/*#*/(new Object) +object obj2 extends BadPos/*#*/[AnyRef](new Object) +object obj3 extends Trait/*#*/ +object obj4 extends SubTrait/*#*/ +object obj5 extends Trait/*#*/ with LateralTrait/*#*/ +object obj6 extends Base/*#*/ with Trait/*#*/ with LateralTrait/*#*/ + +class PBase[A] + +trait PTrait[A] extends PBase/*#*/[A] +trait PSubTrait[A] extends PTrait/*#*/[A] +trait PLateralTrait[A] extends PBase/*#*/[A] + +object pobj2 extends PTrait/*#*/[Int] +object pobj3 extends PSubTrait/*#*/[Int] +object pobj4 extends PTrait/*#*/[Int] with PLateralTrait/*#*/[Int] +object pobj5 extends PBase/*#*/[Int] with PTrait/*#*/[Int] with PLateralTrait/*#*/[Int] + +class c1 extends BadPos/*#*/(new Object) +class c2 extends PTrait/*#*/[Int] +class c3 extends PSubTrait/*#*/[Int] +class c4 extends PTrait/*#*/[Int] with PLateralTrait/*#*/[Int] +class c5 extends PBase/*#*/[Int] with PTrait/*#*/[Int] with PLateralTrait/*#*/[Int] diff --git a/test/files/run/repl-colon-type.check b/test/files/run/repl-colon-type.check index 56ddd74375..0cb18e989a 100644 --- a/test/files/run/repl-colon-type.check +++ b/test/files/run/repl-colon-type.check @@ -4,12 +4,6 @@ Type :help for more information. scala> scala> :type List[1, 2, 3] -<console>:2: error: identifier expected but integer literal found. - List[1, 2, 3] - ^ -<console>:3: error: ']' expected but '}' found. - } - ^ <console>:1: error: identifier expected but integer literal found. List[1, 2, 3] ^ @@ -45,12 +39,9 @@ scala> :type lazy val f = 5 Int scala> :type protected lazy val f = 5 -<console>:2: error: illegal start of statement (no modifiers allowed here) - protected lazy val f = 5 - ^ <console>:5: error: lazy value f cannot be accessed in object $iw Access to protected value f not permitted because - enclosing object $eval in package $line19 is not a subclass of + enclosing object $eval in package $line13 is not a subclass of object $iw where target is defined lazy val $result = `f` ^ @@ -223,4 +214,14 @@ PolyType( scala> +scala> // SI-7132 - :type doesn't understand Unit + +scala> :type () +Unit + +scala> :type println("side effect!") +Unit + +scala> + scala> diff --git a/test/files/run/repl-colon-type.scala b/test/files/run/repl-colon-type.scala index c055b215c2..8cf81a6afe 100644 --- a/test/files/run/repl-colon-type.scala +++ b/test/files/run/repl-colon-type.scala @@ -26,6 +26,10 @@ object Test extends ReplTest { |:type -v Nil.combinations _ |:type -v def f[T <: AnyVal] = List[T]().combinations _ |:type -v def f[T, U >: T](x: T, y: List[U]) = x :: y + | + |// SI-7132 - :type doesn't understand Unit + |:type () + |:type println("side effect!") """.stripMargin } diff --git a/test/files/run/t5710-1.check b/test/files/run/t5710-1.check new file mode 100644 index 0000000000..eac2025aeb --- /dev/null +++ b/test/files/run/t5710-1.check @@ -0,0 +1 @@ +evaluated = (abc,abc) diff --git a/test/files/run/t5710-1.scala b/test/files/run/t5710-1.scala new file mode 100644 index 0000000000..12bd858e06 --- /dev/null +++ b/test/files/run/t5710-1.scala @@ -0,0 +1,15 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{universe => ru} +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox + +object Test extends App { + val code = reify { + val (x, y) = ("abc": Any) match { case x => (x, x) } + (x, y) + }; + + val toolbox = cm.mkToolBox() + val evaluated = toolbox.eval(code.tree) + println("evaluated = " + evaluated) +}
\ No newline at end of file diff --git a/test/files/run/t5710-2.check b/test/files/run/t5710-2.check new file mode 100644 index 0000000000..eac2025aeb --- /dev/null +++ b/test/files/run/t5710-2.check @@ -0,0 +1 @@ +evaluated = (abc,abc) diff --git a/test/files/run/t5710-2.scala b/test/files/run/t5710-2.scala new file mode 100644 index 0000000000..6d2129cca2 --- /dev/null +++ b/test/files/run/t5710-2.scala @@ -0,0 +1,15 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{universe => ru} +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox + +object Test extends App { + val code = reify { + val (x, y) = "abc" match { case x => (x, x) } + (x, y) + }; + + val toolbox = cm.mkToolBox() + val evaluated = toolbox.eval(code.tree) + println("evaluated = " + evaluated) +}
\ No newline at end of file diff --git a/test/files/run/t6725-1.check b/test/files/run/t6725-1.check new file mode 100644 index 0000000000..6ed281c757 --- /dev/null +++ b/test/files/run/t6725-1.check @@ -0,0 +1,2 @@ +1 +1 diff --git a/test/files/run/t6725-1.scala b/test/files/run/t6725-1.scala new file mode 100644 index 0000000000..a167ef8aa3 --- /dev/null +++ b/test/files/run/t6725-1.scala @@ -0,0 +1,5 @@ +object Test extends App { + val a = 1 + val s = f"$a%s%n$a%s" + println(s) +}
\ No newline at end of file diff --git a/test/files/run/t6725-2.check b/test/files/run/t6725-2.check new file mode 100644 index 0000000000..3496917ad5 --- /dev/null +++ b/test/files/run/t6725-2.check @@ -0,0 +1,8 @@ + + +aaaa + + +aaaa +aaaa +aaaa diff --git a/test/files/run/t6725-2.scala b/test/files/run/t6725-2.scala new file mode 100644 index 0000000000..e033cf5ea8 --- /dev/null +++ b/test/files/run/t6725-2.scala @@ -0,0 +1,6 @@ +object Test extends App { + println(f"%n") + println(f"aaaa%n") + println(f"%naaaa") + println(f"aaaa%naaaa") +}
\ No newline at end of file diff --git a/test/files/run/t7185.check b/test/files/run/t7185.check new file mode 100644 index 0000000000..455c1aa3b7 --- /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 module 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 +} diff --git a/test/files/run/t7235.check b/test/files/run/t7235.check new file mode 100644 index 0000000000..9cb9c55a0c --- /dev/null +++ b/test/files/run/t7235.check @@ -0,0 +1,4 @@ +C +List(C) +private val _ = _ +List() diff --git a/test/files/run/t7235.scala b/test/files/run/t7235.scala new file mode 100644 index 0000000000..6039189716 --- /dev/null +++ b/test/files/run/t7235.scala @@ -0,0 +1,14 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{universe => ru} +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox + +class C + +object Test extends App { + val Block(List(ValDef(_, _, tpt: CompoundTypeTree, _)), _) = reify{ val x: C{} = ??? }.tree + println(tpt) + println(tpt.templ.parents) + println(tpt.templ.self) + println(tpt.templ.body) +} |