From 910a701fcc93e0663f0a6a15ac11499beb1ca6a9 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 12 Mar 2012 17:58:34 +0100 Subject: SI-5189: refined GADT soundness fix extrapolate GADT skolems: only complicate types when needed make sure we only deskolemize GADT skolems after typedCase --- src/compiler/scala/reflect/internal/Flags.scala | 2 +- src/compiler/scala/reflect/internal/Symbols.scala | 22 +++++++--- src/compiler/scala/reflect/internal/Types.scala | 7 ++-- .../scala/tools/nsc/typechecker/Infer.scala | 2 +- .../tools/nsc/typechecker/TypeDiagnostics.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 47 +++++++++++++--------- 6 files changed, 53 insertions(+), 29 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Flags.scala b/src/compiler/scala/reflect/internal/Flags.scala index 8aae80eed4..3110d73461 100644 --- a/src/compiler/scala/reflect/internal/Flags.scala +++ b/src/compiler/scala/reflect/internal/Flags.scala @@ -107,7 +107,7 @@ class ModifierFlags { // pre: PRIVATE or PROTECTED are also set final val JAVA = 0x00100000 // symbol was defined by a Java class final val STATIC = 0x00800000 // static field, method or class - final val CASEACCESSOR = 0x01000000 // symbol is a case parameter (or its accessor) + final val CASEACCESSOR = 0x01000000 // symbol is a case parameter (or its accessor, or a GADT skolem) final val TRAIT = 0x02000000 // symbol is a trait final val DEFAULTPARAM = 0x02000000 // the parameter has a default value final val PARAMACCESSOR = 0x20000000 // for field definitions generated for primary constructor diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 9678d2b8cd..7673aec4c7 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -269,11 +269,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Create a new existential type skolem with this symbol its owner, * based on the given symbol and origin. */ - def newExistentialSkolem(basis: Symbol, origin: AnyRef, name: TypeName = null, info: Type = null): TypeSkolem = { - val skolem = newTypeSkolemSymbol(if (name eq null) basis.name.toTypeName else name, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) - skolem setInfo (if (info eq null) basis.info cloneInfo skolem else info) + def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = { + val skolem = newTypeSkolemSymbol(basis.name.toTypeName, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) + skolem setInfo (basis.info cloneInfo skolem) } + // flags set up to maintain TypeSkolem's invariant: origin.isInstanceOf[Symbol] == !hasFlag(EXISTENTIAL) + // CASEACCESSOR | SYNTHETIC used to single this symbol out in deskolemizeGADT + def newGADTSkolem(name: TypeName, origin: Symbol, info: Type): TypeSkolem = + newTypeSkolemSymbol(name, origin, origin.pos, origin.flags & ~(EXISTENTIAL | PARAM) | CASEACCESSOR | SYNTHETIC) setInfo info + + final def newExistential(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol = newAbstractType(name, pos, EXISTENTIAL | newFlags) @@ -495,6 +501,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // List[T] forSome { type T } final def isExistentialSkolem = isExistentiallyBound && isSkolem final def isExistentialQuantified = isExistentiallyBound && !isSkolem + final def isGADTSkolem = isSkolem && hasFlag(CASEACCESSOR | SYNTHETIC) // class C extends D( { class E { ... } ... } ). Here, E is a class local to a constructor final def isClassLocalToConstructor = isClass && hasFlag(INCONSTRUCTOR) @@ -2129,12 +2136,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE else ExplicitFlags + // make the error message more googlable + def flagsExplanationString = + if (isGADTSkolem) " (this is a GADT skolem)" + else "" + def accessString = hasFlagsToString(PRIVATE | PROTECTED | LOCAL) def defaultFlagString = hasFlagsToString(defaultFlagMask) private def defStringCompose(infoString: String) = compose( defaultFlagString, keyString, - varianceString + nameString + infoString + varianceString + nameString + infoString + flagsExplanationString ) /** String representation of symbol's definition. It uses the * symbol's raw info to avoid forcing types. @@ -2477,7 +2489,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * where the skolem was introduced (this is important for knowing when to pack it * again into ab Existential). origin is `null` only in skolemizeExistentials called * from <:< or isAsSpecific, because here its value does not matter. - * I elieve the following invariant holds: + * I believe the following invariant holds: * * origin.isInstanceOf[Symbol] == !hasFlag(EXISTENTIAL) */ diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 2382413a9a..549c9e4607 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -1041,8 +1041,8 @@ trait Types extends api.Types { self: SymbolTable => baseClasses.head.newOverloaded(this, members.toList) } } - /** The existential skolems and existentially quantified variables which are free in this type */ - def existentialSkolems: List[Symbol] = { + /** The (existential or otherwise) skolems and existentially quantified variables which are free in this type */ + def skolemsExceptMethodTypeParams: List[Symbol] = { var boundSyms: List[Symbol] = List() var skolems: List[Symbol] = List() for (t <- this) { @@ -1050,7 +1050,8 @@ trait Types extends api.Types { self: SymbolTable => case ExistentialType(quantified, qtpe) => boundSyms = boundSyms ::: quantified case TypeRef(_, sym, _) => - if ((sym hasFlag EXISTENTIAL) && !(boundSyms contains sym) && !(skolems contains sym)) + if ((sym.isExistentialSkolem || sym.isGADTSkolem) && // treat GADT skolems like existential skolems + !((boundSyms contains sym) || (skolems contains sym))) skolems = sym :: skolems case _ => } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 8b3bc253fd..a59622d4df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1099,7 +1099,7 @@ trait Infer { // since instantiateTypeVar wants to modify the skolem that corresponds to the method's type parameter, // and it uses the TypeVar's origin to locate it, deskolemize the existential skolem to the method tparam skolem // (the existential skolem was created by adaptConstrPattern to introduce the type slack necessary to soundly deal with variant type parameters) - case skolem if skolem.isExistentialSkolem => freshVar(skolem.deSkolemize.asInstanceOf[TypeSymbol]) + case skolem if skolem.isGADTSkolem => freshVar(skolem.deSkolemize.asInstanceOf[TypeSymbol]) case p => freshVar(p) } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 1434002121..e17a271dd0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -157,7 +157,7 @@ trait TypeDiagnostics { } // todo: use also for other error messages - def existentialContext(tp: Type) = tp.existentialSkolems match { + def existentialContext(tp: Type) = tp.skolemsExceptMethodTypeParams match { case Nil => "" case xs => " where " + (disambiguate(xs map (_.existentialToString)) mkString ", ") } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 25f3e7af5c..0dd4b37131 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -909,8 +909,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def apply(tp: Type) = mapOver(tp) match { case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) - val skolem = context.owner.newExistentialSkolem(tpSym, tpSym, unit.freshTypeName("?"+tpSym.name), bounds) - // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt) + // origin must be the type param so we can deskolemize + val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) + // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) skolems += skolem skolem.tpe case tp1 => tp1 @@ -928,9 +929,19 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { freeVars foreach ctorContext.scope.enter newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) - // tree1's type-slack skolems will be deskolemized (to the method type parameter skolems) - // once the containing CaseDef has been type checked (see typedCase) - tree1 + // simplify types without losing safety, + // so that error messages don't unnecessarily refer to skolems + val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) + val extrapolated = tree1.tpe match { + case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node + ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type + copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) + case tp => tp + } + + // once the containing CaseDef has been type checked (see typedCase), + // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) + tree1 setType extrapolated } else { tree } @@ -1095,7 +1106,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val found = tree.tpe val req = pt if (!found.isErroneous && !req.isErroneous) { - if (!context.reportErrors && isPastTyper && req.existentialSkolems.nonEmpty) { + if (!context.reportErrors && isPastTyper && req.skolemsExceptMethodTypeParams.nonEmpty) { // Ignore type errors raised in later phases that are due to mismatching types with existential skolems // We have lift crashing in 2.9 with an adapt failure in the pattern matcher. // Here's my hypothsis why this happens. The pattern matcher defines a variable of type @@ -1112,7 +1123,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // // val x = expr context.unit.warning(tree.pos, "recovering from existential Skolem type error in tree \n" + tree + "\nwith type " + tree.tpe + "\n expected type = " + pt + "\n context = " + context.tree) - adapt(tree, mode, deriveTypeWithWildcards(pt.existentialSkolems)(pt)) + adapt(tree, mode, deriveTypeWithWildcards(pt.skolemsExceptMethodTypeParams)(pt)) } else { // create an actual error AdaptTypeError(tree, found, req) @@ -2112,21 +2123,21 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // body1 = checkNoEscaping.locals(context.scope, pt, body1) val treeWithSkolems = treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe - // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher - // TODO: Paul, can we do the deskolemization lazily in the old pattern matcher - object deskolemizeOnce extends TypeMap { - def apply(tp: Type): Type = mapOver(tp) match { - case TypeRef(pre, sym, args) if sym.isExistentialSkolem && sym.deSkolemize.isSkolem && sym.deSkolemize.owner.isTerm => - typeRef(NoPrefix, sym.deSkolemize, args) - case tp1 => tp1 - } - } - - new TypeMapTreeSubstituter(deskolemizeOnce).traverse(treeWithSkolems) + new TypeMapTreeSubstituter(deskolemizeGADTSkolems).traverse(treeWithSkolems) treeWithSkolems // now without skolems, actually } + // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher + // the flags are used to avoid accidentally deskolemizing unrelated skolems of skolems + object deskolemizeGADTSkolems extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case TypeRef(pre, sym, args) if sym.isGADTSkolem => + typeRef(NoPrefix, sym.deSkolemize, args) + case tp1 => tp1 + } + } + def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = cases mapConserve { cdef => newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) -- cgit v1.2.3 From 50bf852c9a48312e60b441d052150fdd78b52a1a Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 19 Mar 2012 15:07:57 -0700 Subject: Tree adjustments. I noticed that if we gave Ident a "qualifier" method which is always EmptyTree, a whole bunch of code could be simplified. A transparent subclass of Apply which preserves the source level "new" a little longer than never. Since productPrefix is tied up in reification - and oh I detest this "String"-based programming, we shouldn't have to wait until everyone learns this independently to change it - I added "printingPrefix" to Tree, which defaults to productPrefix but can be changed without breaking reify. --- .../scala/reflect/internal/Importers.scala | 2 ++ src/compiler/scala/reflect/internal/Symbols.scala | 1 + .../scala/reflect/internal/TreePrinters.scala | 2 +- .../tools/nsc/interactive/RangePositions.scala | 2 +- src/library/scala/reflect/api/TreePrinters.scala | 2 +- src/library/scala/reflect/api/Trees.scala | 25 ++++++++++++++++------ 6 files changed, 24 insertions(+), 10 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala index 1003fa804f..04381937d1 100644 --- a/src/compiler/scala/reflect/internal/Importers.scala +++ b/src/compiler/scala/reflect/internal/Importers.scala @@ -351,6 +351,8 @@ trait Importers { self: SymbolTable => new ApplyToImplicitArgs(importTree(fun), args map importTree) case _: from.ApplyImplicitView => new ApplyImplicitView(importTree(fun), args map importTree) + case _: from.ApplyConstructor => + new ApplyConstructor(importTree(fun), args map importTree) case _ => new Apply(importTree(fun), args map importTree) } diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 9678d2b8cd..d8e7ce68a2 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1985,6 +1985,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isTrait) ("trait", "trait", "TRT") else if (isClass) ("class", "class", "CLS") else if (isType) ("type", "type", "TPE") + else if (isClassConstructor && isPrimaryConstructor) ("primary constructor", "constructor", "PCTOR") else if (isClassConstructor) ("constructor", "constructor", "CTOR") else if (isSourceMethod) ("method", "method", "METH") else if (isTerm) ("value", "value", "VAL") diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index f823110440..8ed0ee6357 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -433,7 +433,7 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => /** Hook for extensions */ def xprintTree(treePrinter: TreePrinter, tree: Tree) = - treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) + treePrinter.print(tree.printingPrefix+tree.productIterator.mkString("(", ", ", ")")) def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala index d08a363a9d..88e3827403 100644 --- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala +++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala @@ -189,7 +189,7 @@ self: scala.tools.nsc.Global => override def validatePositions(tree: Tree) { def reportTree(prefix : String, tree : Tree) { val source = if (tree.pos.isDefined) tree.pos.source else "" - inform("== "+prefix+" tree ["+tree.id+"] of type "+tree.productPrefix+" at "+tree.pos.show+source) + inform("== "+prefix+" tree ["+tree.id+"] of type "+tree.printingPrefix+" at "+tree.pos.show+source) inform("") inform(treeStatus(tree)) inform("") diff --git a/src/library/scala/reflect/api/TreePrinters.scala b/src/library/scala/reflect/api/TreePrinters.scala index 21b55e9c0e..43865915d3 100644 --- a/src/library/scala/reflect/api/TreePrinters.scala +++ b/src/library/scala/reflect/api/TreePrinters.scala @@ -41,7 +41,7 @@ trait TreePrinters { self: Universe => else if (tree.original != null) print(".setOriginal(", tree.original, ")") case tree: Tree => - print(tree.productPrefix+"(") + print(tree.printingPrefix+"(") val it = tree.productIterator while (it.hasNext) { it.next() match { diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index a355207ff0..a8276dc853 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -76,6 +76,12 @@ trait Trees { self: Universe => private[this] var rawpos: Position = NoPosition + /** Prefix under which to print this tree type. Defaults to product + * prefix (e.g. DefTree) but because that is used in reification + * it cannot be altered without breaking reflection. + */ + def printingPrefix = productPrefix + def pos = rawpos def pos_=(pos: Position) = rawpos = pos def setPos(pos: Position): this.type = { rawpos = pos; this } @@ -249,6 +255,7 @@ trait Trees { self: Universe => * are in DefTrees. */ trait RefTree extends SymTree { + def qualifier: Tree // empty for Idents def name: Name } @@ -489,16 +496,14 @@ trait Trees { self: Universe => /** Factory method for object creation `new tpt(args_1)...(args_n)` * A `New(t, as)` is expanded to: `(new t).(as)` */ - def New(tpt: Tree, argss: List[List[Tree]]): Tree = { - // todo. we need to expose names in scala.reflect.api - val superRef: Tree = Select(New(tpt), nme.CONSTRUCTOR) - if (argss.isEmpty) Apply(superRef, Nil) - else (superRef /: argss) (Apply) + def New(tpt: Tree, argss: List[List[Tree]]): Tree = argss match { + case Nil => new ApplyConstructor(tpt, Nil) + case xs :: rest => rest.foldLeft(new ApplyConstructor(tpt, xs): Tree)(Apply) } /** 0-1 argument list new, based on a type. */ def New(tpe: Type, args: Tree*): Tree = - New(TypeTree(tpe), List(args.toList)) + new ApplyConstructor(TypeTree(tpe), args.toList) /** Type annotation, eliminated by explicit outer */ case class Typed(expr: Tree, tpt: Tree) @@ -537,6 +542,10 @@ trait Trees { self: Universe => class ApplyImplicitView(fun: Tree, args: List[Tree]) extends Apply(fun, args) + class ApplyConstructor(tpt: Tree, args: List[Tree]) extends Apply(Select(New(tpt), nme.CONSTRUCTOR), args) { + override def printingPrefix = "ApplyConstructor" + } + /** Dynamic value application. * In a dynamic application q.f(as) * - q is stored in qual @@ -575,7 +584,9 @@ trait Trees { self: Universe => Select(qualifier, sym.name) setSymbol sym /** Identifier */ - case class Ident(name: Name) extends RefTree + case class Ident(name: Name) extends RefTree { + def qualifier: Tree = EmptyTree + } def Ident(name: String): Ident = Ident(newTermName(name)) -- cgit v1.2.3 From 752884f1606320913ffe66f3bbf3db61746e2436 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 19 Mar 2012 15:08:43 -0700 Subject: Overhaul of NodePrinters. Drawing on the previous, a half-decade overdue overhaul of NodePrinters. Now with colors if you like colors. % scalac -Xprint:all -Yshow-trees -Dscala.colors a.scala Am heading toward general purpose color configuration. --- .../scala/tools/nsc/ast/NodePrinters.scala | 524 +++++++++++---------- .../scala/tools/nsc/ast/TreeBrowsers.scala | 147 +----- 2 files changed, 287 insertions(+), 384 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index 9466d1c1f2..3d4253f941 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -9,14 +9,13 @@ package ast import compat.Platform.EOL import symtab.Flags._ -/** The object nodePrinter converts the internal tree - * representation to a string formatted as a Scala expression. +/** The object `nodePrinter` converts the internal tree + * representation to a string. * * @author Stephane Micheloud - * @version 1.0 + * @author Paul Phillips */ abstract class NodePrinters { - val global: Global import global._ @@ -25,262 +24,293 @@ abstract class NodePrinters { } var infolevel = InfoLevel.Quiet - object nodeToString extends Function1[Tree, String] { + def nodeToString: Tree => String = + if (sys.props contains "scala.colors") nodeToColorizedString + else nodeToRegularString + + object nodeToRegularString extends DefaultPrintAST with (Tree => String) { + def apply(tree: Tree) = stringify(tree) + } + + object nodeToColorizedString extends ColorPrintAST with (Tree => String) { + def apply(tree: Tree) = stringify(tree) + } + + trait ColorPrintAST extends DefaultPrintAST { + import scala.tools.util.color._ + + def keywordColor = Cyan + def typeColor = Yellow + def termColor = Blue + def flagColor = Red + def literalColor = Green + + override def showFlags(tree: MemberDef) = "" + ( + super.showFlags(tree) in flagColor.bright + ) + override def showDefTreeName(tree: DefTree) = "" + ( + if (tree.name.isTermName) tree.name.decode in termColor.bright + else tree.name.decode in typeColor.bright + ) + override def showName(name: Name): String = "" + ( + if (name == nme.EMPTY || name == tpnme.EMPTY) "" in keywordColor + else if (name.isTermName) name.decode in termColor + else name.decode in typeColor + ) + override def showLiteral(lit: Literal) = "" + ( + super.showLiteral(lit) in literalColor.bright + ) + } + + trait DefaultPrintAST extends PrintAST { + def showDefTreeName(tree: DefTree) = showName(tree.name) + def showFlags(tree: MemberDef) = flagsToString(tree.symbol.flags | tree.mods.flags) + def showLiteral(lit: Literal) = lit.value.escapedStringValue + def showTypeTree(tt: TypeTree) = "" + showAttributes(tt) + def showName(name: Name) = name match { + case nme.EMPTY | tpnme.EMPTY => "" + case name => "\"" + name + "\"" + } + + def showSymbol(tree: Tree): String = { + val sym = tree.symbol + if (sym == null || sym == NoSymbol) "" + else " sym/owner/tpe=%s %s/%s/%s".format(sym.accurateKindString, sym.name, sym.owner, sym.tpe) + } + def showType(tree: Tree): String = { + val tpe = tree.tpe + if (tpe == null || tpe == NoType) "" + else " tree.tpe=" + tpe + } + + def showAttributes(tree: Tree): String = { + if (infolevel == InfoLevel.Quiet) "" + else { + try { showSymbol(tree) + showType(tree) trim } + catch { case ex: Throwable => "sym= " + ex.getMessage } + } + } + } + + trait PrintAST { private val buf = new StringBuilder + private var level = 0 - def apply(tree: Tree): String = { - def traverse(tree: Tree, level: Int, comma: Boolean) { - def println(s: String) { - for (i <- 0 until level) buf.append(" ") - buf.append(s) - buf.append(EOL) - } - def printcln(s: String) { - for (i <- 0 until level) buf.append(" ") - buf.append(s) - if (comma) buf.append(",") - buf.append(EOL) - } - def annotationInfoToString(annot: AnnotationInfo): String = { - val str = new StringBuilder - str.append(annot.atp.toString()) - if (!annot.args.isEmpty) - str.append(annot.args.mkString("(", ",", ")")) - if (!annot.assocs.isEmpty) - for (((name, value), index) <- annot.assocs.zipWithIndex) { - if (index > 0) - str.append(", ") - str.append(name).append(" = ").append(value) - } - str.toString - } - def symflags(tree: Tree): String = { - val buf = new StringBuffer - val sym = tree.symbol - buf append flagsToString(sym.flags) + def showName(name: Name): String + def showDefTreeName(defTree: DefTree): String + def showFlags(tree: MemberDef): String + def showLiteral(lit: Literal): String + def showTypeTree(tt: TypeTree): String + def showAttributes(tree: Tree): String // symbol and type + + def showRefTreeName(tree: Tree): String = tree match { + case SelectFromTypeTree(qual, name) => showRefTreeName(qual) + "#" + showName(name) + case Select(qual, name) => showRefTreeName(qual) + "." + showName(name) + case Ident(name) => showName(name) + case _ => "" + tree + } + def showRefTree(tree: RefTree): String = { + def prefix0 = showRefTreeName(tree.qualifier) + def prefix = if (prefix0 == "") "" else (tree match { + case SelectFromTypeTree(_, _) => prefix0 + "#" + case Select(_, _) => prefix0 + "." + case _ => "" + }) + def attrs = showAttributes(tree) match { + case "" => "" + case s => " // " + s + } + prefix + showName(tree.name) + attrs + } + + def stringify(tree: Tree): String = { + buf.clear() + level = 0 + traverse(tree) + buf.toString + } + def traverseAny(x: Any) { + x match { + case t: Tree => traverse(t) + case xs: List[_] => printMultiline("List", "")(xs foreach traverseAny) + case _ => println("" + x) + } + } + def println(s: String) = printLine(s, "") + + def printLine(value: String, comment: String) { + buf append " " * level + buf append value + if (comment != "") { + buf append " // " + buf append comment + } + buf append EOL + } - val annots = ", annots=" + ( - if (!sym.annotations.isEmpty) - sym.annotations.map(annotationInfoToString).mkString("[", ",", "]") - else - tree.asInstanceOf[MemberDef].mods.annotations) - (if (buf.length() > 2) buf.substring(3) - else "0") + ", // flags=" + flagsToString(sym.flags) + annots + def annotationInfoToString(annot: AnnotationInfo): String = { + val str = new StringBuilder + str.append(annot.atp.toString()) + if (!annot.args.isEmpty) + str.append(annot.args.mkString("(", ",", ")")) + if (!annot.assocs.isEmpty) + for (((name, value), index) <- annot.assocs.zipWithIndex) { + if (index > 0) + str.append(", ") + str.append(name).append(" = ").append(value) } + str.toString + } + def printModifiers(tree: MemberDef) { + val annots0 = tree.symbol.annotations match { + case Nil => tree.mods.annotations + case xs => xs map annotationInfoToString + } + val annots = annots0 match { + case Nil => "" + case xs => " " + xs.mkString("@{ ", ", ", " }") + } + val flagString = showFlags(tree) match { + case "" => "0" + case s => s + } + println(flagString + annots) + } - def nodeinfo(tree: Tree): String = - if (infolevel == InfoLevel.Quiet) "" - else { - try { - val buf = new StringBuilder(" // sym=" + tree.symbol) - if (tree.hasSymbol) { - if (tree.symbol.isPrimaryConstructor) - buf.append(", isPrimaryConstructor") - else if (tree.symbol.isConstructor) - buf.append(", isConstructor") - if (tree.symbol != NoSymbol) - buf.append(", sym.owner=" + tree.symbol.owner) - buf.append(", sym.tpe=" + tree.symbol.tpe) - } - buf.append(", tpe=" + tree.tpe) - if (tree.tpe != null) { - var sym = tree.tpe.termSymbol - if (sym == NoSymbol) sym = tree.tpe.typeSymbol - buf.append(", tpe.sym=" + sym) - if (sym != NoSymbol) { - buf.append(", tpe.sym.owner=" + sym.owner) - if ((infolevel > InfoLevel.Normal) && - !(sym.owner eq definitions.ScalaPackageClass) && - !sym.isModuleClass && !sym.isPackageClass && - !sym.isJavaDefined) { - val members = for (m <- tree.tpe.decls) - yield m.toString() + ": " + m.tpe + ", " - buf.append(", tpe.decls=" + members) - } + def applyCommon(tree: Tree, fun: Tree, args: List[Tree]) { + printMultiline(tree) { + traverse(fun) + traverseList("Nil", _ + " arguments(s)")(args) + } + } + + def printMultiline(tree: Tree)(body: => Unit) { + printMultiline(tree.printingPrefix, showAttributes(tree))(body) + } + def printMultiline(prefix: String, comment: String)(body: => Unit) { + printLine(prefix + "(", comment) + indent(body) + println(")") + } + @inline private def indent[T](body: => T): T = { + level += 1 + try body + finally level -= 1 + } + + def traverseList(ifEmpty: String, comment: Int => String)(trees: List[Tree]) { + if (trees.isEmpty) + println(ifEmpty) + else + printMultiline("List", comment(trees.length))(trees foreach traverse) + } + + def traverse(tree: Tree) { + tree match { + case AppliedTypeTree(tpt, args) => applyCommon(tree, tpt, args) + case ApplyDynamic(fun, args) => applyCommon(tree, fun, args) + case Apply(fun, args) => applyCommon(tree, fun, args) + + case Block(stats, expr) => + printMultiline(tree) { + traverseList("{}", _ + " statement(s)")(stats) + traverse(expr) + } + case cd @ ClassDef(mods, name, tparams, impl) => + printMultiline(tree) { + printModifiers(cd) + println(showDefTreeName(cd)) + traverseList("[]", _ + " type parameter(s)")(tparams) + traverse(impl) + } + case md @ ModuleDef(mods, name, impl) => + printMultiline(tree) { + printModifiers(md) + println(showDefTreeName(md)) + traverse(impl) + } + case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + printMultiline(tree) { + printModifiers(dd) + println(showDefTreeName(dd)) + traverseList("[]", _ + " type parameter(s)")(tparams) + vparamss match { + case Nil => println("Nil") + case Nil :: Nil => println("List(Nil)") + case xss => + printMultiline("List", xss.length + " parameter list(s)") { + xss foreach (xs => traverseList("()", _ + " parameter(s)")(xs)) } + } + traverse(tpt) + traverse(rhs) + } + case EmptyTree => + println(showName(nme.EMPTY)) + case lit @ Literal(value) => + println(showLiteral(lit)) + case New(tpt) => + printMultiline(tree)(traverse(tpt)) + case Super(This(qual), mix) => + println("Super(This(" + showName(qual) + "), " + showName(mix) + ")") + case Super(qual, mix) => + printMultiline(tree) { + traverse(qual) + showName(mix) + } + case Template(parents, self, body) => + printMultiline(tree) { + val ps0 = parents map { p => + if (p.tpe eq null) p match { + case x: RefTree => showRefTree(x) + case x => "" + x } - buf.toString - } catch { - case ex: Throwable => - return " // sym= " + ex.getMessage + else showName(newTypeName(p.tpe.typeSymbol.fullName)) } + printLine(ps0 mkString ", ", "parents") + traverse(self) + traverseList("{}", _ + " statements in body")(body) } - def nodeinfo2(tree: Tree): String = - (if (comma) "," else "") + nodeinfo(tree) - - def applyCommon(name: String, tree: Tree, fun: Tree, args: List[Tree]) { - println(name + "(" + nodeinfo(tree)) - traverse(fun, level + 1, true) - if (args.isEmpty) - println(" Nil // no argument") - else { - val n = args.length - println(" List( // " + n + " arguments(s)") - for (i <- 0 until n) - traverse(args(i), level + 2, i < n-1) - println(" )") + case This(qual) => + println("This(\"" + showName(qual) + "\")" + showAttributes(tree)) + case TypeApply(fun, args) => + printMultiline(tree) { + traverse(fun) + traverseList("[]", _ + " type argument(s)")(args) } - printcln(")") - } + case tt @ TypeTree() => + println(showTypeTree(tt)) - tree match { - case AppliedTypeTree(tpt, args) => applyCommon("AppliedTypeTree", tree, tpt, args) - case Apply(fun, args) => applyCommon("Apply", tree, fun, args) - case ApplyDynamic(fun, args) => applyCommon("ApplyDynamic", tree, fun, args) + case Typed(expr, tpt) => + printMultiline(tree) { + traverse(expr) + traverse(tpt) + } + case vd @ ValDef(mods, name, tpt, rhs) => + printMultiline(tree) { + printModifiers(vd) + println(showDefTreeName(vd)) + traverse(tpt) + traverse(rhs) + } + case td @ TypeDef(mods, name, tparams, rhs) => + printMultiline(tree) { + printModifiers(td) + println(showDefTreeName(td)) + traverseList("[]", _ + " type parameter(s)")(tparams) + traverse(rhs) + } + + case PackageDef(pid, stats) => + printMultiline("PackageDef", "")(pid :: stats foreach traverse) - case Block(stats, expr) => - println("Block(" + nodeinfo(tree)) - if (stats.isEmpty) - println(" List(), // no statement") - else { - val n = stats.length - println(" List( // " + n + " statement(s)") - for (i <- 0 until n) - traverse(stats(i), level + 2, i < n-1) - println(" ),") - } - traverse(expr, level + 1, false) - printcln(")") - case ClassDef(mods, name, tparams, impl) => - println("ClassDef(" + nodeinfo(tree)) - println(" " + symflags(tree)) - println(" \"" + name + "\",") - if (tparams.isEmpty) - println(" List(), // no type parameter") - else { - val n = tparams.length - println(" List( // " + n + " type parameter(s)") - for (i <- 0 until n) - traverse(tparams(i), level + 2, i < n-1) - println(" ),") - } - traverse(impl, level + 1, false) - printcln(")") - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - println("DefDef(" + nodeinfo(tree)) - println(" " + symflags(tree)) - println(" \"" + name + "\",") - if (tparams.isEmpty) - println(" List(), // no type parameter") - else { - val n = tparams.length - println(" List( // " + n + " type parameter(s)") - for (i <- 0 until n) - traverse(tparams(i), level + 2, i < n-1) - println(" ),") - } - val n = vparamss.length - if (n == 1 && vparamss(0).isEmpty) - println(" List(List()), // no parameter") - else { - println(" List(") - for (i <- 0 until n) { - val m = vparamss(i).length - println(" List( // " + m + " parameter(s)") - for (j <- 0 until m) - traverse(vparamss(i)(j), level + 3, j < m-1) - println(" )") - } - println(" ),") - } - println(" " + tpt + ",") - traverse(rhs, level + 1, false) - printcln(")") - case EmptyTree => - printcln("EmptyTree") - case Ident(name) => - printcln("Ident(\"" + name + "\")" + nodeinfo2(tree)) - case Literal(value) => - printcln("Literal(" + value + ")") - case New(tpt) => - println("New(" + nodeinfo(tree)) - traverse(tpt, level + 1, false) - printcln(")") - case Select(qualifier, selector) => - println("Select(" + nodeinfo(tree)) - traverse(qualifier, level + 1, true) - printcln(" \"" + selector + "\")") - case Super(qual, mix) => - println("Super(\"" + mix + "\")" + nodeinfo(tree)) - traverse(qual, level + 1, true) - case Template(parents, self, body) => - println("Template(" + nodeinfo(tree)) - println(" " + parents.map(p => - if (p.tpe ne null) p.tpe.typeSymbol else "null-" + p - ) + ", // parents") - traverse(self, level + 1, true) - if (body.isEmpty) - println(" List() // no body") - else { - val n = body.length - println(" List( // body") - for (i <- 0 until n) - traverse(body(i), level + 2, i < n-1) - println(" )") - } - printcln(")") - case This(qual) => - println("This(\"" + qual + "\")" + nodeinfo2(tree)) - case TypeApply(fun, args) => - println("TypeApply(" + nodeinfo(tree)) - traverse(fun, level + 1, true) - if (args.isEmpty) - println(" List() // no argument") - else { - val n = args.length - println(" List(") - for (i <- 0 until n) - traverse(args(i), level + 1, i < n-1) - println(" )") - } - printcln(")") - case TypeTree() => - printcln("TypeTree()" + nodeinfo2(tree)) - case Typed(expr, tpt) => - println("Typed(" + nodeinfo(tree)) - traverse(expr, level + 1, true) - traverse(tpt, level + 1, false) - printcln(")") - case ValDef(mods, name, tpt, rhs) => - println("ValDef(" + nodeinfo(tree)) - println(" " + symflags(tree)) - println(" \"" + name + "\",") - traverse(tpt, level + 1, true) - traverse(rhs, level + 1, false) - printcln(")") - case PackageDef(pid, stats) => - println("PackageDef(") - traverse(pid, level + 1, false) - println(",\n") - for (stat <- stats) - traverse(stat, level + 1, false) - printcln(")") - case _ => - tree match { - case p: Product => - if (p.productArity != 0) { - println(p.productPrefix+"(") - for (elem <- (0 until p.productArity) map p.productElement) { - def printElem(elem: Any, level: Int): Unit = elem match { - case t: Tree => - traverse(t, level, false) - case xs: List[_] => - print("List(") - for (x <- xs) printElem(x, level+1) - printcln(")") - case _ => - println(elem.toString) - } - printElem(elem, level+1) - } - printcln(")") - } else printcln(p.productPrefix) - } - } + case _ => + tree match { + case t: RefTree => println(showRefTree(t)) + case t if t.productArity == 0 => println(tree.printingPrefix) + case t => printMultiline(tree)(tree.productIterator foreach traverseAny) + } } - buf setLength 0 - traverse(tree, 0, false) - buf.toString } } diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index c1d6c1a4d4..3302c11127 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -352,144 +352,17 @@ abstract class TreeBrowsers { * Tree. */ object TreeInfo { - /** Return the case class name and the Name, if the node defines one */ - def treeName(t: Tree): (String, Name) = t match { - case ProgramTree(units) => - ("Program", EMPTY) - - case UnitTree(unit) => - ("CompilationUnit", newTermName("" + unit)) - - case DocDef(comment, definition) => - ("DocDef", EMPTY) - - case ClassDef(mods, name, tparams, impl) => - ("ClassDef", name) - - case PackageDef(packaged, impl) => - ("PackageDef", EMPTY) - - case ModuleDef(mods, name, impl) => - ("ModuleDef", name) - - case ValDef(mods, name, tpe, rhs) => - ("ValDef", name) - - case DefDef(mods, name, tparams, vparams, tpe, rhs) => - ("DefDef", name) - - case TypeDef(mods, name, tparams, rhs) => - ("TypeDef", name) - - case Import(expr, selectors) => - ("Import", EMPTY) - - case CaseDef(pat, guard, body) => - ("CaseDef", EMPTY) - - case Template(parents, self, body) => - ("Template", EMPTY) - - case LabelDef(name, params, rhs) => - ("LabelDef", name) - - case Block(stats, expr) => - ("Block", EMPTY) - - case Alternative(trees) => - ("Alternative", EMPTY) - - case Bind(name, rhs) => - ("Bind", name) - - case UnApply(fun, args) => - ("UnApply", EMPTY) - - case Match(selector, cases) => - ("Visitor", EMPTY) - - case Function(vparams, body) => - ("Function", EMPTY) - - case Assign(lhs, rhs) => - ("Assign", EMPTY) - - case If(cond, thenp, elsep) => - ("If", EMPTY) - - case Return(expr) => - ("Return", EMPTY) - - case Throw(expr) => - ("Throw", EMPTY) - - case New(init) => - ("New", EMPTY) - - case Typed(expr, tpe) => - ("Typed", EMPTY) - - case TypeApply(fun, args) => - ("TypeApply", EMPTY) - - case Apply(fun, args) => - ("Apply", EMPTY) - - case ApplyDynamic(qual, args) => - ("Apply", EMPTY) - - case Super(qualif, mix) => - ("Super", newTermName("mix: " + mix)) - - case This(qualifier) => - ("This", qualifier) - - case Select(qualifier, selector) => - ("Select", selector) - - case Ident(name) => - ("Ident", name) - - case Literal(value) => - ("Literal", EMPTY) - - case TypeTree() => - ("TypeTree", EMPTY) - - case Annotated(annot, arg) => - ("Annotated", EMPTY) - - case SingletonTypeTree(ref) => - ("SingletonType", EMPTY) - - case SelectFromTypeTree(qualifier, selector) => - ("SelectFromType", selector) - - case CompoundTypeTree(template) => - ("CompoundType", EMPTY) - - case AppliedTypeTree(tpe, args) => - ("AppliedType", EMPTY) - - case TypeBoundsTree(lo, hi) => - ("TypeBoundsTree", EMPTY) - - case ExistentialTypeTree(tpt, whereClauses) => - ("ExistentialTypeTree", EMPTY) - - case Try(block, catcher, finalizer) => - ("Try", EMPTY) - - case EmptyTree => - ("Empty", EMPTY) - - case ArrayValue(elemtpt, trees) => - ("ArrayValue", EMPTY) - - case Star(t) => - ("Star", EMPTY) - } + def treeName(t: Tree): (String, Name) = ((t.printingPrefix, t match { + case UnitTree(unit) => newTermName("" + unit) + case Super(_, mix) => newTermName("mix: " + mix) + case This(qual) => qual + case Select(_, selector) => selector + case Ident(name) => name + case SelectFromTypeTree(_, selector) => selector + case x: DefTree => x.name + case _ => EMPTY + })) /** Return a list of children for the given tree node */ def children(t: Tree): List[Tree] = t match { -- cgit v1.2.3 From aafcc104b6ebe2fa163fc7a9cbfb742bf5f26d08 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 19 Mar 2012 16:14:58 -0700 Subject: Chill out repl ctrl-char filter. So colors can come through unscathed. --- .../scala/tools/nsc/interpreter/Naming.scala | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/compiler/scala/tools/nsc/interpreter/Naming.scala index 8e215cf63b..19266442cb 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Naming.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Naming.scala @@ -11,16 +11,18 @@ package interpreter */ trait Naming { def unmangle(str: String): String = { + val ESC = '\u001b' val cleaned = removeIWPackages(removeLineWrapper(str)) - var ctrlChars = 0 - cleaned map { ch => - if (ch.isControl && !ch.isWhitespace) { - ctrlChars += 1 - if (ctrlChars > 5) return "[line elided for control chars: possibly a scala signature]" - else '?' - } - else ch - } + // Looking to exclude binary data which hoses the terminal, but + // let through the subset of it we need, like whitespace and also + // for ansi codes. + val binaryChars = cleaned count (ch => ch < 32 && !ch.isWhitespace && ch != ESC) + // Lots of binary chars - translate all supposed whitespace into spaces + if (binaryChars > 5) + cleaned map (ch => if (ch.isWhitespace) ' ' else if (ch < 32) '?' else ch) + // Not lots - preserve whitespace and ESC + else + cleaned map (ch => if (ch.isWhitespace || ch == ESC) ch else if (ch < 32) '?' else ch) } // The two name forms this is catching are the two sides of this assignment: -- cgit v1.2.3 From c7d852558302c5c4abc2eadacf42d51d5050c7f2 Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Tue, 20 Mar 2012 01:20:52 +0100 Subject: Adapted indentation in scaladoc code blocks and fixed a pesky crash in the syntax highlighting caused by invalid chars (0x0E) in MarkupParser.scala. --- .../scala/tools/nsc/doc/html/SyntaxHigh.scala | 12 ++--- .../nsc/doc/model/comment/CommentFactory.scala | 60 +++++++++++++++++++++- src/library/scala/xml/parsing/MarkupParser.scala | 22 ++++---- test/scaladoc/resources/code-indent.scala | 37 +++++++++++++ test/scaladoc/scala/html/HtmlFactoryTest.scala | 19 ++++++- 5 files changed, 131 insertions(+), 19 deletions(-) create mode 100644 test/scaladoc/resources/code-indent.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala b/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala index f19f449d2c..f67abc58da 100644 --- a/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala +++ b/src/compiler/scala/tools/nsc/doc/html/SyntaxHigh.scala @@ -219,24 +219,24 @@ private[html] object SyntaxHigh { parse(" ", i+1) case '&' => parse("&", i+1) - case '<' => + case '<' if i+1 < buf.length => val ch = buf(i+1).toChar if (ch == '-' || ch == ':' || ch == '%') parse("<"+ch+"", i+2) else parse("<", i+1) case '>' => - if (buf(i+1) == ':') + if (i+1 < buf.length && buf(i+1) == ':') parse(">:", i+2) else parse(">", i+1) case '=' => - if (buf(i+1) == '>') + if (i+1 < buf.length && buf(i+1) == '>') parse("=>", i+2) else parse(buf(i).toChar.toString, i+1) case '/' => - if (buf(i+1) == '/' || buf(i+1) == '*') { + if (i+1 < buf.length && (buf(i+1) == '/' || buf(i+1) == '*')) { val c = comment(i+1) parse(""+c+"", i+c.length) } else @@ -257,9 +257,9 @@ private[html] object SyntaxHigh { else parse(buf(i).toChar.toString, i+1) case _ => - if (i == 0 || !Character.isJavaIdentifierPart(buf(i-1).toChar)) { + if (i == 0 || (i >= 1 && !Character.isJavaIdentifierPart(buf(i-1).toChar))) { if (Character.isDigit(buf(i)) || - (buf(i) == '.' && Character.isDigit(buf(i+1)))) { + (buf(i) == '.' && i + 1 < buf.length && Character.isDigit(buf(i+1)))) { val s = numlit(i) parse(""+s+"", i+s.length) } else { diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index efa524503c..dbbd6ab432 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -491,7 +491,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => else jump("}}}") blockEnded("code block") - Code(getRead) + Code(normalizeIndentation(getRead)) } /** {{{ title ::= ('=' inline '=' | "==" inline "==" | ...) '\n' }}} */ @@ -732,6 +732,64 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => nextChar() } + /** + * Eliminates the (common) leading spaces in all lines, based on the first line + * For indented pieces of code, it reduces the indent to the least whitespace prefix: + * {{{ + * indented example + * another indented line + * if (condition) + * then do something; + * ^ this is the least whitespace prefix + * }}} + */ + def normalizeIndentation(_code: String): String = { + + var code = _code.trim + var maxSkip = Integer.MAX_VALUE + var crtSkip = 0 + var wsArea = true + var index = 0 + var firstLine = true + var emptyLine = true + + while (index < code.length) { + code(index) match { + case ' ' => + if (wsArea) + crtSkip += 1 + case c => + wsArea = (c == '\n') + maxSkip = if (firstLine || emptyLine) maxSkip else if (maxSkip <= crtSkip) maxSkip else crtSkip + crtSkip = if (c == '\n') 0 else crtSkip + firstLine = if (c == '\n') false else firstLine + emptyLine = if (c == '\n') true else false + } + index += 1 + } + + if (maxSkip == 0) + code + else { + index = 0 + val builder = new StringBuilder + while (index < code.length) { + builder.append(code(index)) + if (code(index) == '\n') { + // we want to skip as many spaces are available, if there are less spaces (like on empty lines, do not + // over-consume them) + index += 1 + val limit = index + maxSkip + while ((index < code.length) && (code(index) == ' ') && index < limit) + index += 1 + } + else + index += 1 + } + builder.toString + } + } + def checkParaEnded(): Boolean = { (char == endOfText) || ((char == endOfLine) && { diff --git a/src/library/scala/xml/parsing/MarkupParser.scala b/src/library/scala/xml/parsing/MarkupParser.scala index 1de08b3025..74781914e3 100644 --- a/src/library/scala/xml/parsing/MarkupParser.scala +++ b/src/library/scala/xml/parsing/MarkupParser.scala @@ -134,7 +134,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests // /** {{{ - * <? prolog ::= xml S ... ?> + * * }}} */ def xmlProcInstr(): MetaData = { xToken("xml") @@ -195,7 +195,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * <? prolog ::= xml S? + * "{char} ) ']]>' * * see [15] * }}} */ @@ -369,7 +369,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' + * Comment ::= '' * * see [15] * }}} */ @@ -399,7 +399,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * '<' content1 ::= ... + * '<' content1 ::= ... * }}} */ def content1(pscope: NamespaceBinding, ts: NodeBuffer) { ch match { @@ -420,7 +420,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * content1 ::= '<' content1 | '&' charref ... + * content1 ::= '<' content1 | '&' charref ... * }}} */ def content(pscope: NamespaceBinding): NodeSeq = { var ts = new NodeBuffer @@ -490,7 +490,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests /** parses document type declaration and assigns it to instance variable * dtd. * {{{ - * <! parseDTD ::= DOCTYPE name ... > + * * }}} */ def parseDTD() { // dirty but fast var extID: ExternalID = null @@ -545,8 +545,8 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag - * | xmlTag1 '/' '>' + * '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag + * | xmlTag1 '/' '>' * }}} */ def element1(pscope: NamespaceBinding): NodeSeq = { val pos = this.pos @@ -778,7 +778,7 @@ trait MarkupParser extends MarkupParserCommon with TokenTests } /** {{{ - * <! attlist := ATTLIST + * { + val s = node.toString + s.contains("
a typicial indented\ncomment on multiple\ncomment lines
") && + s.contains("
one liner
") && + s.contains("
two lines, one useful
") && + s.contains("
line1\nline2\nline3\nline4
") && + s.contains("
a ragged example\na (condition)\n  the t h e n branch\nan alternative\n  the e l s e branch
") && + s.contains("
l1\n\nl2\n\nl3\n\nl4\n\nl5
") + } + case _ => false + } + } + { val files = createTemplates("basic.scala") //println(files) -- cgit v1.2.3 From c82ecabad6fc050411495f3fd50c3bf79ac7e96e Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 19 Mar 2012 18:26:55 -0700 Subject: Finally did something about broken irrefutability. The parser has always been confused about tuple patterns in for comprehensions. It thinks it can fail to recognize an irrefutable pattern and have it removed in refchecks, but it is sadly mistaken, because the unnecessary filter has a tendency to fail the compile in typer. Look more intently for irrefutable patterns and don't insert the unnecessary filter. Closes SI-5589, SI-1336. --- src/compiler/scala/reflect/internal/TreeInfo.scala | 20 ++++++++++++- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 34 ++++++++++------------ test/files/neg/t5589neg.check | 30 +++++++++++++++++++ test/files/neg/t5589neg.scala | 28 ++++++++++++++++++ test/files/pos/t1336.scala | 10 +++++++ test/files/pos/t5589.scala | 22 ++++++++++++++ 6 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 test/files/neg/t5589neg.check create mode 100644 test/files/neg/t5589neg.scala create mode 100644 test/files/pos/t1336.scala create mode 100644 test/files/pos/t5589.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index 769d7a9ed1..ce3de94335 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -17,7 +17,7 @@ abstract class TreeInfo { val global: SymbolTable import global._ - import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass } + import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -312,6 +312,24 @@ abstract class TreeInfo { case _ => false } + /** Is this tree comprised of nothing but identifiers, + * but possibly in bindings or tuples? For instance + * + * foo @ (bar, (baz, quux)) + * + * is a variable pattern; if the structure matches, + * then the remainder is inevitable. + */ + def isVariablePattern(tree: Tree): Boolean = tree match { + case Bind(name, pat) => isVariablePattern(pat) + case Ident(name) => true + case Apply(sel, args) => + ( isReferenceToScalaMember(sel, TupleClass(args.size).name.toTermName) + && (args forall isVariablePattern) + ) + case _ => false + } + /** Is this argument node of the form : _* ? */ def isWildcardStarArg(tree: Tree): Boolean = tree match { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 0d2fbc5372..80c258e456 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -262,29 +262,25 @@ abstract class TreeBuilder { else if (stats.length == 1) stats.head else Block(stats.init, stats.last) + def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = { + val cases = List( + CaseDef(condition, EmptyTree, Literal(Constant(true))), + CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) + ) + val matchTree = makeVisitor(cases, false, scrutineeName) + + atPos(tree.pos)(Apply(Select(tree, nme.filter), matchTree :: Nil)) + } + /** Create tree for for-comprehension generator */ def makeGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree): Enumerator = { val pat1 = patvarTransformer.transform(pat) val rhs1 = - if (valeq) rhs - else matchVarPattern(pat1) match { - case Some(_) => - rhs - case None => - atPos(rhs.pos) { - Apply( - Select(rhs, nme.filter), - List( - makeVisitor( - List( - CaseDef(pat1.duplicate, EmptyTree, Literal(Constant(true))), - CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))), - false, - nme.CHECK_IF_REFUTABLE_STRING - ))) - } - } - if (valeq) ValEq(pos, pat1, rhs1) else ValFrom(pos, pat1, rhs1) + if (valeq || treeInfo.isVariablePattern(pat)) rhs + else makeFilter(rhs, pat1.duplicate, nme.CHECK_IF_REFUTABLE_STRING) + + if (valeq) ValEq(pos, pat1, rhs1) + else ValFrom(pos, pat1, rhs1) } def makeParam(pname: TermName, tpe: Tree) = diff --git a/test/files/neg/t5589neg.check b/test/files/neg/t5589neg.check new file mode 100644 index 0000000000..e75fd2f4f7 --- /dev/null +++ b/test/files/neg/t5589neg.check @@ -0,0 +1,30 @@ +t5589neg.scala:24: error: constructor cannot be instantiated to expected type; + found : (T1, T2) + required: String + def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:25: error: constructor cannot be instantiated to expected type; + found : (T1, T2) + required: String + def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:26: error: constructor cannot be instantiated to expected type; + found : (T1,) + required: (String, Int) + def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:26: error: not found: value y2 + def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:27: error: constructor cannot be instantiated to expected type; + found : (T1, T2, T3) + required: (String, Int) + def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:27: error: not found: value y1 + def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:27: error: not found: value y2 + def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) + ^ +7 errors found diff --git a/test/files/neg/t5589neg.scala b/test/files/neg/t5589neg.scala new file mode 100644 index 0000000000..ddd382d8d8 --- /dev/null +++ b/test/files/neg/t5589neg.scala @@ -0,0 +1,28 @@ +class A { + // First three compile. + def f1(x: Either[Int, String]) = x.right map (y => y) + def f2(x: Either[Int, String]) = for (y <- x.right) yield y + def f3(x: Either[Int, (String, Int)]) = x.right map { case (y1, y2) => (y1, y2) } + // Last one fails. + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) +/** +./a.scala:5: error: constructor cannot be instantiated to expected type; + found : (T1, T2) + required: Either[Nothing,(String, Int)] + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +./a.scala:5: error: not found: value y1 + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +./a.scala:5: error: not found: value y2 + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +three errors found +**/ + + + def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2)) + def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2)) + def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) + def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) +} diff --git a/test/files/pos/t1336.scala b/test/files/pos/t1336.scala new file mode 100644 index 0000000000..63967985c7 --- /dev/null +++ b/test/files/pos/t1336.scala @@ -0,0 +1,10 @@ +object Foo { + def foreach( f : ((Int,Int)) => Unit ) { + println("foreach") + f(1,2) + } + + for( (a,b) <- this ) { + println((a,b)) + } +} diff --git a/test/files/pos/t5589.scala b/test/files/pos/t5589.scala new file mode 100644 index 0000000000..69cbb20391 --- /dev/null +++ b/test/files/pos/t5589.scala @@ -0,0 +1,22 @@ +class A { + // First three compile. + def f1(x: Either[Int, String]) = x.right map (y => y) + def f2(x: Either[Int, String]) = for (y <- x.right) yield y + def f3(x: Either[Int, (String, Int)]) = x.right map { case (y1, y2) => (y1, y2) } + // Last one fails. + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) +/** +./a.scala:5: error: constructor cannot be instantiated to expected type; + found : (T1, T2) + required: Either[Nothing,(String, Int)] + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +./a.scala:5: error: not found: value y1 + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +./a.scala:5: error: not found: value y2 + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +three errors found +**/ +} -- cgit v1.2.3 From 365bb2b4e3ac880243736bf039b649a63b00ccb2 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 19 Mar 2012 19:44:58 -0700 Subject: Discovered filter was still being generated. Rather than withFilter, for a subset of for comprehension structures. Not sure if this was somewhat by design - only seems possible because refchecks was only looking for nme.filter, not nme.withFilter, so perhaps this was intended as some secret irrefutability backchannel? Really have to document that sort of thing if it's intentional. I assumed it wasn't and unified everything. --- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 2 +- .../scala/tools/nsc/typechecker/RefChecks.scala | 2 +- test/files/pos/irrefutable.scala | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 test/files/pos/irrefutable.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 80c258e456..0bc88d1efd 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -269,7 +269,7 @@ abstract class TreeBuilder { ) val matchTree = makeVisitor(cases, false, scrutineeName) - atPos(tree.pos)(Apply(Select(tree, nme.filter), matchTree :: Nil)) + atPos(tree.pos)(Apply(Select(tree, nme.withFilter), matchTree :: Nil)) } /** Create tree for for-comprehension generator */ diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index ec42d251ff..73369f09af 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1445,7 +1445,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R private def transformApply(tree: Apply): Tree = tree match { case Apply( - Select(qual, nme.filter), + Select(qual, nme.filter | nme.withFilter), List(Function( List(ValDef(_, pname, tpt, _)), Match(_, CaseDef(pat1, _, _) :: _)))) diff --git a/test/files/pos/irrefutable.scala b/test/files/pos/irrefutable.scala new file mode 100644 index 0000000000..0a792b644a --- /dev/null +++ b/test/files/pos/irrefutable.scala @@ -0,0 +1,22 @@ +// The test which this should perform but does not +// is that f1 is recognized as irrefutable and f2 is not +// This can be recognized via the generated classes: +// +// A$$anonfun$f1$1.class +// A$$anonfun$f2$1.class +// A$$anonfun$f2$2.class +// +// The extra one in $f2$ is the filter. +// +// !!! Marking with exclamation points so maybe someday +// this test will be finished. +class A { + case class Foo[T](x: T) + + def f1(xs: List[Foo[Int]]) = { + for (Foo(x: Int) <- xs) yield x + } + def f2(xs: List[Foo[Any]]) = { + for (Foo(x: Int) <- xs) yield x + } +} -- cgit v1.2.3 From eb8afde6882a945caa029a2ea9daeb43c590f5ca Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Tue, 20 Mar 2012 14:38:50 +0100 Subject: Fixes SI-5248 --- build.xml | 2 +- src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala | 2 +- src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala | 12 ++++++++++++ src/compiler/scala/tools/nsc/doc/html/page/Template.scala | 12 +++++++++--- 4 files changed, 23 insertions(+), 5 deletions(-) (limited to 'src/compiler') diff --git a/build.xml b/build.xml index dc70330d7f..37794d1743 100644 --- a/build.xml +++ b/build.xml @@ -1607,7 +1607,7 @@ DOCUMENTATION /** The title of this page. */ protected def title: String + /** The page description */ + protected def description: String = + // unless overwritten, will display the title in a spaced format, keeping - and . + title.replaceAll("[^a-zA-Z0-9\\.\\-]+", " ").replaceAll("\\-+", " - ").replaceAll(" +", " ") + + /** The page keywords */ + protected def keywords: String = + // unless overwritten, same as description, minus the " - " + description.replaceAll(" - ", " ") + /** Additional header elements (links, scripts, meta tags, etc.) required for this page. */ protected def headers: NodeSeq @@ -35,6 +45,8 @@ abstract class HtmlPage extends Page { thisPage => { title } + + -- cgit v1.2.3 From 972bf59a65d98286697ca8eed6a80239259808e4 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 28 Feb 2012 16:47:47 +0100 Subject: [vpm] TODO note: make unapply type list stricter when an unapply returns Option[T] where T is some ProductN, does that mean the unapply returns 1 result, i.e., that T, or did it mean to return N results? to disambiguate, falling back to stricter spec-adherence, which requires T be exactly TupleN for N results for now, allow extractor result to be any product, not just tuple --- src/compiler/scala/tools/nsc/typechecker/Unapplies.scala | 5 +++++ test/files/run/virtpatmat_extends_product.check | 1 + test/files/run/virtpatmat_extends_product.flags | 1 + test/files/run/virtpatmat_extends_product.scala | 11 +++++++++++ 4 files changed, 18 insertions(+) create mode 100644 test/files/run/virtpatmat_extends_product.check create mode 100644 test/files/run/virtpatmat_extends_product.flags create mode 100644 test/files/run/virtpatmat_extends_product.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index cc272b7b8d..4f5b6868ae 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -45,6 +45,11 @@ trait Unapplies extends ast.TreeDSL case BooleanClass => Nil case OptionClass | SomeClass => val prod = tp.typeArgs.head +// the spec doesn't allow just any subtype of Product, it *must* be TupleN[...] -- see run/virtpatmat_extends_product.scala +// this breaks plenty of stuff, though... +// val targs = +// if (isTupleType(prod)) getProductArgs(prod) +// else List(prod) val targs = getProductArgs(prod) if (targs.isEmpty || targs.tail.isEmpty) List(prod) // special n == 0 || n == 1 diff --git a/test/files/run/virtpatmat_extends_product.check b/test/files/run/virtpatmat_extends_product.check new file mode 100644 index 0000000000..c07e8385a7 --- /dev/null +++ b/test/files/run/virtpatmat_extends_product.check @@ -0,0 +1 @@ +AnnotationInfo(a,1) diff --git a/test/files/run/virtpatmat_extends_product.flags b/test/files/run/virtpatmat_extends_product.flags new file mode 100644 index 0000000000..ac6b805bd0 --- /dev/null +++ b/test/files/run/virtpatmat_extends_product.flags @@ -0,0 +1 @@ +-Yvirtpatmat diff --git a/test/files/run/virtpatmat_extends_product.scala b/test/files/run/virtpatmat_extends_product.scala new file mode 100644 index 0000000000..e564f4430b --- /dev/null +++ b/test/files/run/virtpatmat_extends_product.scala @@ -0,0 +1,11 @@ +object Test extends App { + case class AnnotationInfo(a: String, b: Int) extends Product2[String, Int] + + // if we're not careful in unapplyTypeListFromReturnType, the generated unapply is + // thought to return two components instead of one, since AnnotationInfo (the result of the unapply) is a Product2 + case class NestedAnnotArg(ai: AnnotationInfo) + + NestedAnnotArg(AnnotationInfo("a", 1)) match { + case NestedAnnotArg(x) => println(x) + } +} \ No newline at end of file -- cgit v1.2.3 From 3e0f24d2e78aa3d8dace0b2b253dfe7870b330fe Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 12 Mar 2012 17:37:04 +0100 Subject: [vpm] label-based translation of matches --- .../tools/nsc/typechecker/PatMatVirtualiser.scala | 358 ++++++++++----------- 1 file changed, 173 insertions(+), 185 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index b060fd7121..552c236bde 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -43,7 +43,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val outer = newTermName("") val runOrElse = newTermName("runOrElse") val zero = newTermName("zero") - val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching... + val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching... def counted(str: String, i: Int) = newTermName(str+i) } @@ -177,7 +177,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => CaseDef( Bind(exSym, Ident(nme.WILDCARD)), // TODO: does this need fixing upping? EmptyTree, - combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, scrut => Throw(CODE.REF(exSym))) + combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, casegen => scrut => Throw(CODE.REF(exSym))) ) }) } @@ -733,23 +733,25 @@ class Foo(x: Other) { x._1 } // no error in this order private[this] var currSub: Substitution = null // build Tree that chains `next` after the current extractor - def chainBefore(next: Tree, pt: Type): Tree + def chainBefore(next: Tree)(casegen: Casegen): Tree } - case class TrivialTreeMaker(tree: Tree) extends TreeMaker { - val localSubstitution: Substitution = EmptySubstitution - def chainBefore(next: Tree, pt: Type): Tree = tree + trait NoNewBinders extends TreeMaker { + protected val localSubstitution: Substitution = EmptySubstitution } - case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker { - val localSubstitution: Substitution = EmptySubstitution - def chainBefore(next: Tree, pt: Type): Tree = // assert(next eq EmptyTree) - atPos(body.pos)(substitution(codegen.one(body, body.tpe, matchPt))) // since SubstOnly treemakers are dropped, need to do it here + case class TrivialTreeMaker(tree: Tree) extends TreeMaker with NoNewBinders { + def chainBefore(next: Tree)(casegen: Casegen): Tree = tree + } + + case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker with NoNewBinders { + def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(next eq EmptyTree) + atPos(body.pos)(casegen.one(substitution(body))) // since SubstOnly treemakers are dropped, need to do it here } case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker { val localSubstitution = Substitution(prevBinder, CODE.REF(nextBinder)) - def chainBefore(next: Tree, pt: Type): Tree = substitution(next) + def chainBefore(next: Tree)(casegen: Casegen): Tree = substitution(next) } abstract class FunTreeMaker extends TreeMaker { @@ -766,8 +768,8 @@ class Foo(x: Other) { x._1 } // no error in this order lazy val nextBinder = freshSym(pos, nextBinderTp) lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) - def chainBefore(next: Tree, pt: Type): Tree = - atPos(pos)(codegen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) + def chainBefore(next: Tree)(casegen: Casegen): Tree = + atPos(pos)(casegen.flatMapCond(cond, res, nextBinder, substitution(next))) } /** @@ -778,11 +780,11 @@ class Foo(x: Other) { x._1 } // no error in this order * in this function's body, and all the subsequent ones, references to the symbols in `from` will be replaced by the corresponding tree in `to` */ case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean) extends FunTreeMaker { - def chainBefore(next: Tree, pt: Type): Tree = { - val condAndNext = extraCond map (codegen.ifThenElseZero(_, next)) getOrElse next + def chainBefore(next: Tree)(casegen: Casegen): Tree = { + val condAndNext = extraCond map (casegen.ifThenElseZero(_, next)) getOrElse next atPos(extractor.pos)( - if (extractorReturnsBoolean) codegen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext)) - else codegen.flatMap(extractor, nextBinder, substitution(condAndNext)) + if (extractorReturnsBoolean) casegen.flatMapCond(extractor, CODE.UNIT, nextBinder, substitution(condAndNext)) + else casegen.flatMap(extractor, nextBinder, substitution(condAndNext)) ) } @@ -791,10 +793,10 @@ class Foo(x: Other) { x._1 } // no error in this order // TODO: allow user-defined unapplyProduct case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree], localSubstitution: Substitution) extends TreeMaker { import CODE._ - def chainBefore(next: Tree, pt: Type): Tree = { + def chainBefore(next: Tree)(casegen: Casegen): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL val cond = extraCond map (nullCheck AND _) getOrElse nullCheck - codegen.ifThenElseZero(cond, substitution(next)) + casegen.ifThenElseZero(cond, substitution(next)) } override def toString = "P"+(prevBinder, extraCond getOrElse "", localSubstitution) @@ -907,58 +909,30 @@ class Foo(x: Other) { x._1 } // no error in this order override def toString = "ET"+(prevBinder, patTree) } - case class AlternativesTreeMaker(prevBinder: Symbol, var altss: List[List[TreeMaker]], pos: Position) extends TreeMaker { + case class AlternativesTreeMaker(prevBinder: Symbol, var altss: List[List[TreeMaker]], pos: Position) extends TreeMaker with NoNewBinders { // don't substitute prevBinder to nextBinder, a set of alternatives does not need to introduce a new binder, simply reuse the previous one - val localSubstitution: Substitution = EmptySubstitution override private[TreeMakers] def incorporateOuterSubstitution(outerSubst: Substitution): Unit = { super.incorporateOuterSubstitution(outerSubst) altss = altss map (alts => propagateSubstitution(alts, substitution)) } - def chainBefore(next: Tree, pt: Type): Tree = { import CODE._ - // next does not contain deftrees, is pretty short - val canDuplicate = { - var okToInline = true - var sizeBudget = 100 / (altss.length max 1) // yep, totally arbitrary! - object travOkToInline extends Traverser { override def traverse(tree: Tree): Unit = if (sizeBudget >= 0) { sizeBudget -= 1; tree match { - case TypeApply(_, _) | Apply(_, _) | Select(_, _) - | Block(_, _) | Assign(_, _) | If(_, _, _) | Typed(_, _) => super.traverse(tree) // these are allowed if their subtrees are - case EmptyTree | This(_) | New(_) | Literal(_) | Ident(_) => // these are always ok - case _ if tree.isType => // these are always ok - case _ => okToInline = false //; println("not inlining: "+ (tree, tree.getClass)) - }}} - travOkToInline.traverse(next) - // println("(okToInline, sizeBudget): "+ (okToInline, sizeBudget)) - okToInline && sizeBudget > 0 // must be strict comparison - } + def chainBefore(next: Tree)(codegenAlt: Casegen): Tree = { import CODE._ + atPos(pos){ + // one alternative may still generate multiple trees (e.g., an extractor call + equality test) + // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers + val combinedAlts = altss map (altTreeMakers => + ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE)))(casegen)) + ) - atPos(pos)( - if (canDuplicate) { - altss map {altTreeMakers => - combineExtractors(altTreeMakers :+ TrivialTreeMaker(substitution(next).duplicate), pt) - } reduceLeft codegen.typedOrElse(pt) - } else { - val rest = freshSym(pos, functionType(List(), inMatchMonad(pt)), "rest") - // rest.info.member(nme.apply).withAnnotation(AnnotationInfo(ScalaInlineClass.tpe, Nil, Nil)) - - // one alternative may still generate multiple trees (e.g., an extractor call + equality test) - // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers - val combinedAlts = altss map (altTreeMakers => - combineExtractors(altTreeMakers :+ TrivialTreeMaker(REF(rest) APPLY ()), pt) - ) - BLOCK( - VAL(rest) === Function(Nil, substitution(next)), - combinedAlts reduceLeft codegen.typedOrElse(pt) - ) - } - ) + val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some((casegen: Casegen) => x => casegen.one(FALSE))) + codegenAlt.ifThenElseZero(findAltMatcher, substitution(next)) + } } } - case class GuardTreeMaker(guardTree: Tree) extends TreeMaker { - val localSubstitution: Substitution = EmptySubstitution - def chainBefore(next: Tree, pt: Type): Tree = codegen.flatMapGuard(substitution(guardTree), next) + case class GuardTreeMaker(guardTree: Tree) extends TreeMaker with NoNewBinders { + def chainBefore(next: Tree)(casegen: Casegen): Tree = casegen.flatMapGuard(substitution(guardTree), next) override def toString = "G("+ guardTree +")" } @@ -978,49 +952,40 @@ class Foo(x: Other) { x._1 } // no error in this order // calls propagateSubstitution on the treemakers def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = { val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them - combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, CODE.MATCHERROR(_)) + combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, casegen => CODE.MATCHERROR(_)) } - def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFail: Tree => Tree): Tree = fixerUpper(owner, scrut.pos){ - emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt).getOrElse{ - val (matcher, hasDefault, toHoist) = - if (casesNoSubstOnly nonEmpty) { - // when specified, need to propagate pt explicitly (type inferencer can't handle it) - val optPt = - if (isFullyDefined(pt)) inMatchMonad(pt) - else NoType - - // do this check on casesNoSubstOnly, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one - // exhaustivity and reachability must be checked before optimization as well - // TODO: improve, a trivial type test before the body still makes for a default case - // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) - val hasDefault = casesNoSubstOnly.nonEmpty && { - val nonTrivLast = casesNoSubstOnly.last - nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] - } - - val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) - - val combinedCases = - cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt)) + def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFail: Casegen => Tree => Tree): Tree = fixerUpper(owner, scrut.pos){ + val ptDefined = if (isFullyDefined(pt)) pt else NoType - (combinedCases, hasDefault, toHoist) - } else (codegen.zero, false, Nil) - - // catch-all - val catchAll = - if (hasDefault) None // no need for a catch-all when there's already a default - else Some(matchFail) - val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, catchAll) - if (toHoist isEmpty) expr - else Block(toHoist, expr) + emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt).getOrElse{ + if (casesNoSubstOnly nonEmpty) { + // check casesNoSubstOnly for presence of a default case, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one + // exhaustivity and reachability must be checked before optimization as well + // TODO: improve, a trivial type test before the body still makes for a default case + // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) + val catchAll = + if (casesNoSubstOnly.nonEmpty && { + val nonTrivLast = casesNoSubstOnly.last + nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] + }) None + else Some(matchFail) + + val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) + + val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases map combineExtractors, catchAll) + + if (toHoist isEmpty) matchRes else Block(toHoist, matchRes) + } else { + codegen.matcher(scrut, scrutSym, pt)(Nil, Some(matchFail)) + } } } // combineExtractors changes the current substitution's of the tree makers in `treeMakers` // requires propagateSubstitution(treeMakers) has been called - def combineExtractors(treeMakers: List[TreeMaker], pt: Type): Tree = - treeMakers.foldRight (EmptyTree: Tree) (_.chainBefore(_, pt)) + def combineExtractors(treeMakers: List[TreeMaker])(casegen: Casegen): Tree = + treeMakers.foldRight(EmptyTree: Tree)((a, b) => a.chainBefore(b)(casegen)) // TODO: do this during tree construction, but that will require tracking the current owner in treemakers // TODO: assign more fine-grained positions @@ -1070,34 +1035,36 @@ class Foo(x: Other) { x._1 } // no error in this order /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// trait CodegenCore extends MatchMonadInterface { private var ctr = 0 - def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = {ctr += 1; - // assert(owner ne null) - // assert(owner ne NoSymbol) - NoSymbol.newTermSymbol(vpmName.counted(prefix, ctr), pos) setInfo repackExistential(tp) - } + def freshName(prefix: String) = {ctr += 1; vpmName.counted(prefix, ctr)} + + // assert(owner ne null); assert(owner ne NoSymbol) + def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = + NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo repackExistential(tp) // codegen relevant to the structure of the translation (how extractors are combined) trait AbsCodegen { - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree - def zero: Tree - def flatMap(prev: Tree, b: Symbol, next: Tree): Tree - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], catchAllGen: Option[Casegen => Tree => Tree]): Tree - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree - def flatMapGuard(cond: Tree, next: Tree): Tree - - def fun(arg: Symbol, body: Tree): Tree - def ifThenElseZero(c: Tree, then: Tree): Tree - def _equals(checker: Tree, binder: Symbol): Tree + // local / context-free def _asInstanceOf(b: Symbol, tp: Type): Tree + def _equals(checker: Tree, binder: Symbol): Tree + def _isInstanceOf(b: Symbol, tp: Type): Tree + def and(a: Tree, b: Tree): Tree + def drop(tgt: Tree)(n: Int): Tree + def index(tgt: Tree)(i: Int): Tree def mkZero(tp: Type): Tree - def tupleSel(binder: Symbol)(i: Int): Tree - def index(tgt: Tree)(i: Int): Tree - def drop(tgt: Tree)(n: Int): Tree - def and(a: Tree, b: Tree): Tree - def _isInstanceOf(b: Symbol, tp: Type): Tree + } + + // structure + trait Casegen extends AbsCodegen { import CODE._ + def one(res: Tree): Tree + + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree + def flatMapGuard(cond: Tree, next: Tree): Tree + def ifThenElseZero(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero + protected def zero: Tree } def codegen: AbsCodegen @@ -1112,7 +1079,6 @@ class Foo(x: Other) { x._1 } // no error in this order def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n)) def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya def and(a: Tree, b: Tree): Tree = a AND b - def ifThenElseZero(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = repackExistential(tp) @@ -1166,28 +1132,29 @@ class Foo(x: Other) { x._1 } // no error in this order trait PureCodegen extends CodegenCore with PureMatchMonadInterface { def codegen: AbsCodegen = pureCodegen - object pureCodegen extends CommonCodegen { import CODE._ + object pureCodegen extends CommonCodegen with Casegen { import CODE._ //// methods in MatchingStrategy (the monad companion) -- used directly in translation // __match.runOrElse(`scrut`)(`scrutSym` => `matcher`) // TODO: consider catchAll, or virtualized matching will break in exception handlers - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree - = _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher)) + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], catchAllGen: Option[Casegen => Tree => Tree]): Tree = + _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, cases map (f => f(this)) reduceLeft typedOrElse)) + // __match.one(`res`) - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (_match(vpmName.one)) (res) + def one(res: Tree): Tree = (_match(vpmName.one)) (res) // __match.zero def zero: Tree = _match(vpmName.zero) // __match.guard(`c`, `then`) - def guard(c: Tree, then: Tree, tp: Type): Tree = _match(vpmName.guard) APPLY (c, then) + def guard(c: Tree, then: Tree): Tree = _match(vpmName.guard) APPLY (c, then) //// methods in the monad instance -- used directly in translation // `prev`.flatMap(`b` => `next`) def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = (prev DOT vpmName.flatMap)(fun(b, next)) // `thisCase`.orElse(`elseCase`) - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (thisCase DOT vpmName.orElse) APPLY (elseCase) + def typedOrElse(thisCase: Tree, elseCase: Tree): Tree = (thisCase DOT vpmName.orElse) APPLY (elseCase) // __match.guard(`cond`, `res`).flatMap(`nextBinder` => `next`) - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), nextBinder, next) + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = flatMap(guard(cond, res), nextBinder, next) // __match.guard(`guardTree`, ()).flatMap((_: P[Unit]) => `next`) - def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, pureType(UnitClass.tpe)), pureType(UnitClass.tpe), next) + def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, pureType(UnitClass.tpe)), next) } } @@ -1456,8 +1423,8 @@ class Foo(x: Other) { x._1 } // no error in this order } // TODO: finer-grained duplication - def chainBefore(next: Tree, pt: Type): Tree = // assert(codegen eq optimizedCodegen) - atPos(pos)(optimizedCodegen.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) + def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(codegen eq optimizedCodegen) + atPos(pos)(casegen.asInstanceOf[optimizedCodegen.OptimizedCasegen].flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) } case class ReusingCondTreeMaker(sharedPrefix: List[Test], toReused: TreeMaker => TreeMaker) extends TreeMaker { import CODE._ @@ -1474,12 +1441,11 @@ class Foo(x: Other) { x._1 } // no error in this order oldSubs.foldLeft(Substitution(from, to))(_ >> _) } - def chainBefore(next: Tree, pt: Type): Tree = { + def chainBefore(next: Tree)(casegen: Casegen): Tree = { val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: ReusedCondTreeMaker)) => ctm}.get.storedCond) - IF (cond) THEN BLOCK( - substitution(next).duplicate // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) - ) ELSE codegen.zero + // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S) + casegen.ifThenElseZero(cond, substitution(next).duplicate) } } } @@ -1657,8 +1623,7 @@ class Foo(x: Other) { x._1 } // no error in this order // for example, `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard // this is a special instance of the advanced inlining optimization that takes a method call on // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases - object optimizedCodegen extends CommonCodegen /*with AbsOptimizedCodegen*/ { import CODE._ - lazy val zeroSym = freshSym(NoPosition, optionType(NothingClass.tpe), "zero") + object optimizedCodegen extends CommonCodegen { import CODE._ /** Inline runOrElse and get rid of Option allocations * @@ -1666,67 +1631,90 @@ class Foo(x: Other) { x._1 } // no error in this order * the matcher's optional result is encoded as a flag, keepGoing, where keepGoing == true encodes result.isEmpty, * if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x */ - @inline private def dontStore(tp: Type) = (tp.typeSymbol eq UnitClass) || (tp.typeSymbol eq NothingClass) - lazy val keepGoing = freshSym(NoPosition, BooleanClass.tpe, "keepGoing") setFlag MUTABLE - lazy val matchRes = freshSym(NoPosition, AnyClass.tpe, "matchRes") setFlag MUTABLE - def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]) = { - matchRes.info = if (resTp ne NoType) resTp.widen else AnyClass.tpe // we don't always know resTp, and it might be AnyVal, in which case we can't assign NULL - if (dontStore(resTp)) matchRes resetFlag MUTABLE // don't assign to Unit-typed var's, in fact, make it a val -- conveniently also works around SI-5245 - BLOCK( - VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite... - VAL(scrutSym) === scrut, - VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above - VAL(keepGoing) === TRUE, - matcher, - catchAll map { catchAllGen => (IF (REF(keepGoing)) THEN catchAllGen(REF(scrutSym)) ELSE REF(matchRes)) } getOrElse REF(matchRes) - ) - } + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], catchAllGen: Option[Casegen => Tree => Tree]): Tree = { + val matchEnd = NoSymbol.newLabel(freshName("matchEnd"), NoPosition) setFlag (SYNTHETIC | Flags.CASE) + val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe + matchEnd setInfo MethodType(List(matchRes), restpe) + + def newCaseSym = NoSymbol.newLabel(freshName("case"), NoPosition) setInfo MethodType(Nil, restpe) setFlag (SYNTHETIC | Flags.CASE) + var nextCase = newCaseSym + def caseDef(mkCase: Casegen => Tree): Tree = { + val currCase = nextCase + nextCase = newCaseSym + val casegen = new OptimizedCasegen(matchEnd, nextCase) + LabelDef(currCase, Nil, mkCase(casegen)) + } - // only used to wrap the RHS of a body - def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = { - BLOCK( - REF(keepGoing) === FALSE, // comes before assignment to matchRes, so the latter is in tail positions (can ignore the trailing zero -- will disappear when we flatten blocks, which is TODO) - if (dontStore(matchPt)) res else (REF(matchRes) === res), // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse... - zero // to have a nice lub for lubs -- otherwise we'll get a boxed unit here -- TODO: get rid of all those dangling else zero's + def catchAll = catchAllGen map { catchAllGen => + val casegen = new OptimizedCasegen(matchEnd, NoSymbol) + val scrutRef = if(scrutSym eq NoSymbol) EmptyTree else REF(scrutSym) + LabelDef(nextCase, Nil, catchAllGen(casegen)(scrutRef)) + } toList + + val prologue = if(scrutSym ne NoSymbol) List(VAL(scrutSym) === scrut) else Nil + Block( + prologue ++ (cases map caseDef) ++ catchAll, + LabelDef(matchEnd, List(matchRes), REF(matchRes)) ) } - def zero: Tree = REF(zeroSym) + class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol) extends CommonCodegen with Casegen { + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], catchAllGen: Option[Casegen => Tree => Tree]): Tree = + optimizedCodegen.matcher(scrut, scrutSym, restpe)(cases, catchAllGen) - def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { - val tp = inMatchMonad(b.tpe) - val prevSym = freshSym(prev.pos, tp, "o") - val isEmpty = tp member vpmName.isEmpty - val get = tp member vpmName.get + def zero: Tree = nextCase APPLY () - BLOCK( - VAL(prevSym) === prev, - IF (prevSym DOT isEmpty) THEN zero ELSE Substitution(b, prevSym DOT get)(next) // must be isEmpty and get as we don't control the target of the call (could be the result of a user-defined extractor) - ) - } + // only used to wrap the RHS of a body + // res: T + // returns MatchMonad[T] + def one(res: Tree): Tree = matchEnd APPLY (res) - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = { - BLOCK( - thisCase, - IF (REF(keepGoing)) THEN elseCase ELSE zero // leave trailing zero for now, otherwise typer adds () anyway - ) + + override def ifThenElseZero(c: Tree, then: Tree): Tree = + IF (c) THEN then ELSE zero + + // prev: MatchMonad[T] + // b: T + // next: MatchMonad[U] + // returns MatchMonad[U] + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { + val tp = inMatchMonad(b.tpe) + val prevSym = freshSym(prev.pos, tp, "o") + val isEmpty = tp member vpmName.isEmpty + val get = tp member vpmName.get + + BLOCK( + VAL(prevSym) === prev, + // must be isEmpty and get as we don't control the target of the call (prev is an extractor call) + ifThenElseZero(NOT(prevSym DOT isEmpty), Substitution(b, prevSym DOT get)(next)) + ) + } + + // cond: Boolean + // res: T + // nextBinder: T + // next == MatchMonad[U] + // returns MatchMonad[U] + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = + ifThenElseZero(cond, BLOCK( + VAL(nextBinder) === res, + next + )) + + // guardTree: Boolean + // next: MatchMonad[T] + // returns MatchMonad[T] + def flatMapGuard(guardTree: Tree, next: Tree): Tree = + ifThenElseZero(guardTree, next) + + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = + ifThenElseZero(cond, BLOCK( + condSym === TRUE, + nextBinder === res, + next + )) } - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree = - IF (cond) THEN BLOCK( - VAL(nextBinder) === res, - next - ) ELSE zero - - def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = - IF (cond) THEN BLOCK( - condSym === TRUE, - nextBinder === res, - next - ) ELSE zero - - def flatMapGuard(guardTree: Tree, next: Tree): Tree = - IF (guardTree) THEN next ELSE zero } } -- cgit v1.2.3 From cd3d342032613e52e5917f3900f2461536a54e26 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 15 Mar 2012 19:07:15 +0100 Subject: [vpm] tailcalls support for jumpy vpm --- .../scala/tools/nsc/transform/TailCalls.scala | 126 +++++++++++++++++++-- .../tools/nsc/typechecker/PatMatVirtualiser.scala | 5 + 2 files changed, 119 insertions(+), 12 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index fdb5c7e52e..6ebecb02c6 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -36,6 +36,8 @@ abstract class TailCalls extends Transform { } } + private def hasSynthCaseSymbol(t: Tree) = (t.symbol ne null) && (t.symbol hasFlag (Flags.CASE | Flags.SYNTHETIC)) + /** * A Tail Call Transformer * @@ -87,10 +89,22 @@ abstract class TailCalls extends Transform { class TailCallElimination(unit: CompilationUnit) extends Transformer { private val defaultReason = "it contains a recursive call not in tail position" + /** Has the label been accessed? Then its symbol is in this set. */ + private val accessed = new collection.mutable.HashSet[Symbol]() + // `accessed` was stored as boolean in the current context -- this is no longer tenable + // with jumps to labels in tailpositions now considered in tailposition, + // a downstream context may access the label, and the upstream one will be none the wiser + // this is necessary because tail-calls may occur in places where syntactically they seem impossible + // (since we now consider jumps to labels that are in tailposition, such as matchEnd(x) {x}) + + class Context() { /** The current method */ var method: Symbol = NoSymbol + // symbols of label defs in this method that are in tail position + var tailLabels: Set[Symbol] = Set() + /** The current tail-call label */ var label: Symbol = NoSymbol @@ -104,24 +118,20 @@ abstract class TailCalls extends Transform { var failReason = defaultReason var failPos = method.pos - /** Has the label been accessed? */ - var accessed = false - def this(that: Context) = { this() this.method = that.method this.tparams = that.tparams this.tailPos = that.tailPos - this.accessed = that.accessed this.failPos = that.failPos this.label = that.label + this.tailLabels = that.tailLabels } def this(dd: DefDef) { this() this.method = dd.symbol this.tparams = dd.tparams map (_.symbol) this.tailPos = true - this.accessed = false this.failPos = dd.pos /** Create a new method symbol for the current method and store it in @@ -141,14 +151,14 @@ abstract class TailCalls extends Transform { def isEligible = method.isEffectivelyFinal // @tailrec annotation indicates mandatory transformation def isMandatory = method.hasAnnotation(TailrecClass) && !forMSIL - def isTransformed = isEligible && accessed + def isTransformed = isEligible && accessed(label) def tailrecFailure() = unit.error(failPos, "could not optimize @tailrec annotated " + method + ": " + failReason) def newThis(pos: Position) = method.newValue(nme.THIS, pos, SYNTHETIC) setInfo currentClass.typeOfThis override def toString(): String = ( "" + method.name + " tparams: " + tparams + " tailPos: " + tailPos + - " accessed: " + accessed + "\nLabel: " + label + "\nLabel type: " + label.info + " Label: " + label + " Label type: " + label.info ) } @@ -206,7 +216,7 @@ abstract class TailCalls extends Transform { def rewriteTailCall(recv: Tree): Tree = { debuglog("Rewriting tail recursive call: " + fun.pos.lineContent.trim) - ctx.accessed = true + accessed += ctx.label typedPos(fun.pos)(Apply(Ident(ctx.label), recv :: transformArgs)) } @@ -242,10 +252,16 @@ abstract class TailCalls extends Transform { unit.error(tree.pos, "@tailrec annotated method contains no recursive calls") } } - debuglog("Considering " + dd.name + " for tailcalls") + + // labels are local to a method, so only traverse the rhs of a defdef + val collectTailPosLabels = new TailPosLabelsTraverser + collectTailPosLabels traverse rhs0 + newCtx.tailLabels = collectTailPosLabels.tailLabels.toSet + + debuglog("Considering " + dd.name + " for tailcalls, with labels in tailpos: "+ newCtx.tailLabels) val newRHS = transform(rhs0, newCtx) - deriveDefDef(tree)(rhs => + deriveDefDef(tree){rhs => if (newCtx.isTransformed) { /** We have rewritten the tree, but there may be nested recursive calls remaining. * If @tailrec is given we need to fail those now. @@ -270,8 +286,22 @@ abstract class TailCalls extends Transform { newRHS } + } + + // a translated match + case Block(stats, expr) if stats forall hasSynthCaseSymbol => + // the assumption is once we encounter a case, the remainder of the block will consist of cases + // the prologue may be empty, usually it is the valdef that stores the scrut + val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + treeCopy.Block(tree, + noTailTransforms(prologue) ++ transformTrees(cases), + transform(expr) ) + // a translated casedef + case LabelDef(_, _, body) if hasSynthCaseSymbol(tree) => + deriveLabelDef(tree)(transform) + case Block(stats, expr) => treeCopy.Block(tree, noTailTransforms(stats), @@ -308,8 +338,18 @@ abstract class TailCalls extends Transform { case Apply(fun, args) => if (fun.symbol == Boolean_or || fun.symbol == Boolean_and) treeCopy.Apply(tree, fun, transformTrees(args)) - else - rewriteApply(fun, fun, Nil, args) + else if (fun.symbol.isLabel && args.nonEmpty && args.tail.isEmpty && ctx.tailLabels(fun.symbol)) { + // this is to detect tailcalls in translated matches + // it's a one-argument call to a label that is in a tailposition and that looks like label(x) {x} + // thus, the argument to the call is in tailposition and we don't need to jump to the label, tail jump instead + val saved = ctx.tailPos + ctx.tailPos = true + debuglog("in tailpos label: "+ args.head) + val res = transform(args.head) + ctx.tailPos = saved + if (res ne args.head) res // we tail-called -- TODO: shield from false-positives where we rewrite but don't tail-call + else rewriteApply(fun, fun, Nil, args) + } else rewriteApply(fun, fun, Nil, args) case Alternative(_) | Star(_) | Bind(_, _) => sys.error("We should've never gotten inside a pattern") @@ -320,4 +360,66 @@ abstract class TailCalls extends Transform { } } } + + // collect the LabelDefs (generated by the pattern matcher) in a DefDef that are in tail position + // the labels all look like: matchEnd(x) {x} + // then, in a forward jump `matchEnd(expr)`, `expr` is considered in tail position (and the matchEnd jump is replaced by the jump generated by expr) + class TailPosLabelsTraverser extends Traverser { + val tailLabels = new collection.mutable.ListBuffer[Symbol]() + + private var maybeTail: Boolean = true // since we start in the rhs of a DefDef + + def traverse(tree: Tree, maybeTailNew: Boolean): Unit = { + val saved = maybeTail + maybeTail = maybeTailNew + try traverse(tree) + finally maybeTail = saved + } + + def traverseNoTail(tree: Tree) = traverse(tree, false) + def traverseTreesNoTail(trees: List[Tree]) = trees foreach traverseNoTail + + override def traverse(tree: Tree) = tree match { + case LabelDef(_, List(arg), body@Ident(_)) if arg.symbol == body.symbol => // we're looking for label(x){x} in tail position, since that means `a` is in tail position in a call `label(a)` + if (maybeTail) tailLabels += tree.symbol + + // a translated casedef + case LabelDef(_, _, body) if hasSynthCaseSymbol(tree) => + traverse(body) + + // a translated match + case Block(stats, expr) if stats forall hasSynthCaseSymbol => + // the assumption is once we encounter a case, the remainder of the block will consist of cases + // the prologue may be empty, usually it is the valdef that stores the scrut + val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + traverseTreesNoTail(prologue) // selector (may be absent) + traverseTrees(cases) + traverse(expr) + + case CaseDef(pat, guard, body) => + traverse(body) + + case Match(selector, cases) => + traverseNoTail(selector) + traverseTrees(cases) + + case dd @ DefDef(_, _, _, _, _, _) => // we are run per-method + + case Block(stats, expr) => + traverseTreesNoTail(stats) + traverse(expr) + + case If(cond, thenp, elsep) => + traverse(thenp) + traverse(elsep) + + case Try(block, catches, finalizer) => + traverseNoTail(block) + traverseTreesNoTail(catches) + traverseNoTail(finalizer) + + case EmptyTree | Super(_, _) | This(_) | Select(_, _) | Ident(_) | Literal(_) | Function(_, _) | TypeTree() => + case _ => super.traverse(tree) + } + } } diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 552c236bde..0422da54e0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -1651,6 +1651,11 @@ class Foo(x: Other) { x._1 } // no error in this order LabelDef(nextCase, Nil, catchAllGen(casegen)(scrutRef)) } toList + // the generated block is taken apart in TailCalls under the following assumptions + // the assumption is once we encounter a case, the remainder of the block will consist of cases + // the prologue may be empty, usually it is the valdef that stores the scrut + // val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) + val prologue = if(scrutSym ne NoSymbol) List(VAL(scrutSym) === scrut) else Nil Block( prologue ++ (cases map caseDef) ++ catchAll, -- cgit v1.2.3 From 2d3b309a36757545901b99da1b77698749fcc0c5 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 16 Mar 2012 11:55:44 +0100 Subject: specialize symbols for labeldef params --- src/compiler/scala/tools/nsc/typechecker/Duplicators.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 29831c8469..eb0d489901 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -256,8 +256,16 @@ abstract class Duplicators extends Analyzer { case ldef @ LabelDef(name, params, rhs) => // log("label def: " + ldef) ldef.tpe = null - val params1 = params map (p => Ident(updateSym(p.symbol))) - super.typed(treeCopy.LabelDef(tree, name, params1, rhs), mode, pt) + // since typer does not create the symbols for a LabelDef's params, + // we do that manually here -- we should really refactor LabelDef to be a subclass of DefDef + def newParam(p: Tree): Ident = { + val newsym = p.symbol.cloneSymbol //(context.owner) // TODO owner? + Ident(newsym.setInfo(fixType(p.symbol.info))) + } + val params1 = params map newParam + val rhs1 = (new TreeSubstituter(params map (_.symbol), params1) transform rhs) // TODO: duplicate? + rhs1.tpe = null + super.typed(treeCopy.LabelDef(tree, name, params1, rhs1), mode, pt) case Bind(name, _) => // log("bind: " + tree) -- cgit v1.2.3 From b046a6e3316df8b27ac31e71da1a139c800ccce7 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 14 Mar 2012 11:47:59 +0100 Subject: [vpm] typer synths Function{} for empty-sel match typedMatchAnonFun is called from typedFunction when the function's body is a match this is work-in-progres: the compiler currently won't bootstrap under -Yvirtpatmat see also the pending test make sure to use the right context in typeFunction when the body is a Match when typer is set up for type checking a Function, the current owner is the symbol for the function, but we'll type check a Block(List(ClassDef(cd)), New(cd)) when the function is a match, and the function symbol is nowhere to be found, so go to outer context in patmatvirt: - simplified default case gen (no need for a Casegen instance) - using CASE | SYNTHETIC to detect generated matches (for switches) and avoid typing them endlessly more uniform, and necessary for new-style anon Function class instance gen for matches --- src/compiler/scala/reflect/internal/Trees.scala | 5 +- .../scala/tools/nsc/transform/UnCurry.scala | 86 +++---- .../tools/nsc/typechecker/PatMatVirtualiser.scala | 141 ++++++----- .../scala/tools/nsc/typechecker/Typers.scala | 210 +++++++++++++---- test/files/pos/virtpatmat_anonfun_for.flags | 1 + test/files/pos/virtpatmat_anonfun_for.scala | 8 + test/files/run/virtpatmat_partial.check | 17 +- test/files/run/virtpatmat_partial.scala | 257 ++++++++++++++------- .../run/virtpatmat_anonfun_underscore.check | 0 .../run/virtpatmat_anonfun_underscore.flags | 1 + .../run/virtpatmat_anonfun_underscore.scala | 4 + 11 files changed, 481 insertions(+), 249 deletions(-) create mode 100644 test/files/pos/virtpatmat_anonfun_for.flags create mode 100644 test/files/pos/virtpatmat_anonfun_for.scala create mode 100644 test/pending/run/virtpatmat_anonfun_underscore.check create mode 100644 test/pending/run/virtpatmat_anonfun_underscore.flags create mode 100644 test/pending/run/virtpatmat_anonfun_underscore.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index 9b1712b790..1a40e0105c 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -350,8 +350,9 @@ trait Trees extends api.Trees { self: SymbolTable => "subst[%s, %s](%s)".format(fromStr, toStr, (from, to).zipped map (_ + " -> " + _) mkString ", ") } - // NOTE: if symbols in `from` occur multiple times in the `tree` passed to `transform`, - // the resulting Tree will be a graph, not a tree... this breaks all sorts of stuff, + // NOTE: calls shallowDuplicate on trees in `to` to avoid problems when symbols in `from` + // occur multiple times in the `tree` passed to `transform`, + // otherwise, the resulting Tree would be a graph, not a tree... this breaks all sorts of stuff, // notably concerning the mutable aspects of Trees (such as setting their .tpe) class TreeSubstituter(from: List[Symbol], to: List[Tree]) extends Transformer { override def transform(tree: Tree): Tree = tree match { diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index ee565530b7..03bef83a90 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -237,8 +237,10 @@ abstract class UnCurry extends InfoTransform def targs = fun.tpe.typeArgs def isPartial = fun.tpe.typeSymbol == PartialFunctionClass + // if the function was eta-expanded, it's not a match without a selector if (fun1 ne fun) fun1 else { + assert(!(opt.virtPatmat && isPartial)) // empty-selector matches have already been translated into instantiations of anonymous (partial) functions val (formals, restpe) = (targs.init, targs.last) val anonClass = owner.newAnonymousFunctionClass(fun.pos, inConstructorFlag) def parents = @@ -286,52 +288,54 @@ abstract class UnCurry extends InfoTransform def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) - val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) +// val casesNoSynthCatchAll = dropSyntheticCatchAll(cases) gen.mkUncheckedMatch( - if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) Literal(Constant(true)) - else substTree(wrap(Match(selector, (casesNoSynthCatchAll map transformCase) :+ defaultCase)).duplicate) + if (cases exists treeInfo.isDefaultCase) Literal(Constant(true)) + else substTree(wrap(Match(selector, (cases map transformCase) :+ defaultCase)).duplicate) ) } - override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { - object noOne extends Transformer { - override val treeCopy = newStrictTreeCopier // must duplicate everything - val one = _match.tpe member newTermName("one") - override def transform(tree: Tree): Tree = tree match { - case Apply(fun, List(a)) if fun.symbol == one => - // blow one's argument away since all we want to know is whether the match succeeds or not - // (the alternative, making `one` CBN, would entail moving away from Option) - Apply(fun.duplicate, List(gen.mkZeroContravariantAfterTyper(a.tpe))) - case _ => - super.transform(tree) - } - } - substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher)))) - } - - override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = { - object dropMatchResAssign extends Transformer { - // override val treeCopy = newStrictTreeCopier // will duplicate below - override def transform(tree: Tree): Tree = tree match { - // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing - case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol => - Block(List(assignKeepGoing), zero) - case _ => - super.transform(tree) - } - } - val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList - val idaBlock = wrap(Block( - zero :: - x :: - /* drop matchRes def */ - keepGoing :: - statsNoMatchRes, - NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing` - )) - substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed - } + override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = {assert(false); orig} + // { + // object noOne extends Transformer { + // override val treeCopy = newStrictTreeCopier // must duplicate everything + // val one = _match.tpe member newTermName("one") + // override def transform(tree: Tree): Tree = tree match { + // case Apply(fun, List(a)) if fun.symbol == one => + // // blow one's argument away since all we want to know is whether the match succeeds or not + // // (the alternative, making `one` CBN, would entail moving away from Option) + // Apply(fun.duplicate, List(gen.mkZeroContravariantAfterTyper(a.tpe))) + // case _ => + // super.transform(tree) + // } + // } + // substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher)))) + // } + + override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = {assert(false); orig} + // { + // object dropMatchResAssign extends Transformer { + // // override val treeCopy = newStrictTreeCopier // will duplicate below + // override def transform(tree: Tree): Tree = tree match { + // // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing + // case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol => + // Block(List(assignKeepGoing), zero) + // case _ => + // super.transform(tree) + // } + // } + // val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList + // val idaBlock = wrap(Block( + // zero :: + // x :: + // /* drop matchRes def */ + // keepGoing :: + // statsNoMatchRes, + // NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing` + // )) + // substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed + // } } DefDef(m, isDefinedAtTransformer(fun.body)) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 0422da54e0..34fefd20fe 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -49,7 +49,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => } object MatchTranslator { - def apply(typer: Typer): MatchTranslation = { + def apply(typer: Typer): MatchTranslation with CodegenCore = { import typer._ // typing `_match` to decide which MatchTranslator to create adds 4% to quick.comp.timer newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { @@ -116,10 +116,6 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore => import typer.{typed, context, silent, reallyExists} - private def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) - case _ => tp - } /** Implement a pattern match by turning its cases (including the implicit failure case) * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. @@ -131,18 +127,15 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => * thus, you must typecheck the result (and that will in turn translate nested matches) * this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch) */ - def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type): Tree = { + def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type, scrutType: Type, matchFailGenOverride: Option[Tree => Tree] = None): Tree = { // we don't transform after typers // (that would require much more sophistication when generating trees, // and the only place that emits Matches after typers is for exception handling anyway) assert(phase.id <= currentRun.typerPhase.id, phase) - val scrutType = repeatedToSeq(elimAnonymousClass(scrut.tpe.widen)) - - val scrutSym = freshSym(scrut.pos, pureType(scrutType)) - val okPt = repeatedToSeq(pt) + val scrutSym = freshSym(scrut.pos, pureType(scrutType)) setFlag (Flags.CASE | SYNTHETIC) // the flags allow us to detect generated matches by looking at the scrutinee's symbol (needed to avoid recursing endlessly on generated switches) // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental - combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, matchOwner) + combineCases(scrut, scrutSym, cases map translateCase(scrutSym, pt), pt, matchOwner, matchFailGenOverride) } // return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard) @@ -154,13 +147,12 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => // if they're already simple enough to be handled by the back-end, we're done if (caseDefs forall treeInfo.isCatchCase) caseDefs else { - val okPt = repeatedToSeq(pt) val switch = { val bindersAndCases = caseDefs map { caseDef => // generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there) // if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this) val caseScrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) - (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, okPt)(caseDef), EmptySubstitution)) + (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, pt)(caseDef), EmptySubstitution)) } (emitTypeSwitch(bindersAndCases, pt) map (_.map(fixerUpper(matchOwner, pos).apply(_).asInstanceOf[CaseDef]))) @@ -168,7 +160,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => val catches = switch getOrElse { val scrutSym = freshSym(pos, pureType(ThrowableClass.tpe)) - val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, okPt)(caseDef), EmptySubstitution))} + val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, pt)(caseDef), EmptySubstitution))} val exSym = freshSym(pos, pureType(ThrowableClass.tpe), "ex") @@ -177,7 +169,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => CaseDef( Bind(exSym, Ident(nme.WILDCARD)), // TODO: does this need fixing upping? EmptyTree, - combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, casegen => scrut => Throw(CODE.REF(exSym))) + combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, Some(scrut => Throw(CODE.REF(exSym)))) ) }) } @@ -706,10 +698,10 @@ class Foo(x: Other) { x._1 } // no error in this order def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = (cases, Nil) - def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = + def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree]): Option[Tree] = None - // for catch + // for catch (no need to customize match failure) def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = None @@ -925,7 +917,7 @@ class Foo(x: Other) { x._1 } // no error in this order ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE)))(casegen)) ) - val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some((casegen: Casegen) => x => casegen.one(FALSE))) + val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanClass.tpe)(combinedAlts, Some(x => FALSE)) codegenAlt.ifThenElseZero(findAltMatcher, substitution(next)) } } @@ -936,6 +928,12 @@ class Foo(x: Other) { x._1 } // no error in this order override def toString = "G("+ guardTree +")" } + // combineExtractors changes the current substitution's of the tree makers in `treeMakers` + // requires propagateSubstitution(treeMakers) has been called + def combineExtractors(treeMakers: List[TreeMaker])(casegen: Casegen): Tree = + treeMakers.foldRight(EmptyTree: Tree)((a, b) => a.chainBefore(b)(casegen)) + + def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker]) // a foldLeft to accumulate the localSubstitution left-to-right @@ -950,42 +948,42 @@ class Foo(x: Other) { x._1 } // no error in this order } // calls propagateSubstitution on the treemakers - def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = { - val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them - combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, casegen => CODE.MATCHERROR(_)) + def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Tree => Tree]): Tree = { + // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them + val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) + combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, matchFailGenOverride) } - def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFail: Casegen => Tree => Tree): Tree = fixerUpper(owner, scrut.pos){ - val ptDefined = if (isFullyDefined(pt)) pt else NoType - - emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt).getOrElse{ - if (casesNoSubstOnly nonEmpty) { - // check casesNoSubstOnly for presence of a default case, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one - // exhaustivity and reachability must be checked before optimization as well - // TODO: improve, a trivial type test before the body still makes for a default case - // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) - val catchAll = - if (casesNoSubstOnly.nonEmpty && { - val nonTrivLast = casesNoSubstOnly.last - nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] - }) None - else Some(matchFail) - - val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) - - val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases map combineExtractors, catchAll) - - if (toHoist isEmpty) matchRes else Block(toHoist, matchRes) - } else { - codegen.matcher(scrut, scrutSym, pt)(Nil, Some(matchFail)) + def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Tree => Tree]): Tree = + fixerUpper(owner, scrut.pos){ + val ptDefined = if (isFullyDefined(pt)) pt else NoType + def matchFailGen = (matchFailGenOverride orElse Some(CODE.MATCHERROR(_: Tree))) + + emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride).getOrElse{ + if (casesNoSubstOnly nonEmpty) { + // before optimizing, check casesNoSubstOnly for presence of a default case, + // since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one + // exhaustivity and reachability must be checked before optimization as well + // TODO: improve notion of trivial/irrefutable -- a trivial type test before the body still makes for a default case + // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) + // irrefutability checking should use the approximation framework also used for CSE, unreachability and exhaustivity checking + val synthCatchAll = + if (casesNoSubstOnly.nonEmpty && { + val nonTrivLast = casesNoSubstOnly.last + nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] + }) None + else matchFailGen + + val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt) + + val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases map combineExtractors, synthCatchAll) + + if (toHoist isEmpty) matchRes else Block(toHoist, matchRes) + } else { + codegen.matcher(scrut, scrutSym, pt)(Nil, matchFailGen) + } } } - } - - // combineExtractors changes the current substitution's of the tree makers in `treeMakers` - // requires propagateSubstitution(treeMakers) has been called - def combineExtractors(treeMakers: List[TreeMaker])(casegen: Casegen): Tree = - treeMakers.foldRight(EmptyTree: Tree)((a, b) => a.chainBefore(b)(casegen)) // TODO: do this during tree construction, but that will require tracking the current owner in treemakers // TODO: assign more fine-grained positions @@ -1043,7 +1041,7 @@ class Foo(x: Other) { x._1 } // no error in this order // codegen relevant to the structure of the translation (how extractors are combined) trait AbsCodegen { - def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], catchAllGen: Option[Casegen => Tree => Tree]): Tree + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree // local / context-free def _asInstanceOf(b: Symbol, tp: Type): Tree @@ -1136,13 +1134,13 @@ class Foo(x: Other) { x._1 } // no error in this order //// methods in MatchingStrategy (the monad companion) -- used directly in translation // __match.runOrElse(`scrut`)(`scrutSym` => `matcher`) // TODO: consider catchAll, or virtualized matching will break in exception handlers - def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], catchAllGen: Option[Casegen => Tree => Tree]): Tree = + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, cases map (f => f(this)) reduceLeft typedOrElse)) // __match.one(`res`) def one(res: Tree): Tree = (_match(vpmName.one)) (res) // __match.zero - def zero: Tree = _match(vpmName.zero) + protected def zero: Tree = _match(vpmName.zero) // __match.guard(`c`, `then`) def guard(c: Tree, then: Tree): Tree = _match(vpmName.guard) APPLY (c, then) @@ -1517,7 +1515,7 @@ class Foo(x: Other) { x._1 } // no error in this order } } - class RegularSwitchMaker(scrutSym: Symbol) extends SwitchMaker { + class RegularSwitchMaker(scrutSym: Symbol, matchFailGenOverride: Option[Tree => Tree]) extends SwitchMaker { val switchableTpe = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) val alternativesSupported = true @@ -1540,14 +1538,14 @@ class Foo(x: Other) { x._1 } // no error in this order } def defaultSym: Symbol = scrutSym - def defaultBody: Tree = { import CODE._; MATCHERROR(REF(scrutSym)) } + def defaultBody: Tree = { import CODE._; matchFailGenOverride map (gen => gen(REF(scrutSym))) getOrElse MATCHERROR(REF(scrutSym)) } def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) { DEFAULT ==> body }} } - override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { import CODE._ - val regularSwitchMaker = new RegularSwitchMaker(scrutSym) + override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree]): Option[Tree] = { import CODE._ + val regularSwitchMaker = new RegularSwitchMaker(scrutSym, matchFailGenOverride) // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result if (regularSwitchMaker.switchableTpe(scrutSym.tpe)) { val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) @@ -1555,7 +1553,7 @@ class Foo(x: Other) { x._1 } // no error in this order else { // match on scrutSym -- converted to an int if necessary -- not on scrut directly (to avoid duplicating scrut) val scrutToInt: Tree = - if(scrutSym.tpe =:= IntClass.tpe) REF(scrutSym) + if (scrutSym.tpe =:= IntClass.tpe) REF(scrutSym) else (REF(scrutSym) DOT (nme.toInt)) Some(BLOCK( VAL(scrutSym) === scrut, @@ -1631,7 +1629,7 @@ class Foo(x: Other) { x._1 } // no error in this order * the matcher's optional result is encoded as a flag, keepGoing, where keepGoing == true encodes result.isEmpty, * if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x */ - def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], catchAllGen: Option[Casegen => Tree => Tree]): Tree = { + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { val matchEnd = NoSymbol.newLabel(freshName("matchEnd"), NoPosition) setFlag (SYNTHETIC | Flags.CASE) val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe matchEnd setInfo MethodType(List(matchRes), restpe) @@ -1645,38 +1643,35 @@ class Foo(x: Other) { x._1 } // no error in this order LabelDef(currCase, Nil, mkCase(casegen)) } - def catchAll = catchAllGen map { catchAllGen => - val casegen = new OptimizedCasegen(matchEnd, NoSymbol) - val scrutRef = if(scrutSym eq NoSymbol) EmptyTree else REF(scrutSym) - LabelDef(nextCase, Nil, catchAllGen(casegen)(scrutRef)) + def catchAll = matchFailGen map { matchFailGen => + val scrutRef = if(scrutSym ne NoSymbol) REF(scrutSym) else EmptyTree // for alternatives + LabelDef(nextCase, Nil, matchEnd APPLY (matchFailGen(scrutRef))) // need to jump to matchEnd with result generated by matchFailGen (could be `FALSE` for isDefinedAt) } toList + // catchAll.isEmpty iff no synthetic default case needed (the (last) user-defined case is a default) + // if the last user-defined case is a default, it will never jump to the next case; it will go immediately to matchEnd // the generated block is taken apart in TailCalls under the following assumptions // the assumption is once we encounter a case, the remainder of the block will consist of cases // the prologue may be empty, usually it is the valdef that stores the scrut // val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) - val prologue = if(scrutSym ne NoSymbol) List(VAL(scrutSym) === scrut) else Nil + // scrutSym == NoSymbol when generating an alternatives matcher + val scrutDef = if(scrutSym ne NoSymbol) List(VAL(scrutSym) === scrut) else Nil // for alternatives Block( - prologue ++ (cases map caseDef) ++ catchAll, + scrutDef ++ (cases map caseDef) ++ catchAll, LabelDef(matchEnd, List(matchRes), REF(matchRes)) ) } class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol) extends CommonCodegen with Casegen { - def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], catchAllGen: Option[Casegen => Tree => Tree]): Tree = - optimizedCodegen.matcher(scrut, scrutSym, restpe)(cases, catchAllGen) - - def zero: Tree = nextCase APPLY () + def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = + optimizedCodegen.matcher(scrut, scrutSym, restpe)(cases, matchFailGen) // only used to wrap the RHS of a body // res: T // returns MatchMonad[T] def one(res: Tree): Tree = matchEnd APPLY (res) - - - override def ifThenElseZero(c: Tree, then: Tree): Tree = - IF (c) THEN then ELSE zero + protected def zero: Tree = nextCase APPLY () // prev: MatchMonad[T] // b: T diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 0dd4b37131..506e347828 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2143,6 +2143,137 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) } + def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) + + def translateMatch(selector: Tree, cases: List[CaseDef], mode: Int, resTp: Type, scrutTp: Type = NoType, matchFailGen: Option[Tree => Tree] = None) = { + val selector1 = if(scrutTp eq NoType) checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) else selector + val selectorTp = if(scrutTp eq NoType) packCaptured(selector1.tpe.widen) else scrutTp + val casesTyped = typedCases(cases, selectorTp, resTp) + val (ownType, needAdapt) = if (isFullyDefined(resTp)) (resTp, false) else weakLub(casesTyped map (_.tpe.deconst)) + val casesAdapted = if (!needAdapt) casesTyped else casesTyped map (adaptCase(_, mode, ownType)) + // val (owntype0, needAdapt) = ptOrLub(casesTyped map (x => repackExistential(x.tpe))) + // val owntype = elimAnonymousClass(owntype0) + + def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { + case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args) + case _ => tp + } + + def isSynthSelector(selector: Tree): Boolean = selector match { + case Ident(_) if selector.symbol.hasFlag(SYNTHETIC | CASE) => true + case Select(sel, nme.toInt) => isSynthSelector(sel) // switch may need to convert to int first + case _ => false + } + + if (isSynthSelector(selector1)) { // a switch + (Match(selector1, casesAdapted) setType ownType, ownType) // setType of the Match to avoid recursing endlessly + } else { + val scrutType = repeatedToSeq(elimAnonymousClass(selectorTp)) + (MatchTranslator(this).translateMatch(selector1, casesAdapted, repeatedToSeq(ownType), scrutType, matchFailGen), ownType) + } + } + + // TODO: use this to synthesize (partial)function implementation for matches from the get-go, + // instead of the dirty post-factum hacks in uncurry -- typedMatchAnonFun is currently not used due to mindboggling failures (see virtpatmat_anonfun_for.scala) + def typedMatchAnonFun(tree: Tree, cases: List[CaseDef], mode: Int, pt0: Type, selOverride: Option[(List[Symbol], Tree)] = None) = { + val pt = deskolemizeGADTSkolems(pt0) + val targs = pt.normalize.typeArgs + val arity = if (isFunctionType(pt)) targs.length - 1 else 1 + val scrutTp0 = if (arity == 1) targs.head else /* arity > 1 */ tupleType(targs.init) + val scrutTp = packCaptured(scrutTp0) + val ptRes = targs.last // may not be fully defined + val isPartial = pt.typeSymbol == PartialFunctionClass + val cname = tpnme.ANON_FUN_NAME + val funThis = This(cname) + // used to create a new context for pattern matching translation so that + // we can easily rejig the owner structure when we have the actual symbols for these methods + // (after type checking them, but type checking requires translation -- this seems like the easiest way to end this vicious cycle) + val applySentinel = NoSymbol.newMethod(nme.apply) + val idaSentinel = NoSymbol.newMethod(nme._isDefinedAt) + + def mkParams = { + val params = + for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { + ValDef(Modifiers(SYNTHETIC | PARAM), unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree) + } + val ids = params map (p => Ident(p.name)) + + val paramsRef = selOverride match { + case None => atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) } + case Some((_, sel)) => sel.duplicate // we'll replace the symbols that refer to the function's original syms by the ones introduced by the DefDef once the method's been type checked (until then, we don't know them) + } + + (params, paramsRef) // paramsRef can't be typed until after match has been translated, thus supply explicit scrutTp to translate below + } + + import CODE._ + + // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up + val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => TRUE).duplicate) else Nil + + val (applyMethod, parents) = { + val (params, paramsRef) = mkParams + val (body, resTp) = newTyper(context.make(context.tree, applySentinel)).translateMatch(paramsRef, cases, mode, ptRes, scrutTp, if (isPartial) Some(scrut => (funThis DOT nme.missingCase) (scrut)) else None) + + def abstractFunctionType = { + val sym = AbstractFunctionClass(arity) + typeRef(sym.typeConstructor.prefix, sym, targs.init :+ resTp) + } + + val parents = + if (isFunctionType(pt)) List(abstractFunctionType, SerializableClass.tpe) + else if (isPartial) List(appliedType(AbstractPartialFunctionClass.typeConstructor, List(scrutTp, resTp)), SerializableClass.tpe) + else List(ObjectClass.tpe, pt, SerializableClass.tpe) + + (atPos(tree.pos.focus)(DefDef(Modifiers(FINAL), nme.apply, Nil, List(params), TypeTree() setType resTp, body)), parents) + } + + def isDefinedAtMethod = { + val (params, paramsRef) = mkParams + val (body, _) = newTyper(context.make(context.tree, idaSentinel)).translateMatch(paramsRef, casesTrue, mode, BooleanClass.tpe, scrutTp, Some(scrutinee => FALSE)) + atPos(tree.pos.focus)( + DefDef(Modifiers(FINAL), nme._isDefinedAt, Nil, List(params), TypeTree() setType BooleanClass.tpe, body) + ) + } + + val members = if (!isPartial) List(applyMethod) else List(applyMethod, isDefinedAtMethod) + + val cmods = Modifiers(FINAL | SYNTHETIC /*TODO: when do we need INCONSTRUCTOR ?*/) withAnnotations ( + List(NEW(SerialVersionUIDAttr, LIT(0)))) + val cdef = + ClassDef(cmods, cname, Nil, + Template(parents map (TypeTree() setType _), emptyValDef, Modifiers(0), Nil, List(Nil), members, tree.pos) + ) + val funInst = (Block(List(cdef), Apply(Select(New(Ident(cname)), nme.CONSTRUCTOR), Nil))) + + val res = typed(funInst, mode, pt) + + // now that we have the symbols corresponding to the apply/isDefinedAt methods, + // we can fix up the result of fixerUpper... URGH + // fixerUpper nests the top-level definitions generated in the match under context.owner, but they should be owner by the apply/isDefinedAt method + res foreach { + case d: DefDef if (d.symbol.name == nme.apply) => + d.rhs.changeOwner(applySentinel -> d.symbol) + case d: DefDef if (d.symbol.name == nme._isDefinedAt) => + d.rhs.changeOwner(idaSentinel -> d.symbol) + case _ => + } + + selOverride match { + case None => res + case Some((paramSyms, sel)) => + object substParamSyms extends Transformer { + override def transform(t: Tree): Tree = t match { + case d: DefDef if (d.symbol.name == nme.apply) || (d.symbol.name == nme._isDefinedAt) && (d.symbol.owner == res.tpe.typeSymbol) => + deriveDefDef(d)(rhs => rhs.substTreeSyms(paramSyms, d.vparamss.head.map(_.symbol))) + case _ => + super.transform(t) + } + } + substParamSyms.transform(res) + } + } + /** * @param fun ... * @param mode ... @@ -2155,14 +2286,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { return MaxFunctionArityError(fun) def decompose(pt: Type): (Symbol, List[Type], Type) = - if ((isFunctionType(pt) - || - pt.typeSymbol == PartialFunctionClass && - numVparams == 1 && fun.body.isInstanceOf[Match]) - && // see bug901 for a reason why next conditions are needed - (pt.normalize.typeArgs.length - 1 == numVparams - || - fun.vparams.exists(_.tpt.isEmpty))) + if ((isFunctionType(pt) || (pt.typeSymbol == PartialFunctionClass && numVparams == 1 && fun.body.isInstanceOf[Match])) && // see bug901 for a reason why next conditions are needed + ( pt.normalize.typeArgs.length - 1 == numVparams + || fun.vparams.exists(_.tpt.isEmpty) + )) (pt.typeSymbol, pt.normalize.typeArgs.init, pt.normalize.typeArgs.last) else (FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType) @@ -2204,13 +2331,27 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // for (vparam <- vparams) { // checkNoEscaping.locals(context.scope, WildcardType, vparam.tpt); () // } - val body1 = typed(fun.body, respt) - val formals = vparamSyms map (_.tpe) - val restpe = packedType(body1, fun.symbol).deconst.resultType - val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) -// body = checkNoEscaping.locals(context.scope, restpe, body) - treeCopy.Function(fun, vparams, body1).setType(funtpe) - } + + def recompose(from: Type, to: Type) = + if(clazz == PartialFunctionClass) appliedType(PartialFunctionClass.typeConstructor, List(from, to)) + else functionType(List(from), to) + + fun.body match { + case Match(sel, cases) if opt.virtPatmat => + val typedSel = typed(sel, EXPRmode | BYVALmode, WildcardType) + // go to outer context -- must discard the context that was created for the Function since we're discarding the function + // thus, its symbol, which serves as the current context.owner, is not the right owner + // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) + newTyper(context.outer).typedMatchAnonFun(fun, cases, mode, recompose(typedSel.tpe, respt), Some((vparamSyms, typedSel))) + case _ => + val body1 = typed(fun.body, respt) + val formals = vparamSyms map (_.tpe) + val restpe = packedType(body1, fun.symbol).deconst.resultType + val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe) + // body = checkNoEscaping.locals(context.scope, restpe, body) + treeCopy.Function(fun, vparams, body1).setType(funtpe) + } + } } def typedRefinement(stats: List[Tree]) { @@ -3431,7 +3572,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def typedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = { - if (selector == EmptyTree) { + if (opt.virtPatmat && !isPastTyper) { + if (selector ne EmptyTree) typed(translateMatch(selector, cases, mode, pt)._1, mode, pt) + else typedMatchAnonFun(tree, cases, mode, pt) + } else if (selector == EmptyTree) { val arity = if (isFunctionType(pt)) pt.normalize.typeArgs.length - 1 else 1 val params = for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { @@ -3445,32 +3589,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } else { val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) - - if (isPastTyper || !opt.virtPatmat) { - val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) - if (needAdapt) { - cases1 = cases1 map (adaptCase(_, owntype)) - } - treeCopy.Match(tree, selector1, cases1) setType owntype - } else { // don't run translator after typers (see comments in PatMatVirtualiser) - val (owntype0, needAdapt) = ptOrLub(cases1 map (x => repackExistential(x.tpe))) - val owntype = elimAnonymousClass(owntype0) - if (needAdapt) cases1 = cases1 map (adaptCase(_, owntype)) - - (MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match { - case Block(vd :: Nil, tree@Match(selector, cases)) => - val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType)) - var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt) - val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) - if (needAdapt) - cases1 = cases1 map (adaptCase(_, owntype)) - typed(Block(vd :: Nil, treeCopy.Match(tree, selector1, cases1) setType owntype)) - case translated => - // TODO: get rid of setType owntype -- it should all typecheck - // must call typed, not typed1, or we overflow the stack when emitting switches - typed(translated, mode, WildcardType) setType owntype - } + val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe)) + if (needAdapt) { + cases1 = cases1 map (adaptCase(_, mode, owntype)) } + treeCopy.Match(tree, selector1, cases1) setType owntype } } @@ -4240,9 +4363,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def adaptCase(cdef: CaseDef, tpe: Type): CaseDef = - deriveCaseDef(cdef)(adapt(_, mode, tpe)) - // begin typed1 val sym: Symbol = tree.symbol if ((sym ne null) && (sym ne NoSymbol)) sym.initialize @@ -4351,7 +4471,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe))) if (needAdapt) { block1 = adapt(block1, mode, owntype) - catches1 = catches1 map (adaptCase(_, owntype)) + catches1 = catches1 map (adaptCase(_, mode, owntype)) } if(!isPastTyper && opt.virtPatmat) { diff --git a/test/files/pos/virtpatmat_anonfun_for.flags b/test/files/pos/virtpatmat_anonfun_for.flags new file mode 100644 index 0000000000..23e3dc7d26 --- /dev/null +++ b/test/files/pos/virtpatmat_anonfun_for.flags @@ -0,0 +1 @@ +-Yvirtpatmat \ No newline at end of file diff --git a/test/files/pos/virtpatmat_anonfun_for.scala b/test/files/pos/virtpatmat_anonfun_for.scala new file mode 100644 index 0000000000..8623cd97ba --- /dev/null +++ b/test/files/pos/virtpatmat_anonfun_for.scala @@ -0,0 +1,8 @@ +trait Foo { + def bla = { + val tvs = "tvs" + Nil.foreach(x => x match { + case _ => println(tvs) + }) + } +} \ No newline at end of file diff --git a/test/files/run/virtpatmat_partial.check b/test/files/run/virtpatmat_partial.check index 1555eca82b..137d16da79 100644 --- a/test/files/run/virtpatmat_partial.check +++ b/test/files/run/virtpatmat_partial.check @@ -1,4 +1,17 @@ Map(a -> Some(1), b -> None) -79 -undefined Map(a -> 1) +a +undefined +a +undefined +a +undefined +a +undefined +hai! +hai! +2 +hai! +undefined +1 +undefined diff --git a/test/files/run/virtpatmat_partial.scala b/test/files/run/virtpatmat_partial.scala index 6597f2f5ae..a235314610 100644 --- a/test/files/run/virtpatmat_partial.scala +++ b/test/files/run/virtpatmat_partial.scala @@ -2,95 +2,180 @@ object Test extends App { val a = Map("a" -> Some(1), "b" -> None) println(a) +// inferred type should be Map[String, Int] val res = a collect {case (p, Some(a)) => (p, a)} - final val GT = 79 - final val GTGT = 93 - final val GTGTGT = 94 - final val GTEQ = 81 - final val GTGTEQ = 113 - final val GTGTGTEQ = 114 - final val ASSIGN = 75 - - def acceptClosingAngle(in: Int) { - val closers: PartialFunction[Int, Int] = { - case GTGTGTEQ => GTGTEQ - case GTGTGT => GTGT - case GTGTEQ => GTEQ - case GTGT => GT - case GTEQ => ASSIGN +// variations: const target -> switch, non-const -> normal match, char target --> scrut needs toInt, +// eta-expanded --> work is done by typedFunction, non-eta-expanded --> typedMatch + + object nonConstCharEta { + final val GT : Char = 'a' + final val GTGT : Char = 'b' + final val GTGTGT : Char = 'c' + final val GTEQ : Char = 'd' + final val GTGTEQ : Char = 'e' + final val GTGTGTEQ: Char = 'f' + final val ASSIGN : Char = 'g' + + def acceptClosingAngle(in: Char) { + val closers: PartialFunction[Char, Char] = { + case GTGTGTEQ => GTGTEQ + case GTGTGT => GTGT + case GTGTEQ => GTEQ + case GTGT => GT + case GTEQ => ASSIGN + } + if (closers isDefinedAt in) println(closers(in)) + else println("undefined") + } + + def test() = { + acceptClosingAngle(GTGT) + acceptClosingAngle(ASSIGN) + } + } + + object nonConstChar { + final val GT : Char = 'a' + final val GTGT : Char = 'b' + final val GTGTGT : Char = 'c' + final val GTEQ : Char = 'd' + final val GTGTEQ : Char = 'e' + final val GTGTGTEQ: Char = 'f' + final val ASSIGN : Char = 'g' + + def acceptClosingAngle(in: Char) { + val closers: PartialFunction[Char, Char] = x => x match { + case GTGTGTEQ => GTGTEQ + case GTGTGT => GTGT + case GTGTEQ => GTEQ + case GTGT => GT + case GTEQ => ASSIGN + } + if (closers isDefinedAt in) println(closers(in)) + else println("undefined") + } + + def test() = { + acceptClosingAngle(GTGT) + acceptClosingAngle(ASSIGN) } - if (closers isDefinedAt in) println(closers(in)) - else println("undefined") } - acceptClosingAngle(GTGT) - acceptClosingAngle(ASSIGN) - - // should uncurry to: - // val res: Map[String,Int] = a.collect[(String, Int), Map[String,Int]]( - // new PartialFunction[(String, Option[Int]),(String, Int)] { - // def apply(x0_1: (String, Option[Int])): (String, Int) = MatchingStrategy.OptionMatchingStrategy.runOrElse[(String, Option[Int]), (String, Int)](x0_1)( - // (x1: (String, Option[Int])) => { - // val o9: Option[(String, Int)] = ({ - // val o8: Option[(String, Option[Int])] = Tuple2.unapply[String, Option[Int]](x1); - // if (o8.isEmpty) - // MatchingStrategy.OptionMatchingStrategy.zero - // else - // { - // val o7: Option[Some[Int]] = if (o8.get._2.isInstanceOf[Some[Int]]) - // MatchingStrategy.OptionMatchingStrategy.one[Some[Int]](o8.get._2.asInstanceOf[Some[Int]]) - // else - // MatchingStrategy.OptionMatchingStrategy.zero; - // if (o7.isEmpty) - // MatchingStrategy.OptionMatchingStrategy.zero - // else - // { - // val o6: Option[Int] = Some.unapply[Int](o7.get); - // if (o6.isEmpty) - // MatchingStrategy.OptionMatchingStrategy.zero - // else - // MatchingStrategy.OptionMatchingStrategy.one[(String, Int)]((o8.get._1, o6.get).asInstanceOf[(String, Int)]) - // } - // } - // }: Option[(String, Int)]); - // if (o9.isEmpty) - // (MatchingStrategy.OptionMatchingStrategy.zero: Option[(String, Int)]) - // else - // o9 - // }) - // - // def isDefinedAt(x_1: (String, Option[Int])): Boolean = MatchingStrategy.OptionMatchingStrategy.isSuccess[(String, Option[Int]), (String, Int)](x_1)( - // (x1: (String, Option[Int])) => { - // val o9: Option[(String, Int)] = ({ - // val o8: Option[(String, Option[Int])] = scala.Tuple2.unapply[String, Option[Int]](x1); - // if (o8.isEmpty) - // MatchingStrategy.OptionMatchingStrategy.zero - // else - // { - // val o7: Option[Some[Int]] = if (o8.get._2.isInstanceOf[Some[Int]]) - // MatchingStrategy.OptionMatchingStrategy.one[Some[Int]](o8.get._2.asInstanceOf[Some[Int]]) // XXX - // else - // MatchingStrategy.OptionMatchingStrategy.zero; - // if (o7.isEmpty) - // MatchingStrategy.OptionMatchingStrategy.zero - // else - // { - // val o6: Option[Int] = scala.Some.unapply[Int](o7.get); - // if (o6.isEmpty) - // MatchingStrategy.OptionMatchingStrategy.zero - // else - // MatchingStrategy.OptionMatchingStrategy.one[(String, Int)](null.asInstanceOf[(String, Int)]) - // } - // } - // }: Option[(String, Int)]); - // if (o9.isEmpty) - // (MatchingStrategy.OptionMatchingStrategy.zero: Option[(String, Int)]) - // else - // o9 - // }) - // } - // ) - - println(res) + object constCharEta { + final val GT = 'a' + final val GTGT = 'b' + final val GTGTGT = 'c' + final val GTEQ = 'd' + final val GTGTEQ = 'e' + final val GTGTGTEQ= 'f' + final val ASSIGN = 'g' + + def acceptClosingAngle(in: Char) { + val closers: PartialFunction[Char, Char] = x => x match { + case GTGTGTEQ => GTGTEQ + case GTGTGT => GTGT + case GTGTEQ => GTEQ + case GTGT => GT + case GTEQ => ASSIGN + } + if (closers isDefinedAt in) println(closers(in)) + else println("undefined") + } + + def test() = { + acceptClosingAngle(GTGT) + acceptClosingAngle(ASSIGN) + } + } + + object constChar { + final val GT = 'a' + final val GTGT = 'b' + final val GTGTGT = 'c' + final val GTEQ = 'd' + final val GTGTEQ = 'e' + final val GTGTGTEQ= 'f' + final val ASSIGN = 'g' + + def acceptClosingAngle(in: Char) { + val closers: PartialFunction[Char, Char] = { + case GTGTGTEQ => GTGTEQ + case GTGTGT => GTGT + case GTGTEQ => GTEQ + case GTGT => GT + case GTEQ => ASSIGN + } + if (closers isDefinedAt in) println(closers(in)) + else println("undefined") + } + + def test() = { + acceptClosingAngle(GTGT) + acceptClosingAngle(ASSIGN) + } + } + + object constIntEta { + final val GT = 1 + final val GTGT = 2 + final val GTGTGT = 3 + final val GTEQ = 4 + final val GTGTEQ = 5 + final val GTGTGTEQ = 6 + final val ASSIGN = 7 + + def acceptClosingAngle(in: Int) { + val closers: PartialFunction[Int, Int] = x => {println("hai!"); (x + 1)} match { + case GTGTGTEQ => GTGTEQ + case GTGTGT => GTGT + case GTGTEQ => GTEQ + case GTGT => GT + case GTEQ => ASSIGN + } + if (closers isDefinedAt in) println(closers(in)) + else println("undefined") + } + + def test() = { + acceptClosingAngle(GTGT) + acceptClosingAngle(ASSIGN) + } + } + + object constInt { + final val GT = 1 + final val GTGT = 2 + final val GTGTGT = 3 + final val GTEQ = 4 + final val GTGTEQ = 5 + final val GTGTGTEQ = 6 + final val ASSIGN = 7 + + def acceptClosingAngle(in: Int) { + val closers: PartialFunction[Int, Int] = { + case GTGTGTEQ => GTGTEQ + case GTGTGT => GTGT + case GTGTEQ => GTEQ + case GTGT => GT + case GTEQ => ASSIGN + } + if (closers isDefinedAt in) println(closers(in)) + else println("undefined") + } + + def test() = { + acceptClosingAngle(GTGT) + acceptClosingAngle(ASSIGN) + } + } + + println(res) // prints "Map(a -> 1)" + + nonConstCharEta.test() + nonConstChar.test() + constCharEta.test() + constChar.test() + constIntEta.test() + constInt.test() } diff --git a/test/pending/run/virtpatmat_anonfun_underscore.check b/test/pending/run/virtpatmat_anonfun_underscore.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/pending/run/virtpatmat_anonfun_underscore.flags b/test/pending/run/virtpatmat_anonfun_underscore.flags new file mode 100644 index 0000000000..23e3dc7d26 --- /dev/null +++ b/test/pending/run/virtpatmat_anonfun_underscore.flags @@ -0,0 +1 @@ +-Yvirtpatmat \ No newline at end of file diff --git a/test/pending/run/virtpatmat_anonfun_underscore.scala b/test/pending/run/virtpatmat_anonfun_underscore.scala new file mode 100644 index 0000000000..db6705d025 --- /dev/null +++ b/test/pending/run/virtpatmat_anonfun_underscore.scala @@ -0,0 +1,4 @@ +object Test extends App { + List(1,2,3) map (_ match { case x => x + 1} ) // `_ match` is redundant but shouldn't crash the compiler + List((1,2)) map (_ match { case (x, z) => x + z}) +} \ No newline at end of file -- cgit v1.2.3 From 9e513a6d29f2cb060caf58ff5568d7955b96305a Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Sat, 17 Mar 2012 12:15:37 +0100 Subject: [vpm] fix half of my existential troubles no need for the clunky repackExistential pack the type of each case instead, since the skolems we've created shouldn't last beyond the case anyway this way we don't end up with fresh, incompatible, skolems for every case, but a neatly packed existential --- src/compiler/scala/reflect/internal/Types.scala | 9 ------ .../tools/nsc/typechecker/PatMatVirtualiser.scala | 10 +++---- .../scala/tools/nsc/typechecker/Typers.scala | 6 +++- test/files/pos/virtpatmat_exist4.scala | 35 ++++++++++++++++++++++ 4 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 test/files/pos/virtpatmat_exist4.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 549c9e4607..2bb19e2b65 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -2617,15 +2617,6 @@ trait Types extends api.Types { self: SymbolTable => } } - // TODO: I don't really know why this happens -- maybe because - // the owner hierarchy changes? the other workaround (besides - // repackExistential) is to explicitly pass expectedTp as the type - // argument for the call to guard, but repacking the existential - // somehow feels more robust - // - // TODO: check if optimization makes a difference, try something else - // if necessary (cache?) - /** Repack existential types, otherwise they sometimes get unpacked in the * wrong location (type inference comes up with an unexpected skolem) */ diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 34fefd20fe..de7f03dc62 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -1037,7 +1037,7 @@ class Foo(x: Other) { x._1 } // no error in this order // assert(owner ne null); assert(owner ne NoSymbol) def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = - NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo repackExistential(tp) + NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo /*repackExistential*/(tp) // codegen relevant to the structure of the translation (how extractors are combined) trait AbsCodegen { @@ -1079,18 +1079,18 @@ class Foo(x: Other) { x._1 } // no error in this order def and(a: Tree, b: Tree): Tree = a AND b // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) - def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = repackExistential(tp) + def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = /*repackExistential*/(tp) if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX) else gen.mkAsInstanceOf(t, tpX, true, false) } - def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), repackExistential(tp), true, false) - // { val tpX = repackExistential(tp) + def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), /*repackExistential*/(tp), true, false) + // { val tpX = /*repackExistential*/(tp) // if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } // else gen.mkIsInstanceOf(REF(b), tpX, true, false) // } - def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = repackExistential(tp) + def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = /*repackExistential*/(tp) if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX) else gen.mkAsInstanceOf(REF(b), tpX, true, false) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 506e347828..daf4ddd100 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2140,7 +2140,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = cases mapConserve { cdef => - newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) + val caseTyped = newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt) + if (opt.virtPatmat) { + val tpPacked = packedType(caseTyped, context.owner) + caseTyped setType tpPacked + } else caseTyped } def adaptCase(cdef: CaseDef, mode: Int, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe)) diff --git a/test/files/pos/virtpatmat_exist4.scala b/test/files/pos/virtpatmat_exist4.scala new file mode 100644 index 0000000000..a04d0e3229 --- /dev/null +++ b/test/files/pos/virtpatmat_exist4.scala @@ -0,0 +1,35 @@ +trait Global { + trait Tree + trait Symbol { def foo: Boolean } +} + +trait IMain { self: MemberHandlers => + val global: Global + def handlers: List[MemberHandler] +} + +trait MemberHandlers { + val intp: IMain + import intp.global._ + sealed abstract class MemberHandler(val member: Tree) { + def importedSymbols: List[Symbol] + } +} + +object Test { + var intp: IMain with MemberHandlers = null + + val handlers = intp.handlers + handlers.filterNot(_.importedSymbols.isEmpty).zipWithIndex foreach { + case (handler, idx) => + val (types, terms) = handler.importedSymbols partition (_.foo) + } +} + +object Test2 { + type JClass = java.lang.Class[_] + + def tvarString(bounds: List[AnyRef]) = { + bounds collect { case x: JClass => x } + } +} \ No newline at end of file -- cgit v1.2.3 From 8552740beda9bb07de1da329e9dc8c002090fe16 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Sat, 17 Mar 2012 12:18:43 +0100 Subject: [vpm] pack types of if branches before lubbing this way we don't end up with fresh, incompatible, skolems for every branch, but a neatly packed existential you get much nicer lubs this way -- threw in a fast path (I hope) that avoids lubbing when unnecessary (same types) since lub isn't robust enough to deal with the result of packedType, and we only really need the trivial case (S =:= T), special case it for now ... don't pack in ifs after typer, also only when virtPatmat --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index daf4ddd100..ad48712a32 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3566,7 +3566,15 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } else { var thenp1 = typed(thenp, pt) var elsep1 = typed(elsep, pt) - val (owntype, needAdapt) = ptOrLub(List(thenp1.tpe, elsep1.tpe)) + + lazy val thenTp = packedType(thenp1, context.owner) + lazy val elseTp = packedType(elsep1, context.owner) + val (owntype, needAdapt) = + // virtpatmat needs more aggressive unification of skolemized types, but lub is not robust enough --> middle ground + if (opt.virtPatmat && !isPastTyper && thenTp =:= elseTp) (thenTp, true) // this breaks src/library/scala/collection/immutable/TrieIterator.scala + // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala + else ptOrLub(List(thenp1.tpe, elsep1.tpe)) + if (needAdapt) { //isNumericValueType(owntype)) { thenp1 = adapt(thenp1, mode, owntype) elsep1 = adapt(elsep1, mode, owntype) -- cgit v1.2.3 From d8ba5d091e5641553b438ef9930a6023a2709dcd Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 20 Mar 2012 13:05:17 -0700 Subject: Revert irrefutability commits. Temporary reversion of irrefutability commits in interests of stable milestone. Expect to restore shortly. --- src/compiler/scala/reflect/internal/TreeInfo.scala | 20 +----------- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 34 +++++++++++--------- .../scala/tools/nsc/typechecker/RefChecks.scala | 2 +- test/files/neg/t5589neg.check | 37 ---------------------- test/files/neg/t5589neg.scala | 6 ---- test/files/neg/t5589neg2.check | 9 ------ test/files/neg/t5589neg2.scala | 13 -------- test/files/pos/irrefutable.scala | 22 ------------- test/files/pos/t1336.scala | 10 ------ test/files/pos/t5589.scala | 22 ------------- test/files/run/t4574.check | 2 -- test/files/run/t4574.scala | 13 -------- 12 files changed, 21 insertions(+), 169 deletions(-) delete mode 100644 test/files/neg/t5589neg.check delete mode 100644 test/files/neg/t5589neg.scala delete mode 100644 test/files/neg/t5589neg2.check delete mode 100644 test/files/neg/t5589neg2.scala delete mode 100644 test/files/pos/irrefutable.scala delete mode 100644 test/files/pos/t1336.scala delete mode 100644 test/files/pos/t5589.scala delete mode 100644 test/files/run/t4574.check delete mode 100644 test/files/run/t4574.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index ce3de94335..769d7a9ed1 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -17,7 +17,7 @@ abstract class TreeInfo { val global: SymbolTable import global._ - import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass } + import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -312,24 +312,6 @@ abstract class TreeInfo { case _ => false } - /** Is this tree comprised of nothing but identifiers, - * but possibly in bindings or tuples? For instance - * - * foo @ (bar, (baz, quux)) - * - * is a variable pattern; if the structure matches, - * then the remainder is inevitable. - */ - def isVariablePattern(tree: Tree): Boolean = tree match { - case Bind(name, pat) => isVariablePattern(pat) - case Ident(name) => true - case Apply(sel, args) => - ( isReferenceToScalaMember(sel, TupleClass(args.size).name.toTermName) - && (args forall isVariablePattern) - ) - case _ => false - } - /** Is this argument node of the form : _* ? */ def isWildcardStarArg(tree: Tree): Boolean = tree match { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 0bc88d1efd..0d2fbc5372 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -262,25 +262,29 @@ abstract class TreeBuilder { else if (stats.length == 1) stats.head else Block(stats.init, stats.last) - def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = { - val cases = List( - CaseDef(condition, EmptyTree, Literal(Constant(true))), - CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) - ) - val matchTree = makeVisitor(cases, false, scrutineeName) - - atPos(tree.pos)(Apply(Select(tree, nme.withFilter), matchTree :: Nil)) - } - /** Create tree for for-comprehension generator */ def makeGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree): Enumerator = { val pat1 = patvarTransformer.transform(pat) val rhs1 = - if (valeq || treeInfo.isVariablePattern(pat)) rhs - else makeFilter(rhs, pat1.duplicate, nme.CHECK_IF_REFUTABLE_STRING) - - if (valeq) ValEq(pos, pat1, rhs1) - else ValFrom(pos, pat1, rhs1) + if (valeq) rhs + else matchVarPattern(pat1) match { + case Some(_) => + rhs + case None => + atPos(rhs.pos) { + Apply( + Select(rhs, nme.filter), + List( + makeVisitor( + List( + CaseDef(pat1.duplicate, EmptyTree, Literal(Constant(true))), + CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))), + false, + nme.CHECK_IF_REFUTABLE_STRING + ))) + } + } + if (valeq) ValEq(pos, pat1, rhs1) else ValFrom(pos, pat1, rhs1) } def makeParam(pname: TermName, tpe: Tree) = diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 73369f09af..ec42d251ff 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1445,7 +1445,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R private def transformApply(tree: Apply): Tree = tree match { case Apply( - Select(qual, nme.filter | nme.withFilter), + Select(qual, nme.filter), List(Function( List(ValDef(_, pname, tpt, _)), Match(_, CaseDef(pat1, _, _) :: _)))) diff --git a/test/files/neg/t5589neg.check b/test/files/neg/t5589neg.check deleted file mode 100644 index b3ff16d7e4..0000000000 --- a/test/files/neg/t5589neg.check +++ /dev/null @@ -1,37 +0,0 @@ -t5589neg.scala:2: warning: `withFilter' method does not yet exist on Either.RightProjection[Int,String], using `filter' method instead - def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2)) - ^ -t5589neg.scala:2: error: constructor cannot be instantiated to expected type; - found : (T1, T2) - required: String - def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2)) - ^ -t5589neg.scala:3: warning: `withFilter' method does not yet exist on Either.RightProjection[Int,String], using `filter' method instead - def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2)) - ^ -t5589neg.scala:3: error: constructor cannot be instantiated to expected type; - found : (T1, T2) - required: String - def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2)) - ^ -t5589neg.scala:4: error: constructor cannot be instantiated to expected type; - found : (T1,) - required: (String, Int) - def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) - ^ -t5589neg.scala:4: error: not found: value y2 - def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) - ^ -t5589neg.scala:5: error: constructor cannot be instantiated to expected type; - found : (T1, T2, T3) - required: (String, Int) - def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) - ^ -t5589neg.scala:5: error: not found: value y1 - def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) - ^ -t5589neg.scala:5: error: not found: value y2 - def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) - ^ -two warnings found -7 errors found diff --git a/test/files/neg/t5589neg.scala b/test/files/neg/t5589neg.scala deleted file mode 100644 index 31ff2c3693..0000000000 --- a/test/files/neg/t5589neg.scala +++ /dev/null @@ -1,6 +0,0 @@ -class A { - def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2)) - def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2)) - def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) - def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) -} diff --git a/test/files/neg/t5589neg2.check b/test/files/neg/t5589neg2.check deleted file mode 100644 index 6af4955a83..0000000000 --- a/test/files/neg/t5589neg2.check +++ /dev/null @@ -1,9 +0,0 @@ -t5589neg2.scala:7: error: constructor cannot be instantiated to expected type; - found : (T1, T2) - required: String - for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok - ^ -t5589neg2.scala:7: error: not found: value d - for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok - ^ -two errors found diff --git a/test/files/neg/t5589neg2.scala b/test/files/neg/t5589neg2.scala deleted file mode 100644 index b7c7ab7218..0000000000 --- a/test/files/neg/t5589neg2.scala +++ /dev/null @@ -1,13 +0,0 @@ -class A { - def f1(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = { - for (((((a, (b, (c, d))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // ok - } - - def f2(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = { - for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok - } - - def f3(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = { - for (((((a, (b, _)), es), fs), gs) <- x) yield (es ::: fs).mkString(", ") // ok - } -} \ No newline at end of file diff --git a/test/files/pos/irrefutable.scala b/test/files/pos/irrefutable.scala deleted file mode 100644 index 0a792b644a..0000000000 --- a/test/files/pos/irrefutable.scala +++ /dev/null @@ -1,22 +0,0 @@ -// The test which this should perform but does not -// is that f1 is recognized as irrefutable and f2 is not -// This can be recognized via the generated classes: -// -// A$$anonfun$f1$1.class -// A$$anonfun$f2$1.class -// A$$anonfun$f2$2.class -// -// The extra one in $f2$ is the filter. -// -// !!! Marking with exclamation points so maybe someday -// this test will be finished. -class A { - case class Foo[T](x: T) - - def f1(xs: List[Foo[Int]]) = { - for (Foo(x: Int) <- xs) yield x - } - def f2(xs: List[Foo[Any]]) = { - for (Foo(x: Int) <- xs) yield x - } -} diff --git a/test/files/pos/t1336.scala b/test/files/pos/t1336.scala deleted file mode 100644 index 63967985c7..0000000000 --- a/test/files/pos/t1336.scala +++ /dev/null @@ -1,10 +0,0 @@ -object Foo { - def foreach( f : ((Int,Int)) => Unit ) { - println("foreach") - f(1,2) - } - - for( (a,b) <- this ) { - println((a,b)) - } -} diff --git a/test/files/pos/t5589.scala b/test/files/pos/t5589.scala deleted file mode 100644 index 69cbb20391..0000000000 --- a/test/files/pos/t5589.scala +++ /dev/null @@ -1,22 +0,0 @@ -class A { - // First three compile. - def f1(x: Either[Int, String]) = x.right map (y => y) - def f2(x: Either[Int, String]) = for (y <- x.right) yield y - def f3(x: Either[Int, (String, Int)]) = x.right map { case (y1, y2) => (y1, y2) } - // Last one fails. - def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) -/** -./a.scala:5: error: constructor cannot be instantiated to expected type; - found : (T1, T2) - required: Either[Nothing,(String, Int)] - def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) - ^ -./a.scala:5: error: not found: value y1 - def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) - ^ -./a.scala:5: error: not found: value y2 - def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) - ^ -three errors found -**/ -} diff --git a/test/files/run/t4574.check b/test/files/run/t4574.check deleted file mode 100644 index a4522fff24..0000000000 --- a/test/files/run/t4574.check +++ /dev/null @@ -1,2 +0,0 @@ -I hereby refute null! -I denounce null as unListLike! diff --git a/test/files/run/t4574.scala b/test/files/run/t4574.scala deleted file mode 100644 index 1dde496aca..0000000000 --- a/test/files/run/t4574.scala +++ /dev/null @@ -1,13 +0,0 @@ -object Test { - val xs: List[(Int, Int)] = List((2, 2), null) - - def expectMatchError[T](msg: String)(body: => T) { - try { body ; assert(false, "Should not succeed.") } - catch { case _: MatchError => println(msg) } - } - - def main(args: Array[String]): Unit = { - expectMatchError("I hereby refute null!")( for ((x, y) <- xs) yield x ) - expectMatchError("I denounce null as unListLike!")( (null: Any) match { case List(_*) => true } ) - } -} -- cgit v1.2.3 From b9e933ffca8108ed86c09a298a44636b714f7a19 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 20 Mar 2012 11:25:39 -0700 Subject: Tweak to string coloring. Concluded String->String would be more useful more often. --- src/compiler/scala/tools/nsc/ast/NodePrinters.scala | 15 +++++++-------- src/compiler/scala/tools/util/color/CString.scala | 19 ++++++++++++++----- src/compiler/scala/tools/util/color/package.scala | 1 + 3 files changed, 22 insertions(+), 13 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index 3d4253f941..07e864879d 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -45,21 +45,20 @@ abstract class NodePrinters { def flagColor = Red def literalColor = Green - override def showFlags(tree: MemberDef) = "" + ( + override def showFlags(tree: MemberDef) = super.showFlags(tree) in flagColor.bright - ) - override def showDefTreeName(tree: DefTree) = "" + ( + + override def showDefTreeName(tree: DefTree) = if (tree.name.isTermName) tree.name.decode in termColor.bright else tree.name.decode in typeColor.bright - ) - override def showName(name: Name): String = "" + ( + + override def showName(name: Name) = if (name == nme.EMPTY || name == tpnme.EMPTY) "" in keywordColor else if (name.isTermName) name.decode in termColor else name.decode in typeColor - ) - override def showLiteral(lit: Literal) = "" + ( + + override def showLiteral(lit: Literal) = super.showLiteral(lit) in literalColor.bright - ) } trait DefaultPrintAST extends PrintAST { diff --git a/src/compiler/scala/tools/util/color/CString.scala b/src/compiler/scala/tools/util/color/CString.scala index d0785eaeff..fa57229f09 100644 --- a/src/compiler/scala/tools/util/color/CString.scala +++ b/src/compiler/scala/tools/util/color/CString.scala @@ -11,18 +11,27 @@ final class CString(val uncolorized: String, val colorized: String) { def visibleLength = uncolorized.length def colorizedLength = colorized.length def show() = Console println colorized - def bytes() = colorized map (ch => ch.toByte) - def > = show() + def bytes() = colorized map (_.toByte) + def >() = show() def append(x: CString): CString = new CString(uncolorized + x.uncolorized, colorized + x.colorized) def +(other: CString): CString = this append other + override def toString = colorized } class CStringOps(str: String) { - /** Enables for example + /** String to String operation. * println("foo" in Red) - * println("foo" in Magenta.bright) + * println("bar" in Magenta.bright) + */ + def in(ansi: Ansi): String = ansi colorize str + + /** Gave in to one bit of punctuation, because everyone adds + * strings with '+' and we need something which higher precedence + * for it to be at all satisfying. + * + * "foo" %> Red + "bar" %> Magenta.bright */ - def in(ansi: Ansi): CString = new CString(str, ansi colorize str) + def %>(ansi: Ansi): CString = new CString(str, in(ansi)) } diff --git a/src/compiler/scala/tools/util/color/package.scala b/src/compiler/scala/tools/util/color/package.scala index 7c7c7dab74..3b3e85751e 100644 --- a/src/compiler/scala/tools/util/color/package.scala +++ b/src/compiler/scala/tools/util/color/package.scala @@ -18,4 +18,5 @@ package object color { case x: AnsiForeground => x.flip } implicit def implicitCStringOps(str: String): CStringOps = new CStringOps(str) + implicit def implicitCString(str: String): CString = new CString(str, str) } -- cgit v1.2.3 From 6d7bcd5818b856d4596b57b7e9f1543b71ed7329 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 20 Mar 2012 14:23:40 -0700 Subject: Lots of tedious warning and tree printing work. Fewer deprecation warnings, prettier trees, prettier symbols, more polished error messages. Oh the interesting people you meet handling warnings, I feel sorry for you all that I get to do it all the time. One of the characters I met invited me into the "Dead Code Society" and that's what I'm doing on Tuesdays now. No of course you haven't, it's a SECRET society. --- .../scala/reflect/internal/Definitions.scala | 18 +++- src/compiler/scala/reflect/internal/Kinds.scala | 23 +++-- src/compiler/scala/reflect/internal/Symbols.scala | 57 +++++------ src/compiler/scala/reflect/internal/Types.scala | 23 +++-- src/compiler/scala/reflect/runtime/ToolBoxes.scala | 4 +- .../scala/tools/nsc/ast/NodePrinters.scala | 90 +++++++++++++----- .../backend/icode/analysis/TypeFlowAnalysis.scala | 2 +- .../scala/tools/nsc/backend/opt/Inliners.scala | 2 +- .../tools/nsc/reporters/ConsoleReporter.scala | 4 +- .../scala/tools/nsc/symtab/clr/TypeParser.scala | 24 ++--- .../scala/tools/nsc/transform/UnCurry.scala | 2 +- .../tools/nsc/typechecker/ContextErrors.scala | 2 +- .../scala/tools/nsc/typechecker/RefChecks.scala | 2 +- .../tools/nsc/typechecker/SyntheticMethods.scala | 2 +- src/library/scala/Specializable.scala | 12 +-- .../scala/collection/GenTraversableOnce.scala | 2 +- .../collection/mutable/ConcurrentTrieMap.scala | 2 +- src/library/scala/concurrent/DelayedLazyVal.scala | 3 +- src/library/scala/reflect/api/Trees.scala | 3 +- src/library/scala/xml/Elem.scala | 8 +- src/library/scala/xml/XML.scala | 17 ++-- test/files/neg/t1364.check | 2 +- test/files/neg/t1477.check | 2 +- test/files/neg/t2070.check | 3 +- test/files/neg/t4044.check | 9 +- test/files/neg/t5152.check | 6 +- test/files/neg/t708.check | 2 +- test/files/neg/t742.check | 3 +- test/files/neg/tcpoly_override.check | 3 +- test/files/neg/tcpoly_typealias.check | 9 +- test/files/neg/tcpoly_variance_enforce.check | 34 ++++--- test/files/run/existentials-in-compiler.check | 104 ++++++++++----------- test/files/run/reify_ann1a.check | 4 +- test/files/run/reify_ann1b.check | 4 +- 34 files changed, 286 insertions(+), 201 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index bd823c3128..a2dd6fc4c3 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -764,9 +764,25 @@ trait Definitions extends reflect.api.StandardDefinitions { else removeRedundantObjects(parents) } + + def typeStringNoPackage(tp: Type) = + "" + tp stripPrefix tp.typeSymbol.enclosingPackage.fullName + "." + + def briefParentsString(parents: List[Type]) = + normalizedParents(parents) map typeStringNoPackage mkString " with " + def parentsString(parents: List[Type]) = normalizedParents(parents) mkString " with " + def typeParamsString(tp: Type) = tp match { + case PolyType(tparams, _) => tparams map (_.defString) mkString ("[", ",", "]") + case _ => "" + } + def valueParamsString(tp: Type) = tp match { + case MethodType(params, _) => params map (_.defString) mkString ("(", ",", ")") + case _ => "" + } + // members of class java.lang.{ Object, String } lazy val Object_## = enterNewMethod(ObjectClass, nme.HASHHASH, Nil, inttype, FINAL) lazy val Object_== = enterNewMethod(ObjectClass, nme.EQ, anyrefparam, booltype, FINAL) @@ -970,7 +986,7 @@ trait Definitions extends reflect.api.StandardDefinitions { case (_, restpe) => NullaryMethodType(restpe) } - msym setInfoAndEnter polyType(tparams, mtpe) + msym setInfoAndEnter genPolyType(tparams, mtpe) } /** T1 means one type parameter. diff --git a/src/compiler/scala/reflect/internal/Kinds.scala b/src/compiler/scala/reflect/internal/Kinds.scala index 23bff950b8..eca63c7c15 100644 --- a/src/compiler/scala/reflect/internal/Kinds.scala +++ b/src/compiler/scala/reflect/internal/Kinds.scala @@ -49,9 +49,15 @@ trait Kinds { private def kindMessage(a: Symbol, p: Symbol)(f: (String, String) => String): String = f(a+qualify(a,p), p+qualify(p,a)) + // Normally it's nicer to print nothing rather than '>: Nothing <: Any' all over + // the place, but here we need it for the message to make sense. private def strictnessMessage(a: Symbol, p: Symbol) = - kindMessage(a, p)("%s's bounds %s are stricter than %s's declared bounds %s".format( - _, a.info, _, p.info)) + kindMessage(a, p)("%s's bounds%s are stricter than %s's declared bounds%s".format( + _, a.info, _, p.info match { + case tb @ TypeBounds(_, _) if tb.isEmptyBounds => " >: Nothing <: Any" + case tb => "" + tb + }) + ) private def varianceMessage(a: Symbol, p: Symbol) = kindMessage(a, p)("%s is %s, but %s is declared %s".format(_, varStr(a), _, varStr(p))) @@ -62,11 +68,16 @@ trait Kinds { _, countAsString(p.typeParams.length)) ) + private def buildMessage(xs: List[SymPair], f: (Symbol, Symbol) => String) = ( + if (xs.isEmpty) "" + else xs map f.tupled mkString ("\n", ", ", "") + ) + def errorMessage(targ: Type, tparam: Symbol): String = ( - (targ+"'s type parameters do not match "+tparam+"'s expected parameters: ") - + (arity map { case (a, p) => arityMessage(a, p) } mkString ", ") - + (variance map { case (a, p) => varianceMessage(a, p) } mkString ", ") - + (strictness map { case (a, p) => strictnessMessage(a, p) } mkString ", ") + (targ+"'s type parameters do not match "+tparam+"'s expected parameters:") + + buildMessage(arity, arityMessage) + + buildMessage(variance, varianceMessage) + + buildMessage(strictness, strictnessMessage) ) } val NoKindErrors = KindErrors(Nil, Nil, Nil) diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 907f7d1237..2ba45c5972 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -2078,47 +2078,32 @@ trait Symbols extends api.Symbols { self: SymbolTable => case s => " in " + s } def fullLocationString: String = toString + locationString - def signatureString: String = if (hasRawInfo) infoString(rawInfo) else "<_>" + def signatureString: String = if (hasRawInfo) infoString(rawInfo) else "<_>" /** String representation of symbol's definition following its name */ final def infoString(tp: Type): String = { - def typeParamsString: String = tp match { - case PolyType(tparams, _) if tparams.nonEmpty => - (tparams map (_.defString)).mkString("[", ",", "]") - case _ => - "" - } - if (isClass) - typeParamsString + " extends " + tp.resultType - else if (isAliasType) - typeParamsString + " = " + tp.resultType - else if (isAbstractType) - typeParamsString + { - tp.resultType match { - case TypeBounds(lo, hi) => - (if (lo.typeSymbol == NothingClass) "" else " >: " + lo) + - (if (hi.typeSymbol == AnyClass) "" else " <: " + hi) - case rtp => - "<: " + rtp - } - } - else if (isModule) - moduleClass.infoString(tp) - else - tp match { - case PolyType(tparams, res) => - typeParamsString + infoString(res) - case NullaryMethodType(res) => - infoString(res) - case MethodType(params, res) => - params.map(_.defString).mkString("(", ",", ")") + infoString(res) - case _ => - ": " + tp + def parents = ( + if (settings.debug.value) parentsString(tp.parents) + else briefParentsString(tp.parents) + ) + if (isType) typeParamsString(tp) + ( + if (isClass) " extends " + parents + else if (isAliasType) " = " + tp.resultType + else tp.resultType match { + case rt @ TypeBounds(_, _) => "" + rt + case rt => " <: " + rt } + ) + else if (isModule) moduleClass.infoString(tp) + else tp match { + case PolyType(tparams, res) => typeParamsString(tp) + infoString(res) + case NullaryMethodType(res) => infoString(res) + case MethodType(params, res) => valueParamsString(tp) + infoString(res) + case _ => ": " + tp + } } - def infosString = infos.toString() - + def infosString = infos.toString def debugLocationString = fullLocationString + " " + debugFlagString def debugFlagString = hasFlagsToString(-1L) def hasFlagsToString(mask: Long): String = flagsToString( @@ -2684,7 +2669,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def rawInfo: Type = NoType protected def doCookJavaRawInfo() {} override def accessBoundary(base: Symbol): Symbol = RootClass - def cloneSymbolImpl(owner: Symbol, newFlags: Long): Symbol = abort() + def cloneSymbolImpl(owner: Symbol, newFlags: Long): Symbol = abort("NoSymbol.clone()") override def originalEnclosingMethod = this override def owner: Symbol = diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 2bb19e2b65..a20853adc8 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -1337,9 +1337,14 @@ trait Types extends api.Types { self: SymbolTable => case TypeBounds(_, _) => that <:< this case _ => lo <:< that && that <:< hi } - def isEmptyBounds = (lo.typeSymbolDirect eq NothingClass) && (hi.typeSymbolDirect eq AnyClass) + private def lowerString = if (emptyLowerBound) "" else " >: " + lo + private def upperString = if (emptyUpperBound) "" else " <: " + hi + private def emptyLowerBound = lo.typeSymbolDirect eq NothingClass + private def emptyUpperBound = hi.typeSymbolDirect eq AnyClass + def isEmptyBounds = emptyLowerBound && emptyUpperBound + // override def isNullable: Boolean = NullClass.tpe <:< lo; - override def safeToString = ">: " + lo + " <: " + hi + override def safeToString = lowerString + upperString override def kind = "TypeBoundsType" } @@ -3321,16 +3326,18 @@ trait Types extends api.Types { self: SymbolTable => * may or may not be poly? (It filched the standard "canonical creator" name.) */ object GenPolyType { - def apply(tparams: List[Symbol], tpe: Type): Type = - if (tparams nonEmpty) typeFun(tparams, tpe) - else tpe // it's okay to be forgiving here + def apply(tparams: List[Symbol], tpe: Type): Type = ( + if (tparams nonEmpty) typeFun(tparams, tpe) + else tpe // it's okay to be forgiving here + ) def unapply(tpe: Type): Option[(List[Symbol], Type)] = tpe match { - case PolyType(tparams, restpe) => Some(tparams, restpe) - case _ => Some(List(), tpe) + case PolyType(tparams, restpe) => Some((tparams, restpe)) + case _ => Some((Nil, tpe)) } } + def genPolyType(params: List[Symbol], tpe: Type): Type = GenPolyType(params, tpe) - @deprecated("use GenPolyType(...) instead") + @deprecated("use genPolyType(...) instead", "2.10.0") def polyType(params: List[Symbol], tpe: Type): Type = GenPolyType(params, tpe) /** A creator for anonymous type functions, where the symbol for the type function still needs to be created. diff --git a/src/compiler/scala/reflect/runtime/ToolBoxes.scala b/src/compiler/scala/reflect/runtime/ToolBoxes.scala index 8cc4d5f788..28f12b378f 100644 --- a/src/compiler/scala/reflect/runtime/ToolBoxes.scala +++ b/src/compiler/scala/reflect/runtime/ToolBoxes.scala @@ -1,6 +1,7 @@ package scala.reflect package runtime +import scala.tools.nsc import scala.tools.nsc.reporters.Reporter import scala.tools.nsc.reporters.StoreReporter import scala.tools.nsc.reporters.AbstractReporter @@ -21,8 +22,7 @@ trait ToolBoxes extends { self: Universe => class ToolBox(val reporter: Reporter = new StoreReporter, val options: String = "") { - class ToolBoxGlobal(settings: scala.tools.nsc.Settings, reporter: scala.tools.nsc.reporters.Reporter) - extends ReflectGlobal(settings, reporter) { + class ToolBoxGlobal(settings0: nsc.Settings, reporter0: nsc.reporters.Reporter) extends ReflectGlobal(settings0, reporter0) { import definitions._ private val trace = scala.tools.nsc.util.trace when settings.debug.value diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index 07e864879d..acbdcd501f 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -65,7 +65,7 @@ abstract class NodePrinters { def showDefTreeName(tree: DefTree) = showName(tree.name) def showFlags(tree: MemberDef) = flagsToString(tree.symbol.flags | tree.mods.flags) def showLiteral(lit: Literal) = lit.value.escapedStringValue - def showTypeTree(tt: TypeTree) = "" + showAttributes(tt) + def showTypeTree(tt: TypeTree) = "" + emptyOrComment(showType(tt)) def showName(name: Name) = name match { case nme.EMPTY | tpnme.EMPTY => "" case name => "\"" + name + "\"" @@ -74,18 +74,18 @@ abstract class NodePrinters { def showSymbol(tree: Tree): String = { val sym = tree.symbol if (sym == null || sym == NoSymbol) "" - else " sym/owner/tpe=%s %s/%s/%s".format(sym.accurateKindString, sym.name, sym.owner, sym.tpe) + else sym.defString + sym.locationString } def showType(tree: Tree): String = { val tpe = tree.tpe if (tpe == null || tpe == NoType) "" - else " tree.tpe=" + tpe + else "tree.tpe=" + tpe } def showAttributes(tree: Tree): String = { if (infolevel == InfoLevel.Quiet) "" else { - try { showSymbol(tree) + showType(tree) trim } + try { List(showSymbol(tree), showType(tree)) filterNot (_ == "") mkString ", " trim } catch { case ex: Throwable => "sym= " + ex.getMessage } } } @@ -115,13 +115,11 @@ abstract class NodePrinters { case Select(_, _) => prefix0 + "." case _ => "" }) - def attrs = showAttributes(tree) match { - case "" => "" - case s => " // " + s - } - prefix + showName(tree.name) + attrs + prefix + showName(tree.name) + emptyOrComment(showAttributes(tree)) } + def emptyOrComment(s: String) = if (s == "") "" else " // " + s + def stringify(tree: Tree): String = { buf.clear() level = 0 @@ -141,7 +139,10 @@ abstract class NodePrinters { buf append " " * level buf append value if (comment != "") { - buf append " // " + if (value != "") + buf append " " + + buf append "// " buf append comment } buf append EOL @@ -179,7 +180,7 @@ abstract class NodePrinters { def applyCommon(tree: Tree, fun: Tree, args: List[Tree]) { printMultiline(tree) { traverse(fun) - traverseList("Nil", _ + " arguments(s)")(args) + traverseList("Nil", "argument")(args) } } @@ -191,17 +192,26 @@ abstract class NodePrinters { indent(body) println(")") } + @inline private def indent[T](body: => T): T = { level += 1 try body finally level -= 1 } - def traverseList(ifEmpty: String, comment: Int => String)(trees: List[Tree]) { + def traverseList(ifEmpty: String, what: String)(trees: List[Tree]) { if (trees.isEmpty) println(ifEmpty) - else - printMultiline("List", comment(trees.length))(trees foreach traverse) + else if (trees.tail.isEmpty) + traverse(trees.head) + else { + printLine("", trees.length + " " + what + "s") + trees foreach traverse + } + } + + def printSingle(tree: Tree, name: Name) { + println(tree.printingPrefix + "(" + showName(name) + ")" + showAttributes(tree)) } def traverse(tree: Tree) { @@ -210,16 +220,44 @@ abstract class NodePrinters { case ApplyDynamic(fun, args) => applyCommon(tree, fun, args) case Apply(fun, args) => applyCommon(tree, fun, args) + case Throw(Ident(name)) => + printSingle(tree, name) + + case Function(vparams, body) => + printMultiline(tree) { + traverseList("()", "parameter")(vparams) + traverse(body) + } + case Try(block, catches, finalizer) => + printMultiline(tree) { + traverse(block) + traverseList("{}", "case")(catches) + if (finalizer ne EmptyTree) + traverse(finalizer) + } + + case Match(selector, cases) => + printMultiline(tree) { + traverse(selector) + traverseList("", "case")(cases) + } + case CaseDef(pat, guard, body) => + printMultiline(tree) { + traverse(pat) + if (guard ne EmptyTree) + traverse(guard) + traverse(body) + } case Block(stats, expr) => printMultiline(tree) { - traverseList("{}", _ + " statement(s)")(stats) + traverseList("{}", "statement")(stats) traverse(expr) } case cd @ ClassDef(mods, name, tparams, impl) => printMultiline(tree) { printModifiers(cd) println(showDefTreeName(cd)) - traverseList("[]", _ + " type parameter(s)")(tparams) + traverseList("[]", "type parameter")(tparams) traverse(impl) } case md @ ModuleDef(mods, name, impl) => @@ -232,14 +270,16 @@ abstract class NodePrinters { printMultiline(tree) { printModifiers(dd) println(showDefTreeName(dd)) - traverseList("[]", _ + " type parameter(s)")(tparams) + traverseList("[]", "type parameter")(tparams) vparamss match { case Nil => println("Nil") case Nil :: Nil => println("List(Nil)") - case xss => - printMultiline("List", xss.length + " parameter list(s)") { - xss foreach (xs => traverseList("()", _ + " parameter(s)")(xs)) - } + case ps :: Nil => + printLine("", "1 parameter list") + ps foreach traverse + case pss => + printLine("", pss.length + " parameter lists") + pss foreach (ps => traverseList("()", "parameter")(ps)) } traverse(tpt) traverse(rhs) @@ -268,14 +308,14 @@ abstract class NodePrinters { } printLine(ps0 mkString ", ", "parents") traverse(self) - traverseList("{}", _ + " statements in body")(body) + traverseList("{}", "statement")(body) } case This(qual) => - println("This(\"" + showName(qual) + "\")" + showAttributes(tree)) + printSingle(tree, qual) case TypeApply(fun, args) => printMultiline(tree) { traverse(fun) - traverseList("[]", _ + " type argument(s)")(args) + traverseList("[]", "type argument")(args) } case tt @ TypeTree() => println(showTypeTree(tt)) @@ -296,7 +336,7 @@ abstract class NodePrinters { printMultiline(tree) { printModifiers(td) println(showDefTreeName(td)) - traverseList("[]", _ + " type parameter(s)")(tparams) + traverseList("[]", "type parameter")(tparams) traverse(rhs) } diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala index 877c51ebc1..d31eafff48 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala @@ -645,7 +645,7 @@ abstract class TypeFlowAnalysis { } while(!done) lastInstruction.clear() - for(b <- isOnPerimeter; val lastIns = b.toList.reverse find isOnWatchlist) { + for (b <- isOnPerimeter; lastIns = b.toList.reverse find isOnWatchlist) { lastInstruction += (b -> lastIns.get.asInstanceOf[opcodes.CALL_METHOD]) } diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index a734b2b92b..dfe9081ee5 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -262,7 +262,7 @@ abstract class Inliners extends SubComponent { def inlineWithoutTFA(inputBlocks: Traversable[BasicBlock], callsites: Function1[BasicBlock, List[opcodes.CALL_METHOD]]): Int = { var inlineCount = 0 import scala.util.control.Breaks._ - for(x <- inputBlocks; val easyCake = callsites(x); if easyCake.nonEmpty) { + for(x <- inputBlocks; easyCake = callsites(x); if easyCake.nonEmpty) { breakable { for(ocm <- easyCake) { assert(ocm.method.isEffectivelyFinal && ocm.method.owner.isEffectivelyFinal) diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala index c76a04c6ba..f5335fb0f5 100644 --- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -8,7 +8,7 @@ package reporters import java.io.{ BufferedReader, IOException, PrintWriter } import util._ -import scala.tools.util.StringOps.countElementsAsString +import scala.tools.util.StringOps /** * This class implements a Reporter that displays messages on a text @@ -40,7 +40,7 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr * @return ... */ private def getCountString(severity: Severity): String = - countElementsAsString((severity).count, label(severity)) + StringOps.countElementsAsString((severity).count, label(severity)) /** Prints the message. */ def printMessage(msg: String) { writer.print(msg + "\n"); writer.flush() } diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala index e11a5a4ad9..4b847fa94a 100644 --- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala @@ -108,7 +108,7 @@ abstract class TypeParser { val method = new ConstructorInfo(declType, attrs, Array[MSILType]()) val flags = Flags.JAVA val owner = clazz - val methodSym = owner.newMethod(NoPosition, nme.CONSTRUCTOR).setFlag(flags) + val methodSym = owner.newMethod(nme.CONSTRUCTOR, NoPosition, flags) val rettype = clazz.tpe val mtype = methodType(Array[MSILType](), rettype); val mInfo = mtype(methodSym) @@ -224,14 +224,14 @@ abstract class TypeParser { if (canBeTakenAddressOf) { clazzBoxed.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata - else polyType(ownTypeParams, classInfoAsInMetadata) ) + else genPolyType(ownTypeParams, classInfoAsInMetadata) ) clazzBoxed.setFlag(flags) val rawValueInfoType = ClassInfoType(definitions.anyvalparam, instanceDefs, clazz) clazz.setInfo( if (ownTypeParams.isEmpty) rawValueInfoType - else polyType(ownTypeParams, rawValueInfoType) ) + else genPolyType(ownTypeParams, rawValueInfoType) ) } else { clazz.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata - else polyType(ownTypeParams, classInfoAsInMetadata) ) + else genPolyType(ownTypeParams, classInfoAsInMetadata) ) } // TODO I don't remember if statics.setInfo and staticModule.setInfo should also know about type params @@ -284,7 +284,7 @@ abstract class TypeParser { else getCLRType(field.FieldType) val owner = if (field.IsStatic()) statics else clazz; - val sym = owner.newValue(NoPosition, name).setFlag(flags).setInfo(fieldType); + val sym = owner.newValue(name, NoPosition, flags).setInfo(fieldType); // TODO: set private within!!! -> look at typechecker/Namers.scala (if (field.IsStatic()) staticDefs else instanceDefs).enter(sym); clrTypes.fields(sym) = field; @@ -313,7 +313,7 @@ abstract class TypeParser { val name: Name = if (gparamsLength == 0) prop.Name else nme.apply; val flags = translateAttributes(getter); val owner: Symbol = if (getter.IsStatic) statics else clazz; - val methodSym = owner.newMethod(NoPosition, name).setFlag(flags) + val methodSym = owner.newMethod(name, NoPosition, flags) val mtype: Type = if (gparamsLength == 0) NullaryMethodType(propType) // .NET properties can't be polymorphic else methodType(getter, getter.ReturnType)(methodSym) methodSym.setInfo(mtype); @@ -337,7 +337,7 @@ abstract class TypeParser { val flags = translateAttributes(setter); val mtype = methodType(setter, definitions.UnitClass.tpe); val owner: Symbol = if (setter.IsStatic) statics else clazz; - val methodSym = owner.newMethod(NoPosition, name).setFlag(flags) + val methodSym = owner.newMethod(name, NoPosition, flags) methodSym.setInfo(mtype(methodSym)) methodSym.setFlag(Flags.ACCESSOR); (if (setter.IsStatic) staticDefs else instanceDefs).enter(methodSym); @@ -424,14 +424,14 @@ abstract class TypeParser { val flags = Flags.JAVA | Flags.FINAL for (cmpName <- ENUM_CMP_NAMES) { - val enumCmp = clazz.newMethod(NoPosition, cmpName) + val enumCmp = clazz.newMethod(cmpName) val enumCmpType = JavaMethodType(enumCmp.newSyntheticValueParams(List(clazz.tpe)), definitions.BooleanClass.tpe) enumCmp.setFlag(flags).setInfo(enumCmpType) instanceDefs.enter(enumCmp) } for (bitLogName <- ENUM_BIT_LOG_NAMES) { - val enumBitLog = clazz.newMethod(NoPosition, bitLogName) + val enumBitLog = clazz.newMethod(bitLogName) val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), clazz.tpe /* was classInfo, infinite typer */) enumBitLog.setFlag(flags).setInfo(enumBitLogType) instanceDefs.enter(enumBitLog) @@ -469,7 +469,7 @@ abstract class TypeParser { val flags = translateAttributes(method); val owner = if (method.IsStatic()) statics else clazz; - val methodSym = owner.newMethod(NoPosition, getName(method)).setFlag(flags) + val methodSym = owner.newMethod(getName(method), NoPosition, flags) /* START CLR generics (snippet 3) */ val newMethodTParams = populateMethodTParams(method, methodSym) /* END CLR generics (snippet 3) */ @@ -480,7 +480,7 @@ abstract class TypeParser { val mtype = methodType(method, rettype); if (mtype == null) return; /* START CLR generics (snippet 4) */ - val mInfo = if (method.IsGeneric) polyType(newMethodTParams, mtype(methodSym)) + val mInfo = if (method.IsGeneric) genPolyType(newMethodTParams, mtype(methodSym)) else mtype(methodSym) /* END CLR generics (snippet 4) */ /* START CLR non-generics (snippet 4) @@ -500,7 +500,7 @@ abstract class TypeParser { } private def createMethod(name: Name, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = { - val methodSym: Symbol = (if (statik) statics else clazz).newMethod(NoPosition, name) + val methodSym: Symbol = (if (statik) statics else clazz).newMethod(name) methodSym.setFlag(flags).setInfo(mtype(methodSym)) (if (statik) staticDefs else instanceDefs).enter(methodSym) if (method != null) diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 03bef83a90..e54e0289bb 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -168,7 +168,7 @@ abstract class UnCurry extends InfoTransform private def nonLocalReturnTry(body: Tree, key: Symbol, meth: Symbol) = { localTyper typed { val extpe = nonLocalReturnExceptionType(meth.tpe.finalResultType) - val ex = meth.newValue(body.pos, nme.ex) setInfo extpe + val ex = meth.newValue(nme.ex, body.pos) setInfo extpe val pat = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(meth.tpe.finalResultType)) val rhs = ( IF ((ex DOT nme.key)() OBJ_EQ Ident(key)) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index ed9fee986f..e37f5784c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -684,7 +684,7 @@ trait ContextErrors { def errMsg = { val location = if (sym.isClassConstructor) owner0 else pre.widen - underlying(sym).fullLocationString + " cannot be accessed in " + + underlyingSymbol(sym).fullLocationString + " cannot be accessed in " + location + explanation } NormalTypeError(tree, errMsg, ErrorKinds.Access) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index ec42d251ff..fa19f380fd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -266,7 +266,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R (if (showLocation) sym1.locationString + (if (sym1.isAliasType) ", which equals "+self.memberInfo(sym1) - else if (sym1.isAbstractType) " with bounds "+self.memberInfo(sym1) + else if (sym1.isAbstractType) " with bounds"+self.memberInfo(sym1) else if (sym1.isTerm) " of type "+self.memberInfo(sym1) else "") else "") diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 2f4eff30d2..da87d38ab0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -136,7 +136,7 @@ trait SyntheticMethods extends ast.TreeDSL { * where that is the given methods first parameter. */ def thatTest(eqmeth: Symbol): Tree = - gen.mkIsInstanceOf(Ident(eqmeth.firstParam), typeCaseType(clazz), true, false) + gen.mkIsInstanceOf(Ident(eqmeth.firstParam), classExistentialType(clazz), true, false) /** (that.asInstanceOf[this.C]) * where that is the given methods first parameter. diff --git a/src/library/scala/Specializable.scala b/src/library/scala/Specializable.scala index 67126b3069..d5e22195d2 100644 --- a/src/library/scala/Specializable.scala +++ b/src/library/scala/Specializable.scala @@ -20,10 +20,10 @@ object Specializable { // Smuggle a list of types by way of a tuple upon which Group is parameterized. class Group[T >: Null](value: T) extends SpecializedGroup { } - final val Primitives = new Group(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit) - final val Everything = new Group(Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit, AnyRef) - final val Bits32AndUp = new Group(Int, Long, Float, Double) - final val Integral = new Group(Byte, Short, Int, Long, Char) - final val AllNumeric = new Group(Byte, Short, Int, Long, Char, Float, Double) - final val BestOfBreed = new Group(Int, Double, Boolean, Unit, AnyRef) + final val Primitives = new Group((Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit)) + final val Everything = new Group((Byte, Short, Int, Long, Char, Float, Double, Boolean, Unit, AnyRef)) + final val Bits32AndUp = new Group((Int, Long, Float, Double)) + final val Integral = new Group((Byte, Short, Int, Long, Char)) + final val AllNumeric = new Group((Byte, Short, Int, Long, Char, Float, Double)) + final val BestOfBreed = new Group((Int, Double, Boolean, Unit, AnyRef)) } diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index f18e2ab6bb..019d3d0785 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -124,7 +124,7 @@ trait GenTraversableOnce[+A] extends Any { * scala> val b = (a /:\ 5)(_+_) * b: Int = 15 * }}}*/ - @deprecated("use fold instead") + @deprecated("use fold instead", "2.10.0") def /:\[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = fold(z)(op) /** Applies a binary operator to a start value and all elements of this $coll, diff --git a/src/library/scala/collection/mutable/ConcurrentTrieMap.scala b/src/library/scala/collection/mutable/ConcurrentTrieMap.scala index 1a44c8e423..cfe1b1950d 100644 --- a/src/library/scala/collection/mutable/ConcurrentTrieMap.scala +++ b/src/library/scala/collection/mutable/ConcurrentTrieMap.scala @@ -1027,7 +1027,7 @@ private[collection] class ConcurrentTrieMapIterator[K, V](var level: Int, privat Seq(this) } - def printDebug { + def printDebug() { println("ctrie iterator") println(stackpos.mkString(",")) println("depth: " + depth) diff --git a/src/library/scala/concurrent/DelayedLazyVal.scala b/src/library/scala/concurrent/DelayedLazyVal.scala index a17153bad5..96a66d83b6 100644 --- a/src/library/scala/concurrent/DelayedLazyVal.scala +++ b/src/library/scala/concurrent/DelayedLazyVal.scala @@ -40,9 +40,8 @@ class DelayedLazyVal[T](f: () => T, body: => Unit) { def apply(): T = if (isDone) complete else f() // TODO replace with scala.concurrent.future { ... } - ops.future { + future { body _isDone = true } - } diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index a8276dc853..466c380cef 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -165,11 +165,12 @@ trait Trees { self: Universe => def foreach(f: Tree => Unit) { new ForeachTreeTraverser(f).traverse(this) } /** Find all subtrees matching predicate `p` */ - def filter(f: Tree => Boolean): List[Tree] = { + def withFilter(f: Tree => Boolean): List[Tree] = { val ft = new FilterTreeTraverser(f) ft.traverse(this) ft.hits.toList } + def filter(f: Tree => Boolean): List[Tree] = withFilter(f) /** Returns optionally first tree (in a preorder traversal) which satisfies predicate `p`, * or None if none exists. diff --git a/src/library/scala/xml/Elem.scala b/src/library/scala/xml/Elem.scala index cc244a5b88..5b6b9f2bb9 100755 --- a/src/library/scala/xml/Elem.scala +++ b/src/library/scala/xml/Elem.scala @@ -23,12 +23,12 @@ object Elem { * @deprecated This factory method is retained for backward compatibility; please use the other one, with which you * can specify your own preference for minimizeEmpty. */ - @deprecated - def apply(prefix: String,label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem = + @deprecated("Use the other apply method in this object", "2.10.0") + def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem = apply(prefix, label, attributes, scope, child.isEmpty, child: _*) - def apply(prefix: String,label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem = - new Elem(prefix,label,attributes,scope, minimizeEmpty, child:_*) + def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem = + new Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*) def unapplySeq(n: Node) = n match { case _: SpecialNode | _: Group => None diff --git a/src/library/scala/xml/XML.scala b/src/library/scala/xml/XML.scala index 4beba91899..f6955c6612 100755 --- a/src/library/scala/xml/XML.scala +++ b/src/library/scala/xml/XML.scala @@ -15,8 +15,7 @@ import java.io.{ InputStream, Reader, StringReader, Writer } import java.nio.channels.Channels import scala.util.control.Exception.ultimately -object Source -{ +object Source { def fromFile(file: File) = new InputSource(new FileInputStream(file)) def fromFile(fd: FileDescriptor) = new InputSource(new FileInputStream(fd)) def fromFile(name: String) = new InputSource(new FileInputStream(name)) @@ -31,13 +30,18 @@ object Source * Governs how empty elements (i.e. those without child elements) should be serialized. */ object MinimizeMode extends Enumeration { - /** Minimize empty tags if they were originally empty when parsed, or if they were constructed with [[scala.xml.Elem]]`#minimizeEmpty` == true */ + /** Minimize empty tags if they were originally empty when parsed, or if they were constructed + * with [[scala.xml.Elem]]`#minimizeEmpty` == true + */ val Default = Value - /** Always minimize empty tags. Note that this may be problematic for XHTML, in which case [[scala.xml.Xhtml]]`#toXhtml` should be used instead. */ + /** Always minimize empty tags. Note that this may be problematic for XHTML, in which + * case [[scala.xml.Xhtml]]`#toXhtml` should be used instead. + */ val Always = Value - /** Never minimize empty tags. */ + /** Never minimize empty tags. + */ val Never = Value } @@ -50,8 +54,7 @@ import Source._ * @author Burak Emir * @version 1.0, 25/04/2005 */ -object XML extends XMLLoader[Elem] -{ +object XML extends XMLLoader[Elem] { val xml = "xml" val xmlns = "xmlns" val namespace = "http://www.w3.org/XML/1998/namespace" diff --git a/test/files/neg/t1364.check b/test/files/neg/t1364.check index 78375333c2..cb8803abdc 100644 --- a/test/files/neg/t1364.check +++ b/test/files/neg/t1364.check @@ -1,4 +1,4 @@ -t1364.scala:9: error: overriding type T in trait A with bounds >: Nothing <: AnyRef{type S[-U]}; +t1364.scala:9: error: overriding type T in trait A with bounds <: AnyRef{type S[-U]}; type T has incompatible type type T = { type S[U] = U } ^ diff --git a/test/files/neg/t1477.check b/test/files/neg/t1477.check index e497637857..72bffa3270 100644 --- a/test/files/neg/t1477.check +++ b/test/files/neg/t1477.check @@ -1,4 +1,4 @@ -t1477.scala:13: error: overriding type V in trait C with bounds >: Nothing <: Middle.this.D; +t1477.scala:13: error: overriding type V in trait C with bounds <: Middle.this.D; type V is a volatile type; cannot override a type with non-volatile upper bound type V <: (D with U) ^ diff --git a/test/files/neg/t2070.check b/test/files/neg/t2070.check index bd049409a8..ef1d08f7b7 100644 --- a/test/files/neg/t2070.check +++ b/test/files/neg/t2070.check @@ -1,5 +1,6 @@ t2070.scala:8: error: The kind of trait T does not conform to the expected kind of type T[X] in trait A. -t2070.B.T's type parameters do not match type T's expected parameters: type X (in object B) has one type parameter, but type X (in trait A) has none +t2070.B.T's type parameters do not match type T's expected parameters: +type X (in object B) has one type parameter, but type X (in trait A) has none trait T[X[_]] ^ one error found diff --git a/test/files/neg/t4044.check b/test/files/neg/t4044.check index 75dcf63bfe..41a04f69b9 100644 --- a/test/files/neg/t4044.check +++ b/test/files/neg/t4044.check @@ -2,15 +2,18 @@ t4044.scala:9: error: AnyRef takes no type parameters, expected: one M[AnyRef] // error, (AnyRef :: *) not kind-conformant to (N :: * -> * -> *) ^ t4044.scala:9: error: kinds of the type arguments () do not conform to the expected kinds of the type parameters (type N). -'s type parameters do not match type N's expected parameters: has no type parameters, but type N has one +'s type parameters do not match type N's expected parameters: + has no type parameters, but type N has one M[AnyRef] // error, (AnyRef :: *) not kind-conformant to (N :: * -> * -> *) ^ t4044.scala:11: error: kinds of the type arguments (Test.A) do not conform to the expected kinds of the type parameters (type N). -Test.A's type parameters do not match type N's expected parameters: type _ has no type parameters, but type O has one +Test.A's type parameters do not match type N's expected parameters: +type _ has no type parameters, but type O has one M[A] // error, (A :: (* -> *) not kind-conformant to (N :: * -> * -> *) ^ t4044.scala:15: error: kinds of the type arguments (Test.C) do not conform to the expected kinds of the type parameters (type N). -Test.C's type parameters do not match type N's expected parameters: type _ has one type parameter, but type _ has none +Test.C's type parameters do not match type N's expected parameters: +type _ has one type parameter, but type _ has none M[C] // error, (C :: (* -> * -> * -> *) not kind-conformant to (N :: * -> * -> *) ^ four errors found diff --git a/test/files/neg/t5152.check b/test/files/neg/t5152.check index 80e0141b64..fd510dbae0 100644 --- a/test/files/neg/t5152.check +++ b/test/files/neg/t5152.check @@ -1,9 +1,11 @@ t5152.scala:7: error: kinds of the type arguments (Test.B) do not conform to the expected kinds of the type parameters (type E) in class A. -Test.B's type parameters do not match type E's expected parameters: type E has one type parameter, but type _ has none +Test.B's type parameters do not match type E's expected parameters: +type E has one type parameter, but type _ has none class B[E[_]] extends A[B] { } // B is depth 2 but A requires 1 ^ t5152.scala:11: error: kinds of the type arguments (Test.B1) do not conform to the expected kinds of the type parameters (type E) in class A1. -Test.B1's type parameters do not match type E's expected parameters: type _ has no type parameters, but type G has one +Test.B1's type parameters do not match type E's expected parameters: +type _ has no type parameters, but type G has one class B1[E[_]] extends A1[B1] // B1 is depth 2 but A1 requires 3 ^ two errors found diff --git a/test/files/neg/t708.check b/test/files/neg/t708.check index 15a9c9ed93..4983aab613 100644 --- a/test/files/neg/t708.check +++ b/test/files/neg/t708.check @@ -1,4 +1,4 @@ -t708.scala:8: error: overriding type S in trait X with bounds >: Nothing <: A.this.T; +t708.scala:8: error: overriding type S in trait X with bounds <: A.this.T; type S has incompatible type override private[A] type S = Any; ^ diff --git a/test/files/neg/t742.check b/test/files/neg/t742.check index f587948ef1..d355715442 100644 --- a/test/files/neg/t742.check +++ b/test/files/neg/t742.check @@ -1,5 +1,6 @@ t742.scala:5: error: kinds of the type arguments (Crash._1,Crash._2,Any) do not conform to the expected kinds of the type parameters (type m,type n,type z). -Crash._1's type parameters do not match type m's expected parameters: type s1 has one type parameter, but type n has two +Crash._1's type parameters do not match type m's expected parameters: +type s1 has one type parameter, but type n has two type p = mul[_1, _2, Any] // mul[_1, _1, Any] needs -Yrecursion ^ one error found diff --git a/test/files/neg/tcpoly_override.check b/test/files/neg/tcpoly_override.check index 95529329e8..dbc3ff9461 100644 --- a/test/files/neg/tcpoly_override.check +++ b/test/files/neg/tcpoly_override.check @@ -1,5 +1,6 @@ tcpoly_override.scala:9: error: The kind of type T does not conform to the expected kind of type T[_] in trait A. -C.this.T's type parameters do not match type T's expected parameters: type T (in class C) has no type parameters, but type T (in trait A) has one +C.this.T's type parameters do not match type T's expected parameters: +type T (in class C) has no type parameters, but type T (in trait A) has one type T = B // This compiles well (@M: ... but it shouldn't) ^ one error found diff --git a/test/files/neg/tcpoly_typealias.check b/test/files/neg/tcpoly_typealias.check index 670add2c04..4beac0e440 100644 --- a/test/files/neg/tcpoly_typealias.check +++ b/test/files/neg/tcpoly_typealias.check @@ -1,13 +1,16 @@ tcpoly_typealias.scala:37: error: The kind of type m does not conform to the expected kind of type m[+x] in trait A. -BInv.this.m's type parameters do not match type m's expected parameters: type x (in trait BInv) is invariant, but type x (in trait A) is declared covariant +BInv.this.m's type parameters do not match type m's expected parameters: +type x (in trait BInv) is invariant, but type x (in trait A) is declared covariant type m[x] = FooCov[x] // error: invariant x in alias def ^ tcpoly_typealias.scala:41: error: The kind of type m does not conform to the expected kind of type m[+x] in trait A. -BCon.this.m's type parameters do not match type m's expected parameters: type x (in trait BCon) is contravariant, but type x (in trait A) is declared covariant +BCon.this.m's type parameters do not match type m's expected parameters: +type x (in trait BCon) is contravariant, but type x (in trait A) is declared covariant type m[-x] = FooCon[x] // error: contravariant x ^ tcpoly_typealias.scala:45: error: The kind of type m does not conform to the expected kind of type m[+x] in trait A. -BBound.this.m's type parameters do not match type m's expected parameters: type x (in trait BBound)'s bounds >: Nothing <: String are stricter than type x (in trait A)'s declared bounds >: Nothing <: Any +BBound.this.m's type parameters do not match type m's expected parameters: +type x (in trait BBound)'s bounds <: String are stricter than type x (in trait A)'s declared bounds >: Nothing <: Any type m[+x <: String] = FooBound[x] // error: x with stricter bound ^ three errors found diff --git a/test/files/neg/tcpoly_variance_enforce.check b/test/files/neg/tcpoly_variance_enforce.check index 44b5b2c15c..3299cc3435 100644 --- a/test/files/neg/tcpoly_variance_enforce.check +++ b/test/files/neg/tcpoly_variance_enforce.check @@ -1,45 +1,57 @@ tcpoly_variance_enforce.scala:15: error: kinds of the type arguments (FooInvar) do not conform to the expected kinds of the type parameters (type m) in trait coll. -FooInvar's type parameters do not match type m's expected parameters: type x (in class FooInvar) is invariant, but type x is declared covariant +FooInvar's type parameters do not match type m's expected parameters: +type x (in class FooInvar) is invariant, but type x is declared covariant object fcollinv extends coll[FooInvar] // error ^ tcpoly_variance_enforce.scala:16: error: kinds of the type arguments (FooContra) do not conform to the expected kinds of the type parameters (type m) in trait coll. -FooContra's type parameters do not match type m's expected parameters: type x (in class FooContra) is contravariant, but type x is declared covariant +FooContra's type parameters do not match type m's expected parameters: +type x (in class FooContra) is contravariant, but type x is declared covariant object fcollcon extends coll[FooContra] // error ^ tcpoly_variance_enforce.scala:17: error: kinds of the type arguments (FooString) do not conform to the expected kinds of the type parameters (type m) in trait coll. -FooString's type parameters do not match type m's expected parameters: type x (in class FooString)'s bounds >: Nothing <: String are stricter than type x's declared bounds >: Nothing <: Any +FooString's type parameters do not match type m's expected parameters: +type x (in class FooString)'s bounds <: String are stricter than type x's declared bounds >: Nothing <: Any object fcollwb extends coll[FooString] // error ^ tcpoly_variance_enforce.scala:19: error: kinds of the type arguments (FooCov) do not conform to the expected kinds of the type parameters (type m) in trait coll2. -FooCov's type parameters do not match type m's expected parameters: type x (in class FooCov) is covariant, but type x is declared contravariant +FooCov's type parameters do not match type m's expected parameters: +type x (in class FooCov) is covariant, but type x is declared contravariant object fcoll2ok extends coll2[FooCov] // error ^ tcpoly_variance_enforce.scala:20: error: kinds of the type arguments (FooInvar) do not conform to the expected kinds of the type parameters (type m) in trait coll2. -FooInvar's type parameters do not match type m's expected parameters: type x (in class FooInvar) is invariant, but type x is declared contravariant +FooInvar's type parameters do not match type m's expected parameters: +type x (in class FooInvar) is invariant, but type x is declared contravariant object fcoll2inv extends coll2[FooInvar] // error ^ tcpoly_variance_enforce.scala:22: error: kinds of the type arguments (FooString) do not conform to the expected kinds of the type parameters (type m) in trait coll2. -FooString's type parameters do not match type m's expected parameters: type x (in class FooString) is covariant, but type x is declared contravarianttype x (in class FooString)'s bounds >: Nothing <: String are stricter than type x's declared bounds >: Nothing <: Any +FooString's type parameters do not match type m's expected parameters: +type x (in class FooString) is covariant, but type x is declared contravariant +type x (in class FooString)'s bounds <: String are stricter than type x's declared bounds >: Nothing <: Any object fcoll2wb extends coll2[FooString] // error ^ tcpoly_variance_enforce.scala:27: error: kinds of the type arguments (FooString) do not conform to the expected kinds of the type parameters (type m) in trait coll3. -FooString's type parameters do not match type m's expected parameters: type x (in class FooString)'s bounds >: Nothing <: String are stricter than type x's declared bounds >: Nothing <: Any +FooString's type parameters do not match type m's expected parameters: +type x (in class FooString)'s bounds <: String are stricter than type x's declared bounds >: Nothing <: Any object fcoll3wb extends coll3[FooString] // error ^ tcpoly_variance_enforce.scala:30: error: kinds of the type arguments (FooString,Int) do not conform to the expected kinds of the type parameters (type m,type y) in trait coll4. -FooString's type parameters do not match type m's expected parameters: type x (in class FooString)'s bounds >: Nothing <: String are stricter than type x's declared bounds >: Nothing <: y +FooString's type parameters do not match type m's expected parameters: +type x (in class FooString)'s bounds <: String are stricter than type x's declared bounds <: y object fcoll4_1 extends coll4[FooString, Int] // error ^ tcpoly_variance_enforce.scala:31: error: kinds of the type arguments (FooString,Any) do not conform to the expected kinds of the type parameters (type m,type y) in trait coll4. -FooString's type parameters do not match type m's expected parameters: type x (in class FooString)'s bounds >: Nothing <: String are stricter than type x's declared bounds >: Nothing <: y +FooString's type parameters do not match type m's expected parameters: +type x (in class FooString)'s bounds <: String are stricter than type x's declared bounds <: y object fcoll4_2 extends coll4[FooString, Any] // error ^ tcpoly_variance_enforce.scala:37: error: kinds of the type arguments (FooInvar) do not conform to the expected kinds of the type parameters (type m) in trait coll. -FooInvar's type parameters do not match type m's expected parameters: type x (in class FooInvar) is invariant, but type x is declared covariant +FooInvar's type parameters do not match type m's expected parameters: +type x (in class FooInvar) is invariant, but type x is declared covariant def x: coll[FooInvar] = sys.error("foo") // error ^ tcpoly_variance_enforce.scala:38: error: kinds of the type arguments (FooContra) do not conform to the expected kinds of the type parameters (type m) in trait coll. -FooContra's type parameters do not match type m's expected parameters: type x (in class FooContra) is contravariant, but type x is declared covariant +FooContra's type parameters do not match type m's expected parameters: +type x (in class FooContra) is contravariant, but type x is declared covariant def y: coll[FooContra] = sys.error("foo") // error ^ 11 errors found diff --git a/test/files/run/existentials-in-compiler.check b/test/files/run/existentials-in-compiler.check index c8040a4cb1..83e3cdf435 100644 --- a/test/files/run/existentials-in-compiler.check +++ b/test/files/run/existentials-in-compiler.check @@ -1,156 +1,156 @@ -abstract trait Bippy[A <: AnyRef,B] extends Object +abstract trait Bippy[A <: AnyRef, B] extends Object extest.Bippy[_ <: AnyRef, _] -abstract trait BippyBud[A <: AnyRef,B,C <: List[A]] extends Object +abstract trait BippyBud[A <: AnyRef, B, C <: List[A]] extends Object extest.BippyBud[A,B,C] forSome { A <: AnyRef; B; C <: List[A] } -abstract trait BippyLike[A <: AnyRef,B <: List[A],This <: extest.BippyLike[A,B,This] with extest.Bippy[A,B]] extends Object +abstract trait BippyLike[A <: AnyRef, B <: List[A], This <: extest.BippyLike[A,B,This] with extest.Bippy[A,B]] extends Object extest.BippyLike[A,B,This] forSome { A <: AnyRef; B <: List[A]; This <: extest.BippyLike[A,B,This] with extest.Bippy[A,B] } -abstract trait Contra[-A >: AnyRef,-B] extends Object +abstract trait Contra[-A >: AnyRef, -B] extends Object extest.Contra[_ >: AnyRef, _] -abstract trait ContraLike[-A >: AnyRef,-B >: List[A]] extends Object +abstract trait ContraLike[-A >: AnyRef, -B >: List[A]] extends Object extest.ContraLike[A,B] forSome { -A >: AnyRef; -B >: List[A] } -abstract trait Cov01[+A <: AnyRef,+B] extends Object +abstract trait Cov01[+A <: AnyRef, +B] extends Object extest.Cov01[_ <: AnyRef, _] -abstract trait Cov02[+A <: AnyRef,B] extends Object +abstract trait Cov02[+A <: AnyRef, B] extends Object extest.Cov02[_ <: AnyRef, _] -abstract trait Cov03[+A <: AnyRef,-B] extends Object +abstract trait Cov03[+A <: AnyRef, -B] extends Object extest.Cov03[_ <: AnyRef, _] -abstract trait Cov04[A <: AnyRef,+B] extends Object +abstract trait Cov04[A <: AnyRef, +B] extends Object extest.Cov04[_ <: AnyRef, _] -abstract trait Cov05[A <: AnyRef,B] extends Object +abstract trait Cov05[A <: AnyRef, B] extends Object extest.Cov05[_ <: AnyRef, _] -abstract trait Cov06[A <: AnyRef,-B] extends Object +abstract trait Cov06[A <: AnyRef, -B] extends Object extest.Cov06[_ <: AnyRef, _] -abstract trait Cov07[-A <: AnyRef,+B] extends Object +abstract trait Cov07[-A <: AnyRef, +B] extends Object extest.Cov07[_ <: AnyRef, _] -abstract trait Cov08[-A <: AnyRef,B] extends Object +abstract trait Cov08[-A <: AnyRef, B] extends Object extest.Cov08[_ <: AnyRef, _] -abstract trait Cov09[-A <: AnyRef,-B] extends Object +abstract trait Cov09[-A <: AnyRef, -B] extends Object extest.Cov09[_ <: AnyRef, _] -abstract trait Cov11[+A <: AnyRef,+B <: List[_]] extends Object +abstract trait Cov11[+A <: AnyRef, +B <: List[_]] extends Object extest.Cov11[_ <: AnyRef, _ <: List[_]] -abstract trait Cov12[+A <: AnyRef,B <: List[_]] extends Object +abstract trait Cov12[+A <: AnyRef, B <: List[_]] extends Object extest.Cov12[_ <: AnyRef, _ <: List[_]] -abstract trait Cov13[+A <: AnyRef,-B <: List[_]] extends Object +abstract trait Cov13[+A <: AnyRef, -B <: List[_]] extends Object extest.Cov13[_ <: AnyRef, _ <: List[_]] -abstract trait Cov14[A <: AnyRef,+B <: List[_]] extends Object +abstract trait Cov14[A <: AnyRef, +B <: List[_]] extends Object extest.Cov14[_ <: AnyRef, _ <: List[_]] -abstract trait Cov15[A <: AnyRef,B <: List[_]] extends Object +abstract trait Cov15[A <: AnyRef, B <: List[_]] extends Object extest.Cov15[_ <: AnyRef, _ <: List[_]] -abstract trait Cov16[A <: AnyRef,-B <: List[_]] extends Object +abstract trait Cov16[A <: AnyRef, -B <: List[_]] extends Object extest.Cov16[_ <: AnyRef, _ <: List[_]] -abstract trait Cov17[-A <: AnyRef,+B <: List[_]] extends Object +abstract trait Cov17[-A <: AnyRef, +B <: List[_]] extends Object extest.Cov17[_ <: AnyRef, _ <: List[_]] -abstract trait Cov18[-A <: AnyRef,B <: List[_]] extends Object +abstract trait Cov18[-A <: AnyRef, B <: List[_]] extends Object extest.Cov18[_ <: AnyRef, _ <: List[_]] -abstract trait Cov19[-A <: AnyRef,-B <: List[_]] extends Object +abstract trait Cov19[-A <: AnyRef, -B <: List[_]] extends Object extest.Cov19[_ <: AnyRef, _ <: List[_]] -abstract trait Cov21[+A,+B] extends Object +abstract trait Cov21[+A, +B] extends Object extest.Cov21[_, _] -abstract trait Cov22[+A,B] extends Object +abstract trait Cov22[+A, B] extends Object extest.Cov22[_, _] -abstract trait Cov23[+A,-B] extends Object +abstract trait Cov23[+A, -B] extends Object extest.Cov23[_, _] -abstract trait Cov24[A,+B] extends Object +abstract trait Cov24[A, +B] extends Object extest.Cov24[_, _] -abstract trait Cov25[A,B] extends Object +abstract trait Cov25[A, B] extends Object extest.Cov25[_, _] -abstract trait Cov26[A,-B] extends Object +abstract trait Cov26[A, -B] extends Object extest.Cov26[_, _] -abstract trait Cov27[-A,+B] extends Object +abstract trait Cov27[-A, +B] extends Object extest.Cov27[_, _] -abstract trait Cov28[-A,B] extends Object +abstract trait Cov28[-A, B] extends Object extest.Cov28[_, _] -abstract trait Cov29[-A,-B] extends Object +abstract trait Cov29[-A, -B] extends Object extest.Cov29[_, _] -abstract trait Cov31[+A,+B,C <: (A, B)] extends Object +abstract trait Cov31[+A, +B, C <: (A, B)] extends Object extest.Cov31[A,B,C] forSome { +A; +B; C <: (A, B) } -abstract trait Cov32[+A,B,C <: (A, B)] extends Object +abstract trait Cov32[+A, B, C <: (A, B)] extends Object extest.Cov32[A,B,C] forSome { +A; B; C <: (A, B) } -abstract trait Cov33[+A,-B,C <: (A, _$10) forSome { type _$10 }] extends Object +abstract trait Cov33[+A, -B, C <: (A, _$10) forSome { type _$10 }] extends Object extest.Cov33[A,B,C] forSome { +A; -B; C <: (A, _$10) forSome { type _$10 } } -abstract trait Cov34[A,+B,C <: (A, B)] extends Object +abstract trait Cov34[A, +B, C <: (A, B)] extends Object extest.Cov34[A,B,C] forSome { A; +B; C <: (A, B) } -abstract trait Cov35[A,B,C <: (A, B)] extends Object +abstract trait Cov35[A, B, C <: (A, B)] extends Object extest.Cov35[A,B,C] forSome { A; B; C <: (A, B) } -abstract trait Cov36[A,-B,C <: (A, _$11) forSome { type _$11 }] extends Object +abstract trait Cov36[A, -B, C <: (A, _$11) forSome { type _$11 }] extends Object extest.Cov36[A,B,C] forSome { A; -B; C <: (A, _$11) forSome { type _$11 } } -abstract trait Cov37[-A,+B,C <: (_$12, B) forSome { type _$12 }] extends Object +abstract trait Cov37[-A, +B, C <: (_$12, B) forSome { type _$12 }] extends Object extest.Cov37[A,B,C] forSome { -A; +B; C <: (_$12, B) forSome { type _$12 } } -abstract trait Cov38[-A,B,C <: (_$13, B) forSome { type _$13 }] extends Object +abstract trait Cov38[-A, B, C <: (_$13, B) forSome { type _$13 }] extends Object extest.Cov38[A,B,C] forSome { -A; B; C <: (_$13, B) forSome { type _$13 } } -abstract trait Cov39[-A,-B,C <: Tuple2[_, _]] extends Object +abstract trait Cov39[-A, -B, C <: Tuple2[_, _]] extends Object extest.Cov39[_, _, _ <: Tuple2[_, _]] -abstract trait Cov41[+A >: Null,+B] extends Object +abstract trait Cov41[+A >: Null, +B] extends Object extest.Cov41[_ >: Null, _] -abstract trait Cov42[+A >: Null,B] extends Object +abstract trait Cov42[+A >: Null, B] extends Object extest.Cov42[_ >: Null, _] -abstract trait Cov43[+A >: Null,-B] extends Object +abstract trait Cov43[+A >: Null, -B] extends Object extest.Cov43[_ >: Null, _] -abstract trait Cov44[A >: Null,+B] extends Object +abstract trait Cov44[A >: Null, +B] extends Object extest.Cov44[_ >: Null, _] -abstract trait Cov45[A >: Null,B] extends Object +abstract trait Cov45[A >: Null, B] extends Object extest.Cov45[_ >: Null, _] -abstract trait Cov46[A >: Null,-B] extends Object +abstract trait Cov46[A >: Null, -B] extends Object extest.Cov46[_ >: Null, _] -abstract trait Cov47[-A >: Null,+B] extends Object +abstract trait Cov47[-A >: Null, +B] extends Object extest.Cov47[_ >: Null, _] -abstract trait Cov48[-A >: Null,B] extends Object +abstract trait Cov48[-A >: Null, B] extends Object extest.Cov48[_ >: Null, _] -abstract trait Cov49[-A >: Null,-B] extends Object +abstract trait Cov49[-A >: Null, -B] extends Object extest.Cov49[_ >: Null, _] -abstract trait Covariant[+A <: AnyRef,+B] extends Object +abstract trait Covariant[+A <: AnyRef, +B] extends Object extest.Covariant[_ <: AnyRef, _] -abstract trait CovariantLike[+A <: AnyRef,+B <: List[A],+This <: extest.CovariantLike[A,B,This] with extest.Covariant[A,B]] extends Object +abstract trait CovariantLike[+A <: AnyRef, +B <: List[A], +This <: extest.CovariantLike[A,B,This] with extest.Covariant[A,B]] extends Object extest.CovariantLike[A,B,This] forSome { +A <: AnyRef; +B <: List[A]; +This <: extest.CovariantLike[A,B,This] with extest.Covariant[A,B] } diff --git a/test/files/run/reify_ann1a.check b/test/files/run/reify_ann1a.check index 97d4848a49..66dce778a8 100644 --- a/test/files/run/reify_ann1a.check +++ b/test/files/run/reify_ann1a.check @@ -1,5 +1,5 @@ { - @new ann(immutable.this.List.apply[String]("1a")) @new ann(immutable.this.List.apply[String]("1b")) class C[@new ann(immutable.this.List.apply[String]("2a")) @new ann(immutable.this.List.apply[String]("2b")) T>: Nothing <: Any] extends scala.AnyRef { + @new ann(immutable.this.List.apply[String]("1a")) @new ann(immutable.this.List.apply[String]("1b")) class C[@new ann(immutable.this.List.apply[String]("2a")) @new ann(immutable.this.List.apply[String]("2b")) T] extends scala.AnyRef { @new ann(immutable.this.List.apply[String]("3a")) @new ann(immutable.this.List.apply[String]("3b")) private[this] val x: T @ann(immutable.this.List.apply[String]("4a")) @ann(immutable.this.List.apply[String]("4b")) = _; def (@new ann(immutable.this.List.apply[String]("3a")) @new ann(immutable.this.List.apply[String]("3b")) x: T @ann(immutable.this.List.apply[String]("4a")) @ann(immutable.this.List.apply[String]("4b"))) = { super.(); @@ -14,7 +14,7 @@ () } { - @ann(immutable.this.List.apply[String]("1a")) @ann(immutable.this.List.apply[String]("1b")) class C[@ann(immutable.this.List.apply[String]("2a")) @ann(immutable.this.List.apply[String]("2b")) T>: Nothing <: Any] extends scala.AnyRef { + @ann(immutable.this.List.apply[String]("1a")) @ann(immutable.this.List.apply[String]("1b")) class C[@ann(immutable.this.List.apply[String]("2a")) @ann(immutable.this.List.apply[String]("2b")) T] extends scala.AnyRef { @ann(immutable.this.List.apply[String]("3a")) @ann(immutable.this.List.apply[String]("3b")) private[this] val x: T @ann(immutable.this.List.apply[String]("4b")) @ann(immutable.this.List.apply[String]("4a")) = _; def (@ann(immutable.this.List.apply[String]("3a")) @ann(immutable.this.List.apply[String]("3b")) x: T @ann(immutable.this.List.apply[String]("4b")) @ann(immutable.this.List.apply[String]("4a"))): C[T] = { C.super.(); diff --git a/test/files/run/reify_ann1b.check b/test/files/run/reify_ann1b.check index ceebc0e2ed..9bc65a422e 100644 --- a/test/files/run/reify_ann1b.check +++ b/test/files/run/reify_ann1b.check @@ -1,5 +1,5 @@ { - @new ann(bar = "1a") @new ann(bar = "1b") class C[@new ann(bar = "2a") @new ann(bar = "2b") T>: Nothing <: Any] extends scala.AnyRef { + @new ann(bar = "1a") @new ann(bar = "1b") class C[@new ann(bar = "2a") @new ann(bar = "2b") T] extends scala.AnyRef { @new ann(bar = "3a") @new ann(bar = "3b") private[this] val x: T @ann(bar = "4a") @ann(bar = "4b") = _; def (@new ann(bar = "3a") @new ann(bar = "3b") x: T @ann(bar = "4a") @ann(bar = "4b")) = { super.(); @@ -14,7 +14,7 @@ () } { - @ann(bar = "1a") @ann(bar = "1b") class C[@ann(bar = "2a") @ann(bar = "2b") T>: Nothing <: Any] extends scala.AnyRef { + @ann(bar = "1a") @ann(bar = "1b") class C[@ann(bar = "2a") @ann(bar = "2b") T] extends scala.AnyRef { @ann(bar = "3a") @ann(bar = "3b") private[this] val x: T @ann(bar = "4b") @ann(bar = "4a") = _; def (@ann(bar = "3a") @ann(bar = "3b") x: T @ann(bar = "4b") @ann(bar = "4a")): C[T] = { C.super.(); -- cgit v1.2.3 From 1c65152c7aaeb3aeaf8a5e39e6fd51e5b4b95836 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 20 Mar 2012 19:56:56 -0700 Subject: Fix for stability failure. Pattern matcher! Totally unrelated to irrefutability, the pattern matcher at some point stopped sorting its lookup switch cases, and the butterfly's wings flapped enough to swap two cases. Now they're sorted in ascending order like they're supposed to be. --- .../tools/nsc/matching/ParallelMatching.scala | 2 +- test/files/pos/lookupswitch.scala | 37 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/lookupswitch.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index 1285e29d4a..be5a9907b8 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -309,7 +309,7 @@ trait ParallelMatching extends ast.TreeDSL } lazy val cases = - for ((tag, indices) <- literalMap.toList) yield { + for ((tag, indices) <- literalMap.toList.sortBy(_._1)) yield { val newRows = indices map (i => addDefaultVars(i)(rest rows i)) val r = remake(newRows ++ defaultRows, includeScrut = false) val r2 = make(r.tvars, r.rows map (x => x rebind bindVars(tag, x.subst))) diff --git a/test/files/pos/lookupswitch.scala b/test/files/pos/lookupswitch.scala new file mode 100644 index 0000000000..33594c0ea6 --- /dev/null +++ b/test/files/pos/lookupswitch.scala @@ -0,0 +1,37 @@ +// There's not a real test here, but on compilation the +// switch should have the cases arranged in order from 1-30. +class A { + def f(x: Int) = x match { + case 6 => "6" + case 18 => "18" + case 7 => "7" + case 2 => "2" + case 13 => "13" + case 11 => "11" + case 26 => "26" + case 27 => "27" + case 29 => "29" + case 25 => "25" + case 9 => "9" + case 17 => "17" + case 16 => "16" + case 1 => "1" + case 30 => "30" + case 15 => "15" + case 22 => "22" + case 19 => "19" + case 23 => "23" + case 8 => "8" + case 28 => "28" + case 5 => "5" + case 12 => "12" + case 10 => "10" + case 21 => "21" + case 24 => "24" + case 4 => "4" + case 14 => "14" + case 3 => "3" + case 20 => "20" + } +} + \ No newline at end of file -- cgit v1.2.3 From fcc63e2b667bdbaddbaacc843de97f8db02f6426 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 20 Mar 2012 20:15:45 -0700 Subject: Restore irrefutability commits. This reverts commit d8ba5d091e5641553b438ef9930a6023a2709dcd. --- src/compiler/scala/reflect/internal/TreeInfo.scala | 20 +++++++++++- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 34 +++++++++----------- .../scala/tools/nsc/typechecker/RefChecks.scala | 2 +- test/files/neg/t5589neg.check | 37 ++++++++++++++++++++++ test/files/neg/t5589neg.scala | 6 ++++ test/files/neg/t5589neg2.check | 9 ++++++ test/files/neg/t5589neg2.scala | 13 ++++++++ test/files/pos/irrefutable.scala | 22 +++++++++++++ test/files/pos/t1336.scala | 10 ++++++ test/files/pos/t5589.scala | 22 +++++++++++++ test/files/run/t4574.check | 2 ++ test/files/run/t4574.scala | 13 ++++++++ 12 files changed, 169 insertions(+), 21 deletions(-) create mode 100644 test/files/neg/t5589neg.check create mode 100644 test/files/neg/t5589neg.scala create mode 100644 test/files/neg/t5589neg2.check create mode 100644 test/files/neg/t5589neg2.scala create mode 100644 test/files/pos/irrefutable.scala create mode 100644 test/files/pos/t1336.scala create mode 100644 test/files/pos/t5589.scala create mode 100644 test/files/run/t4574.check create mode 100644 test/files/run/t4574.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index 769d7a9ed1..ce3de94335 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -17,7 +17,7 @@ abstract class TreeInfo { val global: SymbolTable import global._ - import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass } + import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -312,6 +312,24 @@ abstract class TreeInfo { case _ => false } + /** Is this tree comprised of nothing but identifiers, + * but possibly in bindings or tuples? For instance + * + * foo @ (bar, (baz, quux)) + * + * is a variable pattern; if the structure matches, + * then the remainder is inevitable. + */ + def isVariablePattern(tree: Tree): Boolean = tree match { + case Bind(name, pat) => isVariablePattern(pat) + case Ident(name) => true + case Apply(sel, args) => + ( isReferenceToScalaMember(sel, TupleClass(args.size).name.toTermName) + && (args forall isVariablePattern) + ) + case _ => false + } + /** Is this argument node of the form : _* ? */ def isWildcardStarArg(tree: Tree): Boolean = tree match { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 0d2fbc5372..0bc88d1efd 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -262,29 +262,25 @@ abstract class TreeBuilder { else if (stats.length == 1) stats.head else Block(stats.init, stats.last) + def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = { + val cases = List( + CaseDef(condition, EmptyTree, Literal(Constant(true))), + CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) + ) + val matchTree = makeVisitor(cases, false, scrutineeName) + + atPos(tree.pos)(Apply(Select(tree, nme.withFilter), matchTree :: Nil)) + } + /** Create tree for for-comprehension generator */ def makeGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree): Enumerator = { val pat1 = patvarTransformer.transform(pat) val rhs1 = - if (valeq) rhs - else matchVarPattern(pat1) match { - case Some(_) => - rhs - case None => - atPos(rhs.pos) { - Apply( - Select(rhs, nme.filter), - List( - makeVisitor( - List( - CaseDef(pat1.duplicate, EmptyTree, Literal(Constant(true))), - CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))), - false, - nme.CHECK_IF_REFUTABLE_STRING - ))) - } - } - if (valeq) ValEq(pos, pat1, rhs1) else ValFrom(pos, pat1, rhs1) + if (valeq || treeInfo.isVariablePattern(pat)) rhs + else makeFilter(rhs, pat1.duplicate, nme.CHECK_IF_REFUTABLE_STRING) + + if (valeq) ValEq(pos, pat1, rhs1) + else ValFrom(pos, pat1, rhs1) } def makeParam(pname: TermName, tpe: Tree) = diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index fa19f380fd..d98d248231 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1445,7 +1445,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R private def transformApply(tree: Apply): Tree = tree match { case Apply( - Select(qual, nme.filter), + Select(qual, nme.filter | nme.withFilter), List(Function( List(ValDef(_, pname, tpt, _)), Match(_, CaseDef(pat1, _, _) :: _)))) diff --git a/test/files/neg/t5589neg.check b/test/files/neg/t5589neg.check new file mode 100644 index 0000000000..b3ff16d7e4 --- /dev/null +++ b/test/files/neg/t5589neg.check @@ -0,0 +1,37 @@ +t5589neg.scala:2: warning: `withFilter' method does not yet exist on Either.RightProjection[Int,String], using `filter' method instead + def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:2: error: constructor cannot be instantiated to expected type; + found : (T1, T2) + required: String + def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:3: warning: `withFilter' method does not yet exist on Either.RightProjection[Int,String], using `filter' method instead + def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:3: error: constructor cannot be instantiated to expected type; + found : (T1, T2) + required: String + def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:4: error: constructor cannot be instantiated to expected type; + found : (T1,) + required: (String, Int) + def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:4: error: not found: value y2 + def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:5: error: constructor cannot be instantiated to expected type; + found : (T1, T2, T3) + required: (String, Int) + def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:5: error: not found: value y1 + def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) + ^ +t5589neg.scala:5: error: not found: value y2 + def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) + ^ +two warnings found +7 errors found diff --git a/test/files/neg/t5589neg.scala b/test/files/neg/t5589neg.scala new file mode 100644 index 0000000000..31ff2c3693 --- /dev/null +++ b/test/files/neg/t5589neg.scala @@ -0,0 +1,6 @@ +class A { + def f5(x: Either[Int, String]) = for ((y1, y2: String) <- x.right) yield ((y1, y2)) + def f6(x: Either[Int, String]) = for ((y1, y2: Any) <- x.right) yield ((y1, y2)) + def f7(x: Either[Int, (String, Int)]) = for (y1 @ Tuple1(y2) <- x.right) yield ((y1, y2)) + def f8(x: Either[Int, (String, Int)]) = for ((y1, y2, y3) <- x.right) yield ((y1, y2)) +} diff --git a/test/files/neg/t5589neg2.check b/test/files/neg/t5589neg2.check new file mode 100644 index 0000000000..6af4955a83 --- /dev/null +++ b/test/files/neg/t5589neg2.check @@ -0,0 +1,9 @@ +t5589neg2.scala:7: error: constructor cannot be instantiated to expected type; + found : (T1, T2) + required: String + for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok + ^ +t5589neg2.scala:7: error: not found: value d + for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok + ^ +two errors found diff --git a/test/files/neg/t5589neg2.scala b/test/files/neg/t5589neg2.scala new file mode 100644 index 0000000000..b7c7ab7218 --- /dev/null +++ b/test/files/neg/t5589neg2.scala @@ -0,0 +1,13 @@ +class A { + def f1(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = { + for (((((a, (b, (c, d))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // ok + } + + def f2(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = { + for (((((a, (b, (c, (d1, d2)))), es), fs), gs) <- x) yield (d :: es).mkString(", ") // not ok + } + + def f3(x: List[((((Int, (Double, (Float, String))), List[String]), List[Int]), List[Float])]) = { + for (((((a, (b, _)), es), fs), gs) <- x) yield (es ::: fs).mkString(", ") // ok + } +} \ No newline at end of file diff --git a/test/files/pos/irrefutable.scala b/test/files/pos/irrefutable.scala new file mode 100644 index 0000000000..0a792b644a --- /dev/null +++ b/test/files/pos/irrefutable.scala @@ -0,0 +1,22 @@ +// The test which this should perform but does not +// is that f1 is recognized as irrefutable and f2 is not +// This can be recognized via the generated classes: +// +// A$$anonfun$f1$1.class +// A$$anonfun$f2$1.class +// A$$anonfun$f2$2.class +// +// The extra one in $f2$ is the filter. +// +// !!! Marking with exclamation points so maybe someday +// this test will be finished. +class A { + case class Foo[T](x: T) + + def f1(xs: List[Foo[Int]]) = { + for (Foo(x: Int) <- xs) yield x + } + def f2(xs: List[Foo[Any]]) = { + for (Foo(x: Int) <- xs) yield x + } +} diff --git a/test/files/pos/t1336.scala b/test/files/pos/t1336.scala new file mode 100644 index 0000000000..63967985c7 --- /dev/null +++ b/test/files/pos/t1336.scala @@ -0,0 +1,10 @@ +object Foo { + def foreach( f : ((Int,Int)) => Unit ) { + println("foreach") + f(1,2) + } + + for( (a,b) <- this ) { + println((a,b)) + } +} diff --git a/test/files/pos/t5589.scala b/test/files/pos/t5589.scala new file mode 100644 index 0000000000..69cbb20391 --- /dev/null +++ b/test/files/pos/t5589.scala @@ -0,0 +1,22 @@ +class A { + // First three compile. + def f1(x: Either[Int, String]) = x.right map (y => y) + def f2(x: Either[Int, String]) = for (y <- x.right) yield y + def f3(x: Either[Int, (String, Int)]) = x.right map { case (y1, y2) => (y1, y2) } + // Last one fails. + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) +/** +./a.scala:5: error: constructor cannot be instantiated to expected type; + found : (T1, T2) + required: Either[Nothing,(String, Int)] + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +./a.scala:5: error: not found: value y1 + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +./a.scala:5: error: not found: value y2 + def f4(x: Either[Int, (String, Int)]) = for ((y1, y2) <- x.right) yield ((y1, y2)) + ^ +three errors found +**/ +} diff --git a/test/files/run/t4574.check b/test/files/run/t4574.check new file mode 100644 index 0000000000..a4522fff24 --- /dev/null +++ b/test/files/run/t4574.check @@ -0,0 +1,2 @@ +I hereby refute null! +I denounce null as unListLike! diff --git a/test/files/run/t4574.scala b/test/files/run/t4574.scala new file mode 100644 index 0000000000..1dde496aca --- /dev/null +++ b/test/files/run/t4574.scala @@ -0,0 +1,13 @@ +object Test { + val xs: List[(Int, Int)] = List((2, 2), null) + + def expectMatchError[T](msg: String)(body: => T) { + try { body ; assert(false, "Should not succeed.") } + catch { case _: MatchError => println(msg) } + } + + def main(args: Array[String]): Unit = { + expectMatchError("I hereby refute null!")( for ((x, y) <- xs) yield x ) + expectMatchError("I denounce null as unListLike!")( (null: Any) match { case List(_*) => true } ) + } +} -- cgit v1.2.3 From e2951867f51bf464b07f759662bfc50dfaf48e5b Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 21 Mar 2012 00:44:29 -0700 Subject: Overhauling mixin. Feel the bugs falling away. --- src/compiler/scala/tools/nsc/transform/Mixin.scala | 286 ++++++++++----------- 1 file changed, 140 insertions(+), 146 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 639e060812..adbb7d43d0 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -24,7 +24,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** This map contains a binding (class -> info) if * the class with this info at phase mixinPhase has been treated for mixin composition */ - private val treatedClassInfos = perRunCaches.newMap[Symbol, Type]() + private val treatedClassInfos = perRunCaches.newMap[Symbol, Type]() withDefaultValue NoType /** Map a lazy, mixedin field accessor to it's trait member accessor */ private val initializer = perRunCaches.newMap[Symbol, Symbol] @@ -104,7 +104,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ private val toInterfaceMap = new TypeMap { def apply(tp: Type): Type = mapOver( tp match { - case TypeRef(pre, sym, args) if (sym.isImplClass) => + case TypeRef(pre, sym, args) if sym.isImplClass => typeRef(pre, beforeMixin(sym.toInterface), args) case _ => tp }) @@ -113,10 +113,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** The implementation class corresponding to a currently compiled interface. * todo: try to use Symbol.implClass instead? */ - private def implClass(iface: Symbol): Symbol = { - val impl = iface.implClass - if (impl != NoSymbol) impl else erasure.implClass(iface) - } + private def implClass(iface: Symbol) = iface.implClass orElse (erasure implClass iface) /** Returns the symbol that is accessed by a super-accessor in a mixin composition. * @@ -168,6 +165,29 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { debuglog("new member of " + clazz + ":" + member.defString) clazz.info.decls enter member setFlag MIXEDIN } + def cloneAndAddMember(mixinClass: Symbol, mixinMember: Symbol, clazz: Symbol): Symbol = + addMember(clazz, cloneBeforeErasure(mixinClass, mixinMember, clazz)) + + def cloneBeforeErasure(mixinClass: Symbol, mixinMember: Symbol, clazz: Symbol): Symbol = { + val newSym = beforeErasure { + // since we used `mixinMember` from the interface that represents the trait that's + // being mixed in, have to instantiate the interface type params (that may occur in mixinMember's + // info) as they are seen from the class. We can't use the member that we get from the + // implementation class, as it's a clone that was made after erasure, and thus it does not + // know its info at the beginning of erasure anymore. + // Optimize: no need if mixinClass has no typeparams. + mixinMember cloneSymbol clazz modifyInfo (info => + if (mixinClass.typeParams.isEmpty) info + else (clazz.thisType baseType mixinClass) memberInfo mixinMember + ) + } + // clone before erasure got rid of type info we'll need to generate a javaSig + // now we'll have the type info at (the beginning of) erasure in our history, + // and now newSym has the info that's been transformed to fit this period + // (no need for asSeenFrom as phase.erasedTypes) + // TODO: verify we need the updateInfo and document why + newSym updateInfo (mixinMember.info cloneInfo newSym) + } def needsExpandedSetterName(field: Symbol) = !field.isLazy && ( if (field.isMethod) field.hasStableFlag @@ -181,9 +201,12 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * - lazy fields don't get a setter. */ def addLateInterfaceMembers(clazz: Symbol) { - if ((treatedClassInfos get clazz) != Some(clazz.info)) { + def makeConcrete(member: Symbol) = + member setPos clazz.pos resetFlag (DEFERRED | lateDEFERRED) + + if (treatedClassInfos(clazz) != clazz.info) { treatedClassInfos(clazz) = clazz.info - assert(phase == currentRun.mixinPhase) + assert(phase == currentRun.mixinPhase, phase) /** Create a new getter. Getters are never private or local. They are * always accessors and deferred. */ @@ -210,8 +233,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } clazz.info // make sure info is up to date, so that implClass is set. - val impl = implClass(clazz) - assert(impl != NoSymbol) + val impl = implClass(clazz) orElse abort("No impl class for " + clazz) for (member <- impl.info.decls) { if (!member.isMethod && !member.isModule && !member.isModuleVar) { @@ -242,139 +264,107 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * - for every super accessor in T, add an implementation of that accessor * - for every module in T, add a module */ - def addMixedinMembers(clazz: Symbol, unit : CompilationUnit) { - def cloneBeforeErasure(iface: Symbol, clazz: Symbol, imember: Symbol): Symbol = { - val newSym = beforeErasure { - val res = imember.cloneSymbol(clazz) - // since we used the member (imember) from the interface that represents the trait that's being mixed in, - // have to instantiate the interface type params (that may occur in imember's info) as they are seen from the class - // we can't use the member that we get from the implementation class, as it's a clone that was made after erasure, - // and thus it does not know its info at the beginning of erasure anymore - // optimize: no need if iface has no typeparams - if(iface.typeParams nonEmpty) res.setInfo(clazz.thisType.baseType(iface).memberInfo(imember)) - res - } // clone before erasure got rid of type info we'll need to generate a javaSig - // now we'll have the type info at (the beginning of) erasure in our history, - newSym.updateInfo(imember.info.cloneInfo(newSym)) // and now newSym has the info that's been transformed to fit this period (no need for asSeenFrom as phase.erasedTypes) - newSym // TODO: verify we need the updateInfo and document why - } - - if (!(clazz hasFlag JAVA) && (treatedClassInfos get clazz) != Some(clazz.info)) { - treatedClassInfos(clazz) = clazz.info - - assert(!clazz.isTrait, clazz) - assert(clazz.info.parents.nonEmpty, clazz) - - // first complete the superclass with mixed in members - addMixedinMembers(clazz.superClass, unit) - - //Console.println("adding members of " + clazz.info.baseClasses.tail.takeWhile(superclazz !=) + " to " + clazz);//DEBUG - - /** Mix in members of implementation class mixinClass into class clazz */ - def mixinImplClassMembers(impl: Symbol, iface: Symbol) { - assert( - // XXX this should be impl.isImplClass, except that we get impl classes - // coming through under -optimise which do not agree that they are (because - // the IMPLCLASS flag is unset, I believe.) See ticket #4285. - nme.isImplClassName(impl.name) || impl.isImplClass, - "%s (%s) is not a an implementation class, it cannot mix in %s".format( - impl, impl.defaultFlagString, iface) - ) - if (!impl.isImplClass) { - debugwarn("!!! " + impl + " has an impl class name, but !isImplClass: " + impl.defaultFlagString + ", mixing in " + iface) - } + def addMixedinMembers(clazz: Symbol, unit: CompilationUnit) { + def cloneAndAddMixinMember(mixinClass: Symbol, mixinMember: Symbol): Symbol = ( + cloneAndAddMember(mixinClass, mixinMember, clazz) + setPos clazz.pos + resetFlag DEFERRED | lateDEFERRED + ) - for (member <- impl.info.decls) { - if (isForwarded(member)) { - val imember = member.overriddenSymbol(iface) - // atPhase(currentRun.erasurePhase){ - // println(""+(clazz, iface, clazz.typeParams, iface.typeParams, imember, clazz.thisType.baseType(iface), clazz.thisType.baseType(iface).memberInfo(imember), imember.info substSym(iface.typeParams, clazz.typeParams) )) - // } - // Console.println("mixin member "+member+":"+member.tpe+member.locationString+" "+imember+" "+imember.overridingSymbol(clazz)+" to "+clazz+" with scope "+clazz.info.decls)//DEBUG - if (imember.overridingSymbol(clazz) == NoSymbol && - clazz.info.findMember(member.name, 0, lateDEFERRED, false).alternatives.contains(imember)) { - val member1 = addMember( - clazz, - cloneBeforeErasure(iface, clazz, imember) setPos clazz.pos resetFlag (DEFERRED | lateDEFERRED)) - member1.asInstanceOf[TermSymbol] setAlias member; - } - } + /** Mix in members of implementation class mixinClass into class clazz */ + def mixinImplClassMembers(mixinClass: Symbol, mixinInterface: Symbol) { + assert(mixinClass.isImplClass, "Not an impl class:" + + ((mixinClass.debugLocationString, mixinInterface.debugLocationString))) + + for (member <- mixinClass.info.decls ; if isForwarded(member)) { + val imember = member overriddenSymbol mixinInterface + imember overridingSymbol clazz match { + case NoSymbol => + if (clazz.info.findMember(member.name, 0, lateDEFERRED, false).alternatives contains imember) + cloneAndAddMixinMember(mixinInterface, imember).asInstanceOf[TermSymbol] setAlias member + case _ => } } + } - /** Mix in members of trait mixinClass into class clazz. Also, - * for each lazy field in mixinClass, add a link from its mixed in member to its - * initializer method inside the implclass. - */ - def mixinTraitMembers(mixinClass: Symbol) { - // For all members of a trait's interface do: - for (member <- mixinClass.info.decls) { - if (isConcreteAccessor(member)) { - if (isOverriddenAccessor(member, clazz.info.baseClasses)) { - debugwarn("!!! is overridden val: "+member.fullLocationString) + /** Mix in members of trait mixinClass into class clazz. Also, + * for each lazy field in mixinClass, add a link from its mixed in member to its + * initializer method inside the implclass. + */ + def mixinTraitMembers(mixinClass: Symbol) { + // For all members of a trait's interface do: + for (mixinMember <- mixinClass.info.decls) { + if (isConcreteAccessor(mixinMember)) { + if (isOverriddenAccessor(mixinMember, clazz.info.baseClasses)) + debugwarn("!!! is overridden val: "+mixinMember.fullLocationString) + else { + // mixin field accessors + val mixedInAccessor = cloneAndAddMixinMember(mixinClass, mixinMember) + if (mixinMember.isLazy) { + initializer(mixedInAccessor) = ( + implClass(mixinClass).info.decl(mixinMember.name) + orElse abort("Could not find initializer for " + mixinMember.name) + ) } - else { - // mixin field accessors - val member1 = addMember( - clazz, - cloneBeforeErasure(mixinClass, clazz, member) //member.cloneSymbol(clazz) - setPos clazz.pos - resetFlag (DEFERRED | lateDEFERRED)) - // println("mixing in: "+ (member, member.info, member1.info)) - // atPhase(currentRun.erasurePhase){ - // println("before erasure: "+ (member.info, member1.info)) - // } - if (member.isLazy) { - var init = implClass(mixinClass).info.decl(member.name) - assert(init != NoSymbol, "Could not find initializer for " + member.name) - initializer(member1) = init + if (!mixinMember.isSetter) + mixinMember.tpe match { + case MethodType(Nil, ConstantType(_)) => + // mixinMember is a constant; only getter is needed + ; + case MethodType(Nil, TypeRef(_, UnitClass, _)) => + // mixinMember is a value of type unit. No field needed + ; + case _ => // otherwise mixin a field as well + // atPhase: the private field is moved to the implementation class by erasure, + // so it can no longer be found in the mixinMember's owner (the trait) + val accessed = beforePickler(mixinMember.accessed) + // #3857, need to retain info before erasure when cloning (since cloning only + // carries over the current entry in the type history) + val sym = beforeErasure { + // so we have a type history entry before erasure + clazz.newValue(nme.getterToLocal(mixinMember.name), mixinMember.pos).setInfo(mixinMember.tpe.resultType) + } + sym updateInfo mixinMember.tpe.resultType // info at current phase + + val newFlags = ( + ( PrivateLocal ) + | ( mixinMember getFlag MUTABLE | LAZY) + | ( if (mixinMember.hasStableFlag) 0 else MUTABLE ) + ) + + addMember(clazz, sym setFlag newFlags setAnnotations accessed.annotations) } - if (!member.isSetter) - member.tpe match { - case MethodType(Nil, ConstantType(_)) => - // member is a constant; only getter is needed - ; - case MethodType(Nil, TypeRef(_, UnitClass, _)) => - // member is a value of type unit. No field needed - ; - case _ => // otherwise mixin a field as well - // atPhase: the private field is moved to the implementation class by erasure, - // so it can no longer be found in the member's owner (the trait) - val accessed = beforePickler(member.accessed) - val sym = beforeErasure { // #3857, need to retain info before erasure when cloning (since cloning only carries over the current entry in the type history) - clazz.newValue(nme.getterToLocal(member.name), member.pos).setInfo(member.tpe.resultType) // so we have a type history entry before erasure - } - sym.updateInfo(member.tpe.resultType) // info at current phase - addMember(clazz, - sym - setFlag (PrivateLocal | member.getFlag(MUTABLE | LAZY)) - setFlag (if (!member.hasStableFlag) MUTABLE else 0) - setAnnotations accessed.annotations) - } - } - } - else if (member.isSuperAccessor) { // mixin super accessors - val member1 = addMember(clazz, member.cloneSymbol(clazz)) setPos clazz.pos - assert(member1.alias != NoSymbol, member1) - val alias1 = rebindSuper(clazz, member.alias, mixinClass) - member1.asInstanceOf[TermSymbol] setAlias alias1 - - } - else if (member.isMethod && member.isModule && member.hasNoFlags(LIFTED | BRIDGE)) { - // mixin objects: todo what happens with abstract objects? - addMember(clazz, member.cloneSymbol(clazz, member.flags & ~(DEFERRED | lateDEFERRED)) setPos clazz.pos) } } + else if (mixinMember.isSuperAccessor) { // mixin super accessors + val superAccessor = addMember(clazz, mixinMember.cloneSymbol(clazz)) setPos clazz.pos + assert(superAccessor.alias != NoSymbol, superAccessor) + val alias1 = rebindSuper(clazz, mixinMember.alias, mixinClass) + superAccessor.asInstanceOf[TermSymbol] setAlias alias1 + } + else if (mixinMember.isMethod && mixinMember.isModule && mixinMember.hasNoFlags(LIFTED | BRIDGE)) { + // mixin objects: todo what happens with abstract objects? + addMember(clazz, mixinMember.cloneSymbol(clazz, mixinMember.flags & ~(DEFERRED | lateDEFERRED)) setPos clazz.pos) + } } + } - for (mc <- clazz.mixinClasses) - if (mc hasFlag lateINTERFACE) { - // @SEAN: adding trait tracking so we don't have to recompile transitive closures - unit.depends += mc - addLateInterfaceMembers(mc) - mixinTraitMembers(mc) - mixinImplClassMembers(implClass(mc), mc) - } + if (clazz.isJavaDefined || treatedClassInfos(clazz) == clazz.info) + return + + treatedClassInfos(clazz) = clazz.info + assert(!clazz.isTrait && clazz.info.parents.nonEmpty, clazz) + + // first complete the superclass with mixed in members + addMixedinMembers(clazz.superClass, unit) + + //Console.println("adding members of " + clazz.info.baseClasses.tail.takeWhile(superclazz !=) + " to " + clazz);//DEBUG + for (mc <- clazz.mixinClasses ; if mc hasFlag lateINTERFACE) { + // @SEAN: adding trait tracking so we don't have to recompile transitive closures + unit.depends += mc + addLateInterfaceMembers(mc) + mixinTraitMembers(mc) + mixinImplClassMembers(implClass(mc), mc) } } @@ -436,9 +426,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * Such fields will be nulled after the initializer has memoized the lazy value. */ def singleUseFields(templ: Template): collection.Map[Symbol, List[Symbol]] = { - val usedIn = new mutable.HashMap[Symbol, List[Symbol]] { - override def default(key: Symbol) = Nil - } + val usedIn = mutable.HashMap[Symbol, List[Symbol]]() withDefaultValue Nil object SingleUseTraverser extends Traverser { override def traverse(tree: Tree) { @@ -477,7 +465,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { new MixinTransformer(unit) class MixinTransformer(unit : CompilationUnit) extends Transformer { - /** Within a static implementation method: the parameter referring to the * current object. Undefined everywhere else. */ @@ -1129,18 +1116,20 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * - refer to fields in some implementation class via an abstract method in the interface. */ private def postTransform(tree: Tree): Tree = { + def siteWithinImplClass = currentOwner.enclClass.isImplClass val sym = tree.symbol + // change every node type that refers to an implementation class to its // corresponding interface, unless the node's symbol is an implementation class. if (tree.tpe.typeSymbol.isImplClass && ((sym eq null) || !sym.isImplClass)) tree.tpe = toInterface(tree.tpe) tree match { - case Template(parents, self, body) => + case templ @ Template(parents, self, body) => // change parents of templates to conform to parents in the symbol info val parents1 = currentOwner.info.parents map (t => TypeTree(t) setPos tree.pos) // mark fields which can be nulled afterward - lazyValNullables = nullableFields(tree.asInstanceOf[Template]) withDefaultValue Set() + lazyValNullables = nullableFields(templ) withDefaultValue Set() // add all new definitions to current class or interface treeCopy.Template(tree, parents1, self, addNewDefs(currentOwner, body)) @@ -1179,18 +1168,23 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // - if `m` refers to a trait, insert a static call to the corresponding static // implementation // - otherwise return tree unchanged - if (mix == tpnme.EMPTY && currentOwner.enclClass.isImplClass) - assert(false, "illegal super in trait: " + currentOwner.enclClass + " " + tree); + assert( + !(mix == tpnme.EMPTY && siteWithinImplClass), + "illegal super in trait: " + currentOwner.enclClass + " " + tree + ) + if (sym.owner hasFlag lateINTERFACE) { if (sym.hasAccessorFlag) { assert(args.isEmpty, args) val sym1 = sym.overridingSymbol(currentOwner.enclClass) typedPos(tree.pos)((transformSuper(qual) DOT sym1)()) - } else { + } + else { staticCall(beforePrevPhase(sym.overridingSymbol(implClass(sym.owner)))) } - } else { - assert(!currentOwner.enclClass.isImplClass, currentOwner.enclClass) + } + else { + assert(!siteWithinImplClass, currentOwner.enclClass) tree } case _ => @@ -1208,8 +1202,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // refer to fields in some implementation class via an abstract // getter in the interface. val iface = toInterface(sym.owner.tpe).typeSymbol - val getter = sym.getter(iface) - assert(getter != NoSymbol, sym) + val getter = sym getter iface orElse abort("No getter for " + sym + " in " + iface) + typedPos(tree.pos)((qual DOT getter)()) case Assign(Apply(lhs @ Select(qual, _), List()), rhs) => -- cgit v1.2.3 From 437c626113711ebb5290c129611ee7f4b0c787f5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 21 Mar 2012 14:54:18 +0100 Subject: Allows now private primary constructors in value classes. --- src/compiler/scala/reflect/internal/Symbols.scala | 3 +- .../tools/nsc/transform/ExtensionMethods.scala | 4 ++ .../scala/tools/nsc/typechecker/TreeCheckers.scala | 4 +- .../scala/tools/nsc/typechecker/Typers.scala | 2 - test/files/run/valueclasses-constr.scala | 84 ++++++++++++++++++---- 5 files changed, 77 insertions(+), 20 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 2ba45c5972..f4039cf6d3 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1872,7 +1872,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def unpackLocation: AnyRef = null /** Remove private modifier from symbol `sym`s definition. If `sym` is a - * term symbol rename it by expanding its name to avoid name clashes + * is not a constructor nor a static module rename it by expanding its name to avoid name clashes + * @param base the fully qualified name of this class will be appended if name expansion is needed */ final def makeNotPrivate(base: Symbol) { if (this.isPrivate) { diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 4c3972519a..5104518dd9 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -28,6 +28,9 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { /** the following two members override abstract members in Transform */ val phaseName: String = "extmethods" + /** The following flags may be set by this phase: */ + override def phaseNewFlags: Long = notPRIVATE + def newTransformer(unit: CompilationUnit): Transformer = new Extender(unit) @@ -101,6 +104,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { case Template(_, _, _) => if (currentOwner.isDerivedValueClass) { extensionDefs(currentOwner.companionModule) = new mutable.ListBuffer[Tree] + currentOwner.primaryConstructor.makeNotPrivate(NoSymbol) super.transform(tree) } else if (currentOwner.isStaticOwner) { super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index ed263cbbef..105c2c0b98 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -263,8 +263,8 @@ abstract class TreeCheckers extends Analyzer { tree match { case x: PackageDef => - if (sym.ownerChain contains currentOwner) () - else fail(sym + " owner chain does not contain currentOwner " + currentOwner) + if ((sym.ownerChain contains currentOwner) || currentOwner == definitions.EmptyPackageClass) () + else fail(sym + " owner chain does not contain currentOwner " + currentOwner + sym.ownerChain) case _ => def cond(s: Symbol) = !s.isTerm || s.isMethod || s == sym.owner diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ad48712a32..893941984f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1284,8 +1284,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { unit.error(clazz.pos, "value class may not be a "+ (if (clazz.owner.isTerm) "local class" else "member of another class")) val constr = clazz.primaryConstructor - if ((constr hasFlag (PRIVATE | PROTECTED)) || constr.privateWithin != NoSymbol) - unit.error(constr.pos, "value class must have public primary constructor") clazz.info.decls.toList.filter(acc => acc.isMethod && (acc hasFlag PARAMACCESSOR)) match { case List(acc) => def isUnderlyingAcc(sym: Symbol) = diff --git a/test/files/run/valueclasses-constr.scala b/test/files/run/valueclasses-constr.scala index 7a10299386..652d8d8d22 100644 --- a/test/files/run/valueclasses-constr.scala +++ b/test/files/run/valueclasses-constr.scala @@ -1,25 +1,79 @@ -object TOD { - final val SecondsPerDay = 86400 +package test1 { + object TOD { + final val SecondsPerDay = 86400 - def apply(seconds: Int) = { - val n = seconds % SecondsPerDay - new TOD(if (n >= 0) n else n + SecondsPerDay) - } + def apply(seconds: Int) = { + val n = seconds % SecondsPerDay + new TOD(if (n >= 0) n else n + SecondsPerDay) + } + } + + final class TOD (val secondsOfDay: Int) extends AnyVal { + def hours = secondsOfDay / 3600 + def minutes = (secondsOfDay / 60) % 60 + def seconds = secondsOfDay % 60 + + override def toString = "%02d:%02d:%02d".format(hours, minutes, seconds) + } } +package test2 { + object TOD { + final val SecondsPerDay = 86400 + + def apply(seconds: Int) = { + val n = seconds % SecondsPerDay + new TOD(if (n >= 0) n else n + SecondsPerDay) + } + } + + final class TOD private[test2] (val secondsOfDay: Int) extends AnyVal { + def hours = secondsOfDay / 3600 + def minutes = (secondsOfDay / 60) % 60 + def seconds = secondsOfDay % 60 + + override def toString = "%02d:%02d:%02d".format(hours, minutes, seconds) + } + + object Client { + def newTOD(x: Int) = new TOD(x) + } +} + +package test3 { + object TOD { + final val SecondsPerDay = 86400 + + def apply(seconds: Int) = { + val n = seconds % SecondsPerDay + new TOD(if (n >= 0) n else n + SecondsPerDay) + } + } -final class TOD (val secondsOfDay: Int) extends AnyVal { - def hours = secondsOfDay / 3600 - def minutes = (secondsOfDay / 60) % 60 - def seconds = secondsOfDay % 60 + final class TOD private (val secondsOfDay: Int) extends AnyVal { + def hours = secondsOfDay / 3600 + def minutes = (secondsOfDay / 60) % 60 + def seconds = secondsOfDay % 60 - override def toString = "%02d:%02d:%02d".format(hours, minutes, seconds) + override def toString = "%02d:%02d:%02d".format(hours, minutes, seconds) + } } object Test extends App { - val y: TOD = new TOD(1000) - val x: TOD = TOD(1000) - println(x.hours) - println(x) + val y1: test1.TOD = new test1.TOD(1000) + val y2: test2.TOD = test2.Client.newTOD(1000) + val x1: test1.TOD = test1.TOD(1000) + val x2: test2.TOD = test2.TOD(1000) + val x3: test3.TOD = test3.TOD(1000) + println(y1.minutes) + println(y1) + println(y2.minutes) + println(y2) + println(x1.minutes) + println(x1) + println(x2.minutes) + println(x2) + println(x3.minutes) + println(x3) } -- cgit v1.2.3 From bed3304bf86d88e372309063bd247e4cd9171a6f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 21 Mar 2012 16:43:47 +0100 Subject: Fixed SI-5063. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 8 ++------ test/files/neg/t5063.check | 4 ++++ test/files/neg/t5063.scala | 3 +++ 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 test/files/neg/t5063.check create mode 100644 test/files/neg/t5063.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 893941984f..49ce9712df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1158,6 +1158,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val qtpe = qual.tpe.widen ( !isPastTyper && qual.isTerm + && !qual.isInstanceOf[Super] && ((qual.symbol eq null) || !qual.symbol.isTerm || qual.symbol.isValue) && !qtpe.isError && !qtpe.typeSymbol.isBottomClass @@ -1173,12 +1174,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { ) } - def adaptToMember(qual: Tree, searchTemplate: Type): Tree = - adaptToMember(qual, searchTemplate, true, true) - def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean): Tree = - adaptToMember(qual, searchTemplate, reportAmbiguous, true) - - def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = { if (isAdaptableWithView(qual)) { qual.tpe.widen.normalize match { case et: ExistentialType => diff --git a/test/files/neg/t5063.check b/test/files/neg/t5063.check new file mode 100644 index 0000000000..84690d0a1d --- /dev/null +++ b/test/files/neg/t5063.check @@ -0,0 +1,4 @@ +t5063.scala:2: error: value + is not a member of Object + super.+("") + ^ +one error found diff --git a/test/files/neg/t5063.scala b/test/files/neg/t5063.scala new file mode 100644 index 0000000000..5b34b53fb7 --- /dev/null +++ b/test/files/neg/t5063.scala @@ -0,0 +1,3 @@ +class A { + super.+("") +} -- cgit v1.2.3 From 5f82067bbf5a66f732cee25cdd5e5012438062a0 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 21 Mar 2012 09:17:14 -0700 Subject: Clarifying MethodSynthesis. Tried to paint a picture of how one might synthesize an implicit method to accompany an implicit class. --- .../tools/nsc/typechecker/MethodSynthesis.scala | 54 ++++++++++++++++++---- .../scala/tools/nsc/typechecker/Namers.scala | 4 ++ test/pending/run/implicit-classes.scala | 17 +++++++ 3 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 test/pending/run/implicit-classes.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index f32ad9293c..5287fad3bb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -169,6 +169,16 @@ trait MethodSynthesis { self: Namer => import NamerErrorGen._ + + /** TODO - synthesize method. + */ + def enterImplicitClass(tree: ClassDef) { + /** e.g. + val ClassDef(mods, name, tparams, impl) = tree + val converter = ImplicitClassConverter(tree).createAndEnterSymbol() + ... + */ + } def enterGetterSetter(tree: ValDef) { val ValDef(mods, name, _, _) = tree @@ -230,14 +240,33 @@ trait MethodSynthesis { } trait Derived { + /** The tree from which we are deriving a synthetic member. */ + def tree: Tree def name: TermName def flagsMask: Long def flagsExtra: Long + + /** The tree, symbol, and type completer for the synthetic member. */ def completer(sym: Symbol): Type + def derivedSym: Symbol + def derivedTree: Tree } - trait DerivedFromValDef extends Derived { - /** The declaration from which we are deriving. - */ + + trait DerivedFromMemberDef extends Derived { + def tree: MemberDef + + // Final methods to make the rest easier to reason about. + final def mods = tree.mods + final def basisSym = tree.symbol + final def enclClass = basisSym.enclClass + final def derivedFlags: Long = basisSym.flags & flagsMask | flagsExtra + } + + trait DerivedFromClassDef extends DerivedFromMemberDef { + def tree: ClassDef + } + + trait DerivedFromValDef extends DerivedFromMemberDef { def tree: ValDef /** Which meta-annotation is associated with this kind of entity. @@ -245,14 +274,8 @@ trait MethodSynthesis { */ def category: Symbol - // Final methods to make the rest easier to reason about. - final def mods = tree.mods - final def basisSym = tree.symbol - final def enclClass = basisSym.enclClass - final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter) final def fieldSelection = Select(This(enclClass), basisSym) - final def derivedFlags: Long = basisSym.flags & flagsMask | flagsExtra final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil) def derivedSym: Symbol = tree.symbol @@ -312,6 +335,19 @@ trait MethodSynthesis { private def setterDef = DefDef(derivedSym, setterRhs) override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef } + + /** A synthetic method which performs the implicit conversion implied by + * the declaration of an implicit class. Yet to be written. + */ + case class ImplicitClassConverter(tree: ClassDef) extends DerivedFromClassDef { + def completer(sym: Symbol): Type = ??? + def derivedSym: Symbol = ??? + def derivedTree: DefDef = ??? + def flagsExtra: Long = ??? + def flagsMask: Long = ??? + def name: TermName = ??? + } + case class Getter(tree: ValDef) extends DerivedGetter { def name = tree.name def category = GetterTargetClass diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index c5fb13a5a9..6b27c27652 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -665,6 +665,10 @@ trait Namers extends MethodSynthesis { "If possible, define " + tree.symbol + " in " + owner.skipPackageObject + " instead." ) } + + // Suggested location only. + if (mods.isImplicit) + enterImplicitClass(tree) } // this logic is needed in case typer was interrupted half diff --git a/test/pending/run/implicit-classes.scala b/test/pending/run/implicit-classes.scala new file mode 100644 index 0000000000..02b74de2b0 --- /dev/null +++ b/test/pending/run/implicit-classes.scala @@ -0,0 +1,17 @@ +object O { + implicit class C(s: String) { + def twice = s + s + } +} + +/** +// +// We'd like to augment object O in Namers so that it also has an implicit method +object O { + implicit class C(s: String) { + def twice = s + s + } + implicit def C(s: String): C = new C(s) +} + +**/ -- cgit v1.2.3 From 39938bcc299644807a5d5fa4f4b528f9997fd505 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 21 Mar 2012 12:27:52 -0700 Subject: Fix for SI-5580. Lub explosions mean we can't expect sane types in error messages. Defend against the insane. --- .../tools/nsc/typechecker/TypeDiagnostics.scala | 48 +++++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index e17a271dd0..6efa595d99 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -252,12 +252,17 @@ trait TypeDiagnostics { } "" // no elaborable variance situation found } - def foundReqMsg(found: Type, req: Type): String = ( - withDisambiguation(Nil, found, req)( + // TODO - figure out how to avoid doing any work at all + // when the message will never be seen. I though context.reportErrors + // being false would do that, but if I return "" under + // that condition, I see it. + def foundReqMsg(found: Type, req: Type): String = { + def baseMessage = ( ";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) + "\n required: " + req + existentialContext(req) + explainAlias(req) - ) + explainVariance(found, req) - ) + ) + withDisambiguation(Nil, found, req)(baseMessage) + explainVariance(found, req) + } case class TypeDiag(tp: Type, sym: Symbol) extends Ordered[TypeDiag] { // save the name because it will be mutated until it has been @@ -307,16 +312,37 @@ trait TypeDiagnostics { ) } } - private def typeDiags(locals: List[Symbol], types: Type*): List[TypeDiag] = { - object SymExtractor { - def unapply(x: Any) = x match { - case t @ ConstantType(_) => Some(t -> t.underlying.typeSymbol) - case t @ TypeRef(_, sym, _) => if (locals contains sym) None else Some(t -> sym) - case _ => None + /** This is tricky stuff - we need to traverse types deeply to + * explain name ambiguities, which may occur anywhere. However + * when lub explosions come through it knocks us into an n^2 + * disaster, see SI-5580. This is trying to perform the initial + * filtering of possibly ambiguous types in a sufficiently + * aggressive way that the state space won't explode. + */ + private def typeDiags(locals: List[Symbol], types0: Type*): List[TypeDiag] = { + val types = types0.toList + // If two different type diag instances are seen for a given + // key (either the string representation of a type, or the simple + // name of a symbol) then keep them for disambiguation. + val strings = mutable.Map[String, Set[TypeDiag]]() withDefaultValue Set() + val names = mutable.Map[Name, Set[TypeDiag]]() withDefaultValue Set() + + def record(t: Type, sym: Symbol) = { + val diag = TypeDiag(t, sym) + + strings("" + t) += diag + names(sym.name) += diag + } + for (tpe <- types ; t <- tpe) { + t match { + case ConstantType(_) => record(t, t.underlying.typeSymbol) + case TypeRef(_, sym, _) => record(t, sym) + case _ => () } } - for (tp <- types.toList; SymExtractor(t, sym) <- tp) yield TypeDiag(t, sym) + val collisions = strings.values ++ names.values filter (_.size > 1) + collisions.flatten.toList } /** The distinct pairs from an ordered list. */ -- cgit v1.2.3 From 72c104b8b7b534c70ceaeca19a9239ce2410c76b Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Thu, 22 Mar 2012 16:36:13 +0100 Subject: Fixes SI-5593 After hours of debugging and understanding all the root causes why scaladoc is rummaging through the current directory for classes, I decided to treat the symptoms rather than the cause. I'll document the decision here in case anyone is interested: - in order to find documentable entities, scaladoc exhaustively looks at all the symbols starting from the root package and going all the way to the leaves, visiting all classes in the classpath - if the classpath is empty, scaladoc and scalac add "." as the default classpath. This leads to scaladoc going on a class hunting spree in the current directory. If it happens that the current directory contains package objects and they're not in the expected path (like scala.tools.nsc in build/quick/classes/.../nsc) errors start to pop up - scalac is also affected by this, but to a more limited scope, since it will lazily expand packages whenever necessary. To show this, run the repl in the root directory of the scala source code, after compiling scala: scala> import build.quick.classes.compiler.scala.tools.nsc._ error: error while loading package, class file './build/quick/classes/ compiler/scala/tools/nsc/package.class' contains wrong package package The cleanest solution I can see now is to look at the tree resulting from compilation and use it for guidance -- I will look into this. --- src/compiler/scala/tools/nsc/doc/Settings.scala | 2 ++ src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 3 +++ src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala | 10 +++++++--- .../scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 6 +++++- 4 files changed, 17 insertions(+), 4 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index 6c74951156..45a2ad78b4 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -97,4 +97,6 @@ class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) { docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator ) val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name) + + override def isScaladoc = true } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 5f3c7ec32c..d1224fc79d 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -204,4 +204,7 @@ trait ScalaSettings extends AbsScalaSettings */ val pluginOptions = MultiStringSetting("-P", "plugin:opt", "Pass an option to a plugin") . withHelpSyntax("-P::") + + /** Test whether this is scaladoc we're looking at */ + def isScaladoc = false } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 942ec1fa86..f9ff147e82 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -126,9 +126,13 @@ abstract class SymbolLoaders { ok = false if (settings.debug.value) ex.printStackTrace() val msg = ex.getMessage() - globalError( - if (msg eq null) "i/o error while loading " + root.name - else "error while loading " + root.name + ", " + msg); + // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented + // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects + // that are not in their correct place (see bug for details) + if (!settings.isScaladoc) + globalError( + if (msg eq null) "i/o error while loading " + root.name + else "error while loading " + root.name + ", " + msg); } try { val start = currentTime diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 1cd4ab21ea..61668b1a8a 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -446,7 +446,11 @@ abstract class ClassfileParser { def classNameToSymbol(name: Name): Symbol = { def loadClassSymbol(name: Name): Symbol = { val file = global.classPath findSourceFile ("" +name) getOrElse { - warning("Class " + name + " not found - continuing with a stub.") + // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented + // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects + // that are not in their correct place (see bug for details) + if (!settings.isScaladoc) + warning("Class " + name + " not found - continuing with a stub.") return NoSymbol.newClass(name.toTypeName) } val completer = new global.loaders.ClassfileLoader(file) -- cgit v1.2.3 From a1f5933e43831c25fed55969e4d5d2fd530daa2e Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 20 Feb 2012 15:20:54 +0100 Subject: minimalist refactoring to allow annotating trees not tested for performance, but shouldn't add any memory overhead see https://github.com/adriaanm/scala/commit/44362c557f for an example usage --- src/compiler/scala/reflect/runtime/Universe.scala | 5 +++++ src/compiler/scala/tools/nsc/symtab/Positions.scala | 9 +++++++++ src/compiler/scala/tools/nsc/util/Position.scala | 13 ++++++++++++- src/library/scala/reflect/api/Positions.scala | 18 +++++++++++++++--- src/library/scala/reflect/api/Trees.scala | 19 +++++++++++++------ 5 files changed, 54 insertions(+), 10 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/runtime/Universe.scala b/src/compiler/scala/reflect/runtime/Universe.scala index 324fee87ab..7a5dda3d8e 100644 --- a/src/compiler/scala/reflect/runtime/Universe.scala +++ b/src/compiler/scala/reflect/runtime/Universe.scala @@ -39,6 +39,11 @@ class Universe extends SymbolTable { definitions.AnyValClass // force it. + type TreeAnnotation = Position + val NoTreeAnnotation: TreeAnnotation = NoPosition + def positionToAnnotation(pos: Position): TreeAnnotation = pos // TODO + def annotationToPosition(annot: TreeAnnotation): Position = annot //TODO + // establish root association to avoid cyclic dependency errors later classToScala(classOf[java.lang.Object]).initialize diff --git a/src/compiler/scala/tools/nsc/symtab/Positions.scala b/src/compiler/scala/tools/nsc/symtab/Positions.scala index c96c709fb0..680b06f8ce 100644 --- a/src/compiler/scala/tools/nsc/symtab/Positions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Positions.scala @@ -14,6 +14,15 @@ self: scala.tools.nsc.symtab.SymbolTable => type Position = scala.tools.nsc.util.Position val NoPosition = scala.tools.nsc.util.NoPosition + type TreeAnnotation = scala.tools.nsc.util.TreeAnnotation + val NoTreeAnnotation: TreeAnnotation = NoPosition + def positionToAnnotation(pos: Position): TreeAnnotation = pos + def annotationToPosition(annot: TreeAnnotation): Position = annot.pos + override def _checkSetAnnotation(tree: Tree, annot: TreeAnnotation): Unit = { + if (tree.pos != NoPosition && tree.pos != annot.pos) debugwarn("Overwriting annotation "+ tree.annotation +" of tree "+ tree +" with annotation "+ annot) + // if ((tree.annotation.isInstanceOf[scala.tools.nsc.util.Position] || !annot.isInstanceOf[scala.tools.nsc.util.Position]) && tree.isInstanceOf[Block]) + // println("Updating block from "+ tree.annotation +" to "+ annot) + } def focusPos(pos: Position): Position = pos.focus def isRangePos(pos: Position): Boolean = pos.isRange def showPos(pos: Position): String = pos.show diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index 53c767be20..bc74717366 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -33,6 +33,16 @@ object Position { } } +/** + * A tree does not directly store a Position. It stores a TreeAnnotation, which /typically/ is a Position. + * + * A TreeAnnotion may encompass more than just a Position, though, depending on the exact subclass of TreeAnnotation. + */ +trait TreeAnnotation { + def pos: Position +} + + /** The Position class and its subclasses represent positions of ASTs and symbols. * Except for NoPosition and FakePos, every position refers to a SourceFile * and to an offset in the sourcefile (its `point`). For batch compilation, @@ -77,7 +87,8 @@ object Position { * pos.makeTransparent converts an opaque range position into a transparent one. * returns all other positions unchanged. */ -trait Position { +trait Position extends TreeAnnotation { + def pos: Position = this /** An optional value containing the source file referred to by this position, or * None if not defined. diff --git a/src/library/scala/reflect/api/Positions.scala b/src/library/scala/reflect/api/Positions.scala index 181e7c1525..4c8c33bec8 100644 --- a/src/library/scala/reflect/api/Positions.scala +++ b/src/library/scala/reflect/api/Positions.scala @@ -1,9 +1,21 @@ package scala.reflect package api -trait Positions { +trait Positions { self: Universe => + /** TreeAnnotation is a generalisation of Position. + * + * TreeAnnotation cannot be an upperbound of Position since the corresponding classes + * must live outside of the universe for backwards compatibility (see scala.tools.nsc.util.Position). + * Thus, represent subtyping as coercions. + * + * Typically, positionToAnnotation is the identity, and annotationToPosition returns annot.pos + */ + type TreeAnnotation // <: { def pos: Position } + val NoTreeAnnotation: TreeAnnotation + implicit def positionToAnnotation(pos: Position): TreeAnnotation // = pos + def annotationToPosition(annot: TreeAnnotation): Position // = annot.pos + def _checkSetAnnotation(tree: Tree, annot: TreeAnnotation): Unit = () // check that annot may overwrite tree.annot - type Position + type Position // <: TreeAnnotation, but not practical to enforce this (would require moving Position, SourceFile, Reporter,... into the universe) val NoPosition: Position - } \ No newline at end of file diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index 466c380cef..4ee13adf52 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -74,17 +74,24 @@ trait Trees { self: Universe => val id = nodeCount nodeCount += 1 - private[this] var rawpos: Position = NoPosition - /** Prefix under which to print this tree type. Defaults to product * prefix (e.g. DefTree) but because that is used in reification * it cannot be altered without breaking reflection. */ def printingPrefix = productPrefix - def pos = rawpos - def pos_=(pos: Position) = rawpos = pos - def setPos(pos: Position): this.type = { rawpos = pos; this } + def pos: Position = annotationToPosition(rawannot) + def pos_=(pos: Position): Unit = annotation = pos + def setPos(newpos: Position): this.type = { pos = newpos; this } + + private[this] var rawannot: TreeAnnotation = NoTreeAnnotation + def annotation: TreeAnnotation = rawannot + def annotation_=(annot: TreeAnnotation): Unit = { + _checkSetAnnotation(this, annot) + rawannot = annot + } + + def setAnnotation(annot: TreeAnnotation): this.type = { annotation = annot; this } private[this] var rawtpe: Type = _ @@ -223,7 +230,7 @@ trait Trees { self: Universe => duplicateTree(this).asInstanceOf[this.type] private[scala] def copyAttrs(tree: Tree): this.type = { - pos = tree.pos + annotation = tree.annotation tpe = tree.tpe if (hasSymbol) symbol = tree.symbol this -- cgit v1.2.3 From 2c58044335c2c7dd0458b8d402570cdb31f6e314 Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Thu, 22 Mar 2012 18:48:41 +0100 Subject: Fixed SI-5599 Eliminated the spurious warnings and made the code easier to follow. --- src/compiler/scala/tools/nsc/ast/DocComments.scala | 59 ++++++++++++---------- 1 file changed, 33 insertions(+), 26 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index 678f7b3028..d3f4688d4b 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -229,31 +229,36 @@ trait DocComments { self: Global => * 1. It takes longer to run compared to merge * 2. The inheritdoc annotation should not be used very often, as building the comment from pieces severely * impacts performance + * + * @param parent The source (or parent) comment + * @param child The child (overriding member or usecase) comment + * @param sym The child symbol + * @return The child comment with the inheritdoc sections expanded */ - def expandInheritdoc(src: String, dst: String, sym: Symbol): String = - if (dst.indexOf("@inheritdoc") == -1) - dst + def expandInheritdoc(parent: String, child: String, sym: Symbol): String = + if (child.indexOf("@inheritdoc") == -1) + child else { - val srcSections = tagIndex(src) - val dstSections = tagIndex(dst) - val srcTagMap = sectionTagMap(src, srcSections) - val srcNamedParams = Map() + - ("@param" -> paramDocs(src, "@param", srcSections)) + - ("@tparam" -> paramDocs(src, "@tparam", srcSections)) + - ("@throws" -> paramDocs(src, "@throws", srcSections)) + val parentSections = tagIndex(parent) + val childSections = tagIndex(child) + val parentTagMap = sectionTagMap(parent, parentSections) + val parentNamedParams = Map() + + ("@param" -> paramDocs(parent, "@param", parentSections)) + + ("@tparam" -> paramDocs(parent, "@tparam", parentSections)) + + ("@throws" -> paramDocs(parent, "@throws", parentSections)) val out = new StringBuilder - def replaceInheritdoc(src: String, dst: String) = - if (dst.indexOf("@inheritdoc") == -1) - dst - else - dst.replaceAllLiterally("@inheritdoc", src) + def replaceInheritdoc(childSection: String, parentSection: => String) = + if (childSection.indexOf("@inheritdoc") == -1) + childSection + else + childSection.replaceAllLiterally("@inheritdoc", parentSection) - def getSourceSection(section: (Int, Int)): String = { + def getParentSection(section: (Int, Int)): String = { - def getSectionHeader = extractSectionTag(dst, section) match { - case param@("@param"|"@tparam"|"@throws") => param + " " + extractSectionParam(dst, section) + def getSectionHeader = extractSectionTag(child, section) match { + case param@("@param"|"@tparam"|"@throws") => param + " " + extractSectionParam(child, section) case other => other } @@ -261,17 +266,19 @@ trait DocComments { self: Global => paramMap.get(param) match { case Some(section) => // Cleanup the section tag and parameter - val sectionTextBounds = extractSectionText(src, section) - cleanupSectionText(src.substring(sectionTextBounds._1, sectionTextBounds._2)) + val sectionTextBounds = extractSectionText(parent, section) + cleanupSectionText(parent.substring(sectionTextBounds._1, sectionTextBounds._2)) case None => reporter.info(sym.pos, "The \"" + getSectionHeader + "\" annotation of the " + sym + " comment contains @inheritdoc, but the corresponding section in the parent is not defined.", true) "" } - dst.substring(section._1, section._1 + 7) match { - case param@("@param "|"@tparam"|"@throws") => sectionString(extractSectionParam(dst, section), srcNamedParams(param.trim)) - case _ => sectionString(extractSectionTag(dst, section), srcTagMap) + child.substring(section._1, section._1 + 7) match { + case param@("@param "|"@tparam"|"@throws") => + sectionString(extractSectionParam(child, section), parentNamedParams(param.trim)) + case _ => + sectionString(extractSectionTag(child, section), parentTagMap) } } @@ -283,11 +290,11 @@ trait DocComments { self: Global => // Append main comment out.append("/**") - out.append(replaceInheritdoc(mainComment(src, srcSections), mainComment(dst, dstSections))) + out.append(replaceInheritdoc(mainComment(child, childSections), mainComment(parent, parentSections))) // Append sections - for (section <- dstSections) - out.append(replaceInheritdoc(getSourceSection(section), dst.substring(section._1, section._2))) + for (section <- childSections) + out.append(replaceInheritdoc(child.substring(section._1, section._2), getParentSection(section))) out.append("*/") out.toString -- cgit v1.2.3 From 600b78374789fba87ef72e927207135653e03c1c Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 22 Mar 2012 21:15:57 +0100 Subject: Scaladoc: improved usecases, display full signature. Closes #5291 Makes the full signature of a use case member available, but still hidden under a drop-down menu. --- .../scala/tools/nsc/doc/html/page/Template.scala | 13 +++++++++++- .../tools/nsc/doc/html/resource/lib/template.css | 24 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index bd5f98bab7..0c9c88c0f5 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -345,6 +345,17 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } } + val fullSignature: Seq[scala.xml.Node] = { + mbr match { + case nte: NonTemplateMemberEntity if nte.isUseCase => +
+ Full Signature +
{ signature(nte.useCaseOf.get,true) }
+
+ case _ => NodeSeq.Empty + } + } + val selfType: Seq[scala.xml.Node] = mbr match { case dtpl: DocTemplateEntity if (isSelf && !dtpl.selfType.isEmpty && !isReduced) =>
Self Type
@@ -466,7 +477,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } // end attributes block vals --- - val attributesInfo = attributes ++ definitionClasses ++ selfType ++ annotations ++ deprecation ++ migration ++ sourceLink ++ mainComment + val attributesInfo = attributes ++ definitionClasses ++ fullSignature ++ selfType ++ annotations ++ deprecation ++ migration ++ sourceLink ++ mainComment val attributesBlock = if (attributesInfo.isEmpty) NodeSeq.Empty diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css index 4f552b7169..6fb83c133e 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css @@ -340,6 +340,30 @@ div.members > ol > li:last-child { color: darkgreen; } +.full-signature-usecase h4 span { + font-size: 10pt; +} + +.full-signature-usecase > #signature { + padding-top: 0px; +} + +#template .full-signature-usecase > .signature.closed { + background: none; +} + +#template .full-signature-usecase > .signature.opened { + background: none; +} + +.full-signature-block { + padding: 5px 0 0; + border-top: 1px solid #EBEBEB; + margin-top: 5px; + margin-bottom: 5px; +} + + /* Comments text formating */ .cmt {} -- cgit v1.2.3