diff options
Diffstat (limited to 'src')
13 files changed, 219 insertions, 118 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index d4ac21a6b8..4ac6672727 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -256,4 +256,44 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { val stats1 = if (stats.isEmpty) List(Literal(Constant(()))) else stats mkNew(Nil, noSelfType, stats1, NoPosition, NoPosition) } + + /** + * Create a method based on a Function + * + * Used both to under `-Ydelambdafy:method` create a lifted function and + * under `-Ydelamdafy:inline` to create the apply method on the anonymous + * class. + * + * It creates a method definition with value params cloned from the + * original lambda. Then it calls a supplied function to create + * the body and types the result. Finally + * everything is wrapped up in a DefDef + * + * @param owner The owner for the new method + * @param name name for the new method + * @param additionalFlags flags to be put on the method in addition to FINAL + */ + def mkMethodFromFunction(localTyper: analyzer.Typer) + (fun: Function, owner: Symbol, name: TermName, additionalFlags: FlagSet = NoFlags) = { + val funParams = fun.vparams map (_.symbol) + val formals :+ restpe = fun.tpe.typeArgs + + val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags) + + val paramSyms = map2(formals, fun.vparams) { + (tp, vparam) => methSym.newSyntheticValueParam(tp, vparam.name) + } + + methSym setInfo MethodType(paramSyms, restpe.deconst) + + fun.body.substituteSymbols(funParams, paramSyms) + fun.body changeOwner (fun.symbol -> methSym) + + val methDef = DefDef(methSym, fun.body) + + // Have to repack the type to avoid mismatches when existentials + // appear in the result - see SI-4869. + methDef.tpt setType localTyper.packedType(fun.body, methSym).deconst + methDef + } } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 3d648ccbac..844774e75f 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -217,112 +217,33 @@ abstract class UnCurry extends InfoTransform // nullary or parameterless case fun1 if fun1 ne fun => fun1 case _ => - val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe)) - val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation - anonClass setInfo ClassInfoType(parents, newScope, anonClass) - - val targs = fun.tpe.typeArgs - val (formals, restpe) = (targs.init, targs.last) + def typedFunPos(t: Tree) = localTyper.typedPos(fun.pos)(t) + val funParams = fun.vparams map (_.symbol) + def mkMethod(owner: Symbol, name: TermName, additionalFlags: FlagSet = NoFlags): DefDef = + gen.mkMethodFromFunction(localTyper)(fun, owner, name, additionalFlags) if (inlineFunctionExpansion) { - val applyMethodDef = { - val methSym = anonClass.newMethod(nme.apply, fun.pos, FINAL) - val paramSyms = map2(formals, fun.vparams) { - (tp, param) => methSym.newSyntheticValueParam(tp, param.name) - } - methSym setInfoAndEnter MethodType(paramSyms, restpe) - - fun.vparams foreach (_.symbol.owner = methSym) - fun.body changeOwner (fun.symbol -> methSym) - - val body = localTyper.typedPos(fun.pos)(fun.body) - val methDef = DefDef(methSym, List(fun.vparams), body) + val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe)) + val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation + anonClass setInfo ClassInfoType(parents, newScope, anonClass) - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - methDef.tpt setType localTyper.packedType(body, methSym) - methDef - } + val applyMethodDef = mkMethod(anonClass, nme.apply) + anonClass.info.decls enter applyMethodDef.symbol - localTyper.typedPos(fun.pos) { + typedFunPos { Block( - List(ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos)), + ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } } else { - /** - * Abstracts away the common functionality required to create both - * the lifted function and the apply method on the anonymous class - * It creates a method definition with value params cloned from the - * original lambda. Then it calls a supplied function to create - * the body and types the result. Finally - * everything is wrapped up in a MethodDef - * - * TODO it is intended that this common functionality be used - * whether inlineFunctionExpansion is true or not. However, it - * seems to introduce subtle ownwership changes that produce - * binary incompatible changes and so it is completely - * hidden behind the inlineFunctionExpansion for now. - * - * @param owner The owner for the new method - * @param name name for the new method - * @param additionalFlags flags to be put on the method in addition to FINAL - * @bodyF function that turns the method symbol and list of value params - * into a body for the method - */ - def createMethod(owner: Symbol, name: TermName, additionalFlags: Long)(bodyF: (Symbol, List[ValDef]) => Tree) = { - val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags) - val vparams = fun.vparams map (_.duplicate) - - val paramSyms = map2(formals, vparams) { - (tp, vparam) => methSym.newSyntheticValueParam(tp, vparam.name) - } - foreach2(vparams, paramSyms){(valdef, sym) => valdef.symbol = sym} - vparams foreach (_.symbol.owner = methSym) - - val methodType = MethodType(paramSyms, restpe.deconst) - methSym setInfo methodType - - // TODO this is probably cleaner if bodyF only works with symbols rather than parameter ValDefs - val tempBody = bodyF(methSym, vparams) - val body = localTyper.typedPos(fun.pos)(tempBody) - val methDef = DefDef(methSym, List(vparams), body) - - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - methDef.tpt setType localTyper.packedType(body, methSym).deconst - methDef - } - - val methodFlags = ARTIFACT // method definition with the same arguments, return type, and body as the original lambda - val liftedMethod = createMethod(fun.symbol.owner, tpnme.ANON_FUN_NAME.toTermName, methodFlags){ - case(methSym, vparams) => - fun.body.substituteSymbols(fun.vparams map (_.symbol), vparams map (_.symbol)) - fun.body changeOwner (fun.symbol -> methSym) - } - - // callsite for the lifted method - val args = fun.vparams map { vparam => - val ident = Ident(vparam.symbol) - // if -Yeta-expand-keeps-star is turned on then T* types can get through. In order - // to forward them we need to forward x: T* ascribed as "x:_*" - if (settings.etaExpandKeepsStar && definitions.isRepeatedParamType(vparam.tpt.tpe)) - gen.wildcardStar(ident) - else - ident - } - - val funTyper = localTyper.typedPos(fun.pos) _ - - val liftedMethodCall = funTyper(Apply(liftedMethod.symbol, args:_*)) + val liftedMethod = mkMethod(fun.symbol.owner, nme.ANON_FUN_NAME, additionalFlags = ARTIFACT) // new function whose body is just a call to the lifted method - val newFun = treeCopy.Function(fun, fun.vparams, liftedMethodCall) - funTyper(Block( - List(funTyper(liftedMethod)), - super.transform(newFun) - )) + val newFun = deriveFunction(fun)(_ => typedFunPos( + gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), funParams :: Nil) + )) + typedFunPos(Block(liftedMethod, super.transform(newFun))) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 0eae17612d..94f8f509fc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -259,10 +259,12 @@ trait Checkable { if (uncheckedOk(P0)) return def where = if (inPattern) "pattern " else "" - // singleton types not considered here - val P = P0.widen + // singleton types not considered here, dealias the pattern for SI-XXXX + val P = P0.dealiasWiden val X = X0.widen + def PString = if (P eq P0) P.toString else s"$P (the underlying of $P0)" + P match { // Prohibit top-level type tests for these, but they are ok nested (e.g. case Foldable[Nothing] => ... ) case TypeRef(_, NothingClass | NullClass | AnyValClass, _) => @@ -282,12 +284,12 @@ trait Checkable { if (checker.neverMatches) { val addendum = if (checker.neverSubClass) "" else " (but still might match its erasure)" - getContext.unit.warning(tree.pos, s"fruitless type test: a value of type $X cannot also be a $P$addendum") + getContext.unit.warning(tree.pos, s"fruitless type test: a value of type $X cannot also be a $PString$addendum") } else if (checker.isUncheckable) { val msg = ( - if (checker.uncheckableType =:= P) s"abstract type $where$P" - else s"${checker.uncheckableMessage} in type $where$P" + if (checker.uncheckableType =:= P) s"abstract type $where$PString" + else s"${checker.uncheckableMessage} in type $where$PString" ) getContext.unit.warning(tree.pos, s"$msg is unchecked since it is eliminated by erasure") } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 84531608e0..38065d5ea8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1762,6 +1762,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans | TypeDef(_, _, _, _) => if (result.symbol.isLocal || result.symbol.isTopLevel) varianceValidator.traverse(result) + case tt @ TypeTree() if tt.original != null => + varianceValidator.traverse(tt.original) // See SI-7872 case _ => } result diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8594309818..355e52ce2b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1698,15 +1698,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper psym addChild context.owner else pending += ParentSealedInheritanceError(parent, psym) + val parentTypeOfThis = parent.tpe.dealias.typeOfThis - if (!(selfType <:< parent.tpe.typeOfThis) && + if (!(selfType <:< parentTypeOfThis) && !phase.erasedTypes && !context.owner.isSynthetic && // don't check synthetic concrete classes for virtuals (part of DEVIRTUALIZE) !selfType.isErroneous && !parent.tpe.isErroneous) { pending += ParentSelfTypeConformanceError(parent, selfType) - if (settings.explaintypes) explainTypes(selfType, parent.tpe.typeOfThis) + if (settings.explaintypes) explainTypes(selfType, parentTypeOfThis) } if (parents exists (p => p != parent && p.tpe.typeSymbol == psym && !psym.isError)) diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index a933a5d189..8fdf4dc27a 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -199,7 +199,8 @@ abstract class TreeInfo { * don't reuse it for important matters like inlining * decisions. */ - def isPureExprForWarningPurposes(tree: Tree) = tree match { + def isPureExprForWarningPurposes(tree: Tree): Boolean = tree match { + case Typed(expr, _) => isPureExprForWarningPurposes(expr) case EmptyTree | Literal(Constant(())) => false case _ => def isWarnableRefTree = tree match { diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index af0af8afe8..d191fbd38f 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -590,7 +590,7 @@ trait Trees extends api.Trees { def TypeTree(tp: Type): TypeTree = TypeTree() setType tp private def TypeTreeMemberType(sym: Symbol): TypeTree = { // Needed for pos/t4970*.scala. See SI-7853 - val resType = (sym.owner.thisType memberType sym).finalResultType + val resType = (if (sym.isLocal) sym.tpe else (sym.owner.thisType memberType sym)).finalResultType atPos(sym.pos.focus)(TypeTree(resType)) } @@ -1804,6 +1804,12 @@ trait Trees extends api.Trees { case t => sys.error("Not a LabelDef: " + t + "/" + t.getClass) } + def deriveFunction(func: Tree)(applyToRhs: Tree => Tree): Function = func match { + case Function(params0, rhs0) => + treeCopy.Function(func, params0, applyToRhs(rhs0)) + case t => + sys.error("Not a Function: " + t + "/" + t.getClass) + } // -------------- Classtags -------------------------------------------------------- diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala index 5280467055..cd09e83cd3 100644 --- a/src/reflect/scala/reflect/internal/Variances.scala +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -162,6 +162,16 @@ trait Variances { traverseTreess(vparamss) case Template(_, _, _) => super.traverse(tree) + case CompoundTypeTree(templ) => + super.traverse(tree) + + // SI-7872 These two cases make sure we don't miss variance exploits + // in originals, e.g. in `foo[({type l[+a] = List[a]})#l]` + case tt @ TypeTree() if tt.original != null => + super.traverse(tt.original) + case tt : TypTree => + super.traverse(tt) + case _ => } } diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index 9a54ad8217..f5aa048e6a 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -717,6 +717,12 @@ private[internal] trait TypeMaps { else appliedType(tcon.typeConstructor, args) case SingleType(NoPrefix, sym) => substFor(sym) + case ClassInfoType(parents, decls, sym) => + val parents1 = parents mapConserve this + // We don't touch decls here; they will be touched when an enclosing TreeSubstitutor + // transforms the tree that defines them. + if (parents1 eq parents) tp + else ClassInfoType(parents1, decls, sym) case _ => tp } diff --git a/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala b/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala index cf03ecb480..d8efcda8b5 100644 --- a/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala +++ b/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala @@ -8,7 +8,9 @@ package interpreter import jline.console.{ ConsoleReader, CursorBuffer } -trait ConsoleReaderHelper extends ConsoleReader { +trait ConsoleReaderHelper { _: ConsoleReader with Tabulator => + def isAcross: Boolean + def terminal = getTerminal() def width = terminal.getWidth() def height = terminal.getHeight() @@ -16,7 +18,8 @@ trait ConsoleReaderHelper extends ConsoleReader { def readOneKey(prompt: String): Int def eraseLine(): Unit - private val marginSize = 3 + val marginSize = 3 + private def morePrompt = "--More--" private def emulateMore(): Int = { val key = readOneKey(morePrompt) @@ -38,19 +41,12 @@ trait ConsoleReaderHelper extends ConsoleReader { } override def printColumns(items: JCollection[_ <: CharSequence]): Unit = - printColumns(items: List[String]) - - def printColumns(items: List[String]): Unit = { - if (items forall (_ == "")) - return + printColumns_(items: List[String]) - val longest = items map (_.length) max + private def printColumns_(items: List[String]): Unit = if (items exists (_ != "")) { + val grouped = tabulate(items) var linesLeft = if (isPaginationEnabled()) height - 1 else Int.MaxValue - val columnSize = longest + marginSize - val padded = items map ("%-" + columnSize + "s" format _) - val groupSize = 1 max (width / columnSize) // make sure it doesn't divide to 0 - - padded grouped groupSize foreach { xs => + grouped foreach { xs => println(xs.mkString) linesLeft -= 1 if (linesLeft <= 0) { @@ -61,3 +57,104 @@ trait ConsoleReaderHelper extends ConsoleReader { } } } + +trait Tabulator { + def isAcross: Boolean + def width: Int + def marginSize: Int + + protected def fits(items: Seq[String], width: Int): Boolean = ( + (items map (_.length)).sum + (items.length - 1) * marginSize < width + ) + def tabulate(items: Seq[String]): Seq[Seq[String]] = ( + if (fits(items, width)) Seq(Seq(items mkString " " * marginSize)) + else printMultiLineColumns(items) + ) + protected def columnize(ss: Seq[String]): Seq[Seq[String]] = ss map (s => Seq(s)) + protected def printMultiLineColumns(items: Seq[String]): Seq[Seq[String]] = { + import SimpleMath._ + val longest = (items map (_.length)).max + val columnWidth = longest + marginSize + val maxcols = ( + if (columnWidth >= width) 1 + else 1 max (width / columnWidth) // make sure it doesn't divide to 0 + ) + val nrows = items.size /% maxcols + val ncols = items.size /% nrows + val groupSize = ncols + val padded = items map (s"%-${columnWidth}s" format _) + val xwise = isAcross || ncols >= items.length + val grouped: Seq[Seq[String]] = + if (groupSize == 1) columnize(items) + else if (xwise) (padded grouped groupSize).toSeq + else { + val h = 1 max padded.size /% groupSize + val cols = (padded grouped h).toList + for (i <- 0 until h) yield + for (j <- 0 until groupSize) yield + if (i < cols(j).size) cols(j)(i) else "" + } + grouped + } +} + +/** Adjust the column width and number of columns to minimize the row count. */ +trait VariColumnTabulator extends Tabulator { + override protected def printMultiLineColumns(items: Seq[String]): Seq[Seq[String]] = { + import SimpleMath._ + val longest = (items map (_.length)).max + val shortest = (items map (_.length)).min + val fattest = longest + marginSize + val skinny = shortest + marginSize + + // given ncols, calculate nrows and a list of column widths, or none if not possible + // if ncols > items.size, then columnWidths.size == items.size + def layout(ncols: Int): Option[(Int, Seq[Int], Seq[Seq[String]])] = { + val nrows = items.size /% ncols + val xwise = isAcross || ncols >= items.length + def maxima(sss: Seq[Seq[String]]) = + (0 until (ncols min items.size)) map (i => (sss map (ss => ss(i).length)).max) + def resulting(rows: Seq[Seq[String]]) = { + val columnWidths = maxima(rows) map (_ + marginSize) + val linelen = columnWidths.sum + if (linelen <= width) Some((nrows, columnWidths, rows)) + else None + } + if (ncols == 1) resulting(columnize(items)) + else if (xwise) resulting((items grouped ncols).toSeq) + else { + val cols = (items grouped nrows).toList + val rows = for (i <- 0 until nrows) yield + for (j <- 0 until ncols) yield + if (j < cols.size && i < cols(j).size) cols(j)(i) else "" + resulting(rows) + } + } + + if (fattest >= width) { + columnize(items) + } else { + // if every col is widest, we have at least this many cols + val mincols = 1 max (width / fattest) + // if every other col is skinniest, we have at most this many cols + val maxcols = 1 + ((width - fattest) / skinny) + val possibles = (mincols to maxcols).map(n => layout(n)).flatten + val minrows = (possibles map (_._1)).min + + // select the min ncols that results in minrows + val (_, columnWidths, sss) = (possibles find (_._1 == minrows)).get + + // format to column width + sss map (ss => ss.zipWithIndex map { + case (s, i) => s"%-${columnWidths(i)}s" format s + }) + } + } +} + +private[interpreter] object SimpleMath { + implicit class DivRem(private val i: Int) extends AnyVal { + /** i/n + if (i % n != 0) 1 else 0 */ + def /%(n: Int): Int = (i + n - 1) / n + } +} diff --git a/src/repl/scala/tools/nsc/interpreter/JLineReader.scala b/src/repl/scala/tools/nsc/interpreter/JLineReader.scala index 8b0c6d78fa..b6e834a1ed 100644 --- a/src/repl/scala/tools/nsc/interpreter/JLineReader.scala +++ b/src/repl/scala/tools/nsc/interpreter/JLineReader.scala @@ -33,7 +33,11 @@ class JLineReader(_completion: => Completion) extends InteractiveReader { } } - class JLineConsoleReader extends ConsoleReader with ConsoleReaderHelper { + class JLineConsoleReader extends ConsoleReader with ConsoleReaderHelper with VariColumnTabulator { + val isAcross = interpreter.`package`.isAcross + + this setPaginationEnabled interpreter.`package`.isPaged + // ASAP this setExpandEvents false diff --git a/src/repl/scala/tools/nsc/interpreter/ReplConfig.scala b/src/repl/scala/tools/nsc/interpreter/ReplConfig.scala index 3392ea0b5e..046d6ecbfb 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplConfig.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplConfig.scala @@ -46,4 +46,8 @@ trait ReplConfig { def isReplDebug: Boolean = replProps.debug || isReplTrace def isReplInfo: Boolean = replProps.info || isReplDebug def isReplPower: Boolean = replProps.power + + private def csv(p: String, v: String) = p split "," contains v + def isPaged: Boolean = replProps.format.isSet && csv(replProps.format.get, "paged") + def isAcross: Boolean = replProps.format.isSet && csv(replProps.format.get, "across") } diff --git a/src/repl/scala/tools/nsc/interpreter/ReplProps.scala b/src/repl/scala/tools/nsc/interpreter/ReplProps.scala index 2364918494..36e6dbbccc 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplProps.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplProps.scala @@ -18,6 +18,13 @@ class ReplProps { val trace = bool("scala.repl.trace") val power = bool("scala.repl.power") + /** CSV of paged,across to enable pagination or `-x` style + * columns, "across" instead of down the column. Since + * pagination turns off columnar output, these flags are + * currently mutually exclusive. + */ + val format = Prop[String]("scala.repl.format") + val replAutorunCode = Prop[JFile]("scala.repl.autoruncode") val powerInitCode = Prop[JFile]("scala.repl.power.initcode") val powerBanner = Prop[JFile]("scala.repl.power.banner") |