diff options
184 files changed, 4808 insertions, 3353 deletions
@@ -281,6 +281,8 @@ INITIALISATION <!-- Resolve maven dependencies --> <target name="init.maven.jars" depends="init.maven.tasks"> + <!-- This target has an issue where if the user directory does not exist, we BOMB. ugh. --> + <mkdir dir="${user.home}/.m2/repository"/> <artifact:dependencies pathId="dependency.classpath" filesetId="dependency.fileset"> <!--<dependency groupId="com.typesafe" artifactId="config" version="0.4.0"/>--> </artifact:dependencies> @@ -2178,7 +2180,7 @@ DOCUMENTATION docfooter="epfl" docsourceurl="${scaladoc.url}€{FILE_PATH}.scala#L1" docUncompilable="${src.dir}/library-aux" - skipPackages="scala.reflect.macros" + skipPackages="scala.reflect.macros.internal" sourcepath="${src.dir}" classpathref="pack.classpath" addparams="${scalac.args.all}" diff --git a/lib/scala-compiler-src.jar.desired.sha1 b/lib/scala-compiler-src.jar.desired.sha1 index 95973402e6..bfbcc5a7a3 100644 --- a/lib/scala-compiler-src.jar.desired.sha1 +++ b/lib/scala-compiler-src.jar.desired.sha1 @@ -1 +1 @@ -67257bb7ce010e2ceac800d737e202cfbfc2a1f6 ?scala-compiler-src.jar +7bad3afb569e2c57d1b299b61e0d3c0fa1b1d8d9 ?scala-compiler-src.jar diff --git a/lib/scala-compiler.jar.desired.sha1 b/lib/scala-compiler.jar.desired.sha1 index 7f023e00be..ef2063bc3d 100644 --- a/lib/scala-compiler.jar.desired.sha1 +++ b/lib/scala-compiler.jar.desired.sha1 @@ -1 +1 @@ -c52277de9e76187f34a5ae073e5d2aacc592ac50 ?scala-compiler.jar +5f73d19a6ec70f67e6d8a2f08cde3551e51b2b79 ?scala-compiler.jar diff --git a/lib/scala-library-src.jar.desired.sha1 b/lib/scala-library-src.jar.desired.sha1 index f2d287f120..0709415f43 100644 --- a/lib/scala-library-src.jar.desired.sha1 +++ b/lib/scala-library-src.jar.desired.sha1 @@ -1 +1 @@ -40b9c97e0c3abea4c460b73ca178f60a3bfea242 ?scala-library-src.jar +26c6c1cf7be57afa519ec641fb78e16a771a534a ?scala-library-src.jar diff --git a/lib/scala-library.jar.desired.sha1 b/lib/scala-library.jar.desired.sha1 index 99a06c2024..edc3614c6d 100644 --- a/lib/scala-library.jar.desired.sha1 +++ b/lib/scala-library.jar.desired.sha1 @@ -1 +1 @@ -0a8e0e39e1e6713323a2e659aab743ccfa57c071 ?scala-library.jar +4f14def979133788dd91117a203a40e35cd24528 ?scala-library.jar diff --git a/lib/scala-reflect-src.jar.desired.sha1 b/lib/scala-reflect-src.jar.desired.sha1 index 1752dec7f6..a7a9eddd93 100644 --- a/lib/scala-reflect-src.jar.desired.sha1 +++ b/lib/scala-reflect-src.jar.desired.sha1 @@ -1 +1 @@ -d1abf389fbf5dfc95889a181d28f94a6779c6165 ?scala-reflect-src.jar +60c61467bc992752bf81bc0ca783a9d6a72db588 ?scala-reflect-src.jar diff --git a/lib/scala-reflect.jar.desired.sha1 b/lib/scala-reflect.jar.desired.sha1 index 4cc99695e0..a1732595b8 100644 --- a/lib/scala-reflect.jar.desired.sha1 +++ b/lib/scala-reflect.jar.desired.sha1 @@ -1 +1 @@ -02b44e860b9b9abd1353bbaa1004b3f0004dd0b3 ?scala-reflect.jar +b3764f8b2f7719d1fbfaf9767a3a68f6ef7ad876 ?scala-reflect.jar diff --git a/src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala b/src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala index f45dde8a85..4e4d88c0be 100644 --- a/src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala +++ b/src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala @@ -2,5 +2,6 @@ package scala.reflect.macros package runtime import scala.reflect.internal.util.Position +import scala.util.control.ControlThrowable -class AbortMacroException(val pos: Position, val msg: String) extends Throwable(msg) +class AbortMacroException(val pos: Position, val msg: String) extends Throwable(msg) with ControlThrowable
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/runtime/Aliases.scala b/src/compiler/scala/reflect/macros/runtime/Aliases.scala index 8b742755cd..5e15b61dbd 100644 --- a/src/compiler/scala/reflect/macros/runtime/Aliases.scala +++ b/src/compiler/scala/reflect/macros/runtime/Aliases.scala @@ -21,6 +21,8 @@ trait Aliases { override type TypeTag[T] = universe.TypeTag[T] override val AbsTypeTag = universe.AbsTypeTag override val TypeTag = universe.TypeTag + override def absTypeTag[T](implicit attag: AbsTypeTag[T]) = attag override def typeTag[T](implicit ttag: TypeTag[T]) = ttag + override def absTypeOf[T](implicit attag: AbsTypeTag[T]): Type = attag.tpe override def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe }
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala b/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala index 69fa416f8f..9f328eb82b 100644 --- a/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala +++ b/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala @@ -35,13 +35,10 @@ trait FrontEnds extends scala.tools.reflect.FrontEnds { def error(pos: Position, msg: String): Unit = callsiteTyper.context.error(pos, msg) - def abort(pos: Position, msg: String): Nothing = { - callsiteTyper.context.error(pos, msg) - throw new AbortMacroException(pos, msg) - } + def abort(pos: Position, msg: String): Nothing = throw new AbortMacroException(pos, msg) def interactive(): Unit = universe.reporter match { - case reporter: tools.nsc.reporters.AbstractReporter => reporter.displayPrompt() + case reporter: scala.tools.nsc.reporters.AbstractReporter => reporter.displayPrompt() case _ => () } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/reflect/macros/runtime/Settings.scala b/src/compiler/scala/reflect/macros/runtime/Settings.scala index c602532ea4..e9d9a17b81 100644 --- a/src/compiler/scala/reflect/macros/runtime/Settings.scala +++ b/src/compiler/scala/reflect/macros/runtime/Settings.scala @@ -17,7 +17,7 @@ trait Settings { setCompilerSettings(options.split(" ").toList) def setCompilerSettings(options: List[String]): this.type = { - val settings = new tools.nsc.Settings(_ => ()) + val settings = new scala.tools.nsc.Settings(_ => ()) settings.copyInto(universe.settings) this } diff --git a/src/compiler/scala/reflect/macros/util/Traces.scala b/src/compiler/scala/reflect/macros/util/Traces.scala index 078cd2b74f..d16916b753 100644 --- a/src/compiler/scala/reflect/macros/util/Traces.scala +++ b/src/compiler/scala/reflect/macros/util/Traces.scala @@ -2,7 +2,7 @@ package scala.reflect.macros package util trait Traces { - def globalSettings: tools.nsc.Settings + def globalSettings: scala.tools.nsc.Settings val macroDebugLite = globalSettings.YmacrodebugLite.value val macroDebugVerbose = globalSettings.YmacrodebugVerbose.value @@ -10,4 +10,4 @@ trait Traces { val macroTraceVerbose = scala.tools.nsc.util.trace when macroDebugVerbose @inline final def macroLogLite(msg: => Any) { if (macroDebugLite || macroDebugVerbose) println(msg) } @inline final def macroLogVerbose(msg: => Any) { if (macroDebugVerbose) println(msg) } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 574129a2f1..80e9ede271 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1220,8 +1220,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) var reportedFeature = Set[Symbol]() - /** A flag whether macro expansions failed */ - var macroExpansionFailed = false + /** Has any macro expansion used a fallback during this run? */ + var seenMacroExpansionsFallingBack = false /** To be initialized from firstPhase. */ private var terminalPhase: Phase = NoPhase @@ -1511,7 +1511,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) else { allConditionalWarnings foreach (_.summarize) - if (macroExpansionFailed) + if (seenMacroExpansionsFallingBack) warning("some macros could not be expanded and code fell back to overridden methods;"+ "\nrecompiling with generated classfiles on the classpath might help.") // todo: migrationWarnings diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index 2ee38d4b91..e90d779885 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -95,7 +95,7 @@ trait TreeDSL { def INT_>= (other: Tree) = fn(target, getMember(IntClass, nme.GE), other) def INT_== (other: Tree) = fn(target, getMember(IntClass, nme.EQ), other) def INT_!= (other: Tree) = fn(target, getMember(IntClass, nme.NE), other) - + // generic operations on ByteClass, IntClass, LongClass def GEN_| (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.OR), other) def GEN_& (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.AND), other) @@ -234,7 +234,7 @@ trait TreeDSL { } class DefTreeStart(val name: Name) extends TreeVODDStart with DefCreator { def tparams: List[TypeDef] = Nil - def vparamss: List[List[ValDef]] = List(Nil) + def vparamss: List[List[ValDef]] = ListOfNil } class IfStart(cond: Tree, thenp: Tree) { diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index ca8a377c6f..b22681e52b 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -44,7 +44,7 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { setInfo analyzer.ImportType(qual) ) val importTree = ( - Import(qual, List(ImportSelector(nme.WILDCARD, -1, null, -1))) + Import(qual, ImportSelector.wildList) setSymbol importSym setType NoType ) @@ -58,7 +58,7 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) { // This can't be "Annotated(New(UncheckedClass), expr)" because annotations // are very picky about things and it crashes the compiler with "unexpected new". - Annotated(New(scalaDot(UncheckedClass.name), List(Nil)), expr) + Annotated(New(scalaDot(UncheckedClass.name), ListOfNil), expr) } // if it's a Match, mark the selector unchecked; otherwise nothing. def mkUncheckedMatch(tree: Tree) = tree match { @@ -357,8 +357,8 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL { */ def mkSynchronizedCheck(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = mkSynchronizedCheck(mkAttributedThis(clazz), cond, syncBody, stats) - - def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = + + def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = Block(mkSynchronized( attrThis, If(cond, Block(syncBody: _*), EmptyTree)) :: diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 6f17a7d625..085ce82025 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -50,7 +50,7 @@ trait Trees extends reflect.internal.Trees { self: Global => /** Array selection <qualifier> . <name> only used during erasure */ case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type) - extends TermTree with RefTree + extends RefTree with TermTree /** Derived value class injection (equivalent to: new C(arg) after easure); only used during erasure * The class C is stored as the symbol of the tree node. @@ -111,7 +111,7 @@ trait Trees extends reflect.internal.Trees { self: Global => if (body forall treeInfo.isInterfaceMember) List() else List( atPos(wrappingPos(superPos, lvdefs)) ( - DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, List(), List(List()), TypeTree(), Block(lvdefs, Literal(Constant()))))) + DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, List(), ListOfNil, TypeTree(), Block(lvdefs, Literal(Constant()))))) } else { // convert (implicit ... ) to ()(implicit ... ) if its the only parameter section if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 391874c488..17bea7f796 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -192,7 +192,7 @@ self => override def blockExpr(): Tree = skipBraces(EmptyTree) - override def templateBody(isPre: Boolean) = skipBraces((emptyValDef, List(EmptyTree))) + override def templateBody(isPre: Boolean) = skipBraces((emptyValDef, EmptyTree.asList)) } class UnitParser(val unit: global.CompilationUnit, patches: List[BracePatch]) extends SourceFileParser(unit.source) { @@ -395,7 +395,7 @@ self => NoMods, nme.CONSTRUCTOR, Nil, - List(Nil), + ListOfNil, TypeTree(), Block(List(Apply(gen.mkSuperSelect, Nil)), Literal(Constant(()))) ) @@ -404,7 +404,7 @@ self => def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String))) def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.argv, mainParamType, EmptyTree)) def mainSetArgv = List(ValDef(NoMods, nme.args, TypeTree(), Ident(nme.argv))) - def mainNew = makeNew(Nil, emptyValDef, stmts, List(Nil), NoPosition, NoPosition) + def mainNew = makeNew(Nil, emptyValDef, stmts, ListOfNil, NoPosition, NoPosition) def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), Block(mainSetArgv, mainNew)) // object Main @@ -1302,23 +1302,25 @@ self => placeholderParams = placeholderParams ::: savedPlaceholderParams res } + - def expr0(location: Int): Tree = in.token match { + def expr0(location: Int): Tree = (in.token: @scala.annotation.switch) match { case IF => - atPos(in.skipToken()) { + def parseIf = atPos(in.skipToken()) { val cond = condExpr() newLinesOpt() val thenp = expr() val elsep = if (in.token == ELSE) { in.nextToken(); expr() } - else Literal(Constant()) + else Literal(Constant()) If(cond, thenp, elsep) } + parseIf case TRY => - atPos(in.skipToken()) { + def parseTry = atPos(in.skipToken()) { val body = in.token match { - case LBRACE => inBracesOrUnit(block()) - case LPAREN => inParensOrUnit(expr()) - case _ => expr() + case LBRACE => inBracesOrUnit(block()) + case LPAREN => inParensOrUnit(expr()) + case _ => expr() } def catchFromExpr() = List(makeCatchFromExpr(expr())) val catches: List[CaseDef] = @@ -1332,32 +1334,39 @@ self => } } val finalizer = in.token match { - case FINALLY => in.nextToken() ; expr() - case _ => EmptyTree + case FINALLY => in.nextToken(); expr() + case _ => EmptyTree } Try(body, catches, finalizer) } + parseTry case WHILE => - val start = in.offset - atPos(in.skipToken()) { - val lname: Name = freshTermName(nme.WHILE_PREFIX) - val cond = condExpr() - newLinesOpt() - val body = expr() - makeWhile(lname, cond, body) + def parseWhile = { + val start = in.offset + atPos(in.skipToken()) { + val lname: Name = freshTermName(nme.WHILE_PREFIX) + val cond = condExpr() + newLinesOpt() + val body = expr() + makeWhile(lname, cond, body) + } } + parseWhile case DO => - val start = in.offset - atPos(in.skipToken()) { - val lname: Name = freshTermName(nme.DO_WHILE_PREFIX) - val body = expr() - if (isStatSep) in.nextToken() - accept(WHILE) - val cond = condExpr() - makeDoWhile(lname, body, cond) + def parseDo = { + val start = in.offset + atPos(in.skipToken()) { + val lname: Name = freshTermName(nme.DO_WHILE_PREFIX) + val body = expr() + if (isStatSep) in.nextToken() + accept(WHILE) + val cond = condExpr() + makeDoWhile(lname, body, cond) + } } + parseDo case FOR => - atPos(in.skipToken()) { + def parseFor = atPos(in.skipToken()) { val enums = if (in.token == LBRACE) inBracesOrNil(enumerators()) else inParensOrNil(enumerators()) @@ -1369,70 +1378,78 @@ self => makeFor(enums, expr()) } } + parseFor case RETURN => - atPos(in.skipToken()) { - Return(if (isExprIntro) expr() else Literal(Constant())) - } + def parseReturn = + atPos(in.skipToken()) { + Return(if (isExprIntro) expr() else Literal(Constant())) + } + parseReturn case THROW => - atPos(in.skipToken()) { - Throw(expr()) - } + def parseThrow = + atPos(in.skipToken()) { + Throw(expr()) + } + parseThrow case IMPLICIT => implicitClosure(in.skipToken(), location) case _ => - var t = postfixExpr() - if (in.token == EQUALS) { - t match { - case Ident(_) | Select(_, _) | Apply(_, _) => - t = atPos(t.pos.startOrPoint, in.skipToken()) { makeAssign(t, expr()) } - case _ => - } - } else if (in.token == COLON) { - t = stripParens(t) - val colonPos = in.skipToken() - if (in.token == USCORE) { - //todo: need to handle case where USCORE is a wildcard in a type - val uscorePos = in.skipToken() - if (isIdent && in.name == nme.STAR) { - in.nextToken() - t = atPos(t.pos.startOrPoint, colonPos) { - Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) }) - } - } else { - syntaxErrorOrIncomplete("`*' expected", true) + def parseOther = { + var t = postfixExpr() + if (in.token == EQUALS) { + t match { + case Ident(_) | Select(_, _) | Apply(_, _) => + t = atPos(t.pos.startOrPoint, in.skipToken()) { makeAssign(t, expr()) } + case _ => } - } else if (in.token == AT) { - t = (t /: annotations(skipNewLines = false)) (makeAnnotated) - } else { - t = atPos(t.pos.startOrPoint, colonPos) { - val tpt = typeOrInfixType(location) - if (isWildcard(t)) - (placeholderParams: @unchecked) match { - case (vd @ ValDef(mods, name, _, _)) :: rest => - placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest + } else if (in.token == COLON) { + t = stripParens(t) + val colonPos = in.skipToken() + if (in.token == USCORE) { + //todo: need to handle case where USCORE is a wildcard in a type + val uscorePos = in.skipToken() + if (isIdent && in.name == nme.STAR) { + in.nextToken() + t = atPos(t.pos.startOrPoint, colonPos) { + Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) }) } - // this does not correspond to syntax, but is necessary to - // accept closures. We might restrict closures to be between {...} only. - Typed(t, tpt) + } else { + syntaxErrorOrIncomplete("`*' expected", true) + } + } else if (in.token == AT) { + t = (t /: annotations(skipNewLines = false))(makeAnnotated) + } else { + t = atPos(t.pos.startOrPoint, colonPos) { + val tpt = typeOrInfixType(location) + if (isWildcard(t)) + (placeholderParams: @unchecked) match { + case (vd @ ValDef(mods, name, _, _)) :: rest => + placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest + } + // this does not correspond to syntax, but is necessary to + // accept closures. We might restrict closures to be between {...} only. + Typed(t, tpt) + } } + } else if (in.token == MATCH) { + t = atPos(t.pos.startOrPoint, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses()))) + } + // in order to allow anonymous functions as statements (as opposed to expressions) inside + // templates, we have to disambiguate them from self type declarations - bug #1565 + // The case still missed is unparenthesized single argument, like "x: Int => x + 1", which + // may be impossible to distinguish from a self-type and so remains an error. (See #1564) + def lhsIsTypedParamList() = t match { + case Parens(xs) if xs forall (_.isInstanceOf[Typed]) => true + case _ => false } - } else if (in.token == MATCH) { - t = atPos(t.pos.startOrPoint, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses()))) - } - // in order to allow anonymous functions as statements (as opposed to expressions) inside - // templates, we have to disambiguate them from self type declarations - bug #1565 - // The case still missed is unparenthesized single argument, like "x: Int => x + 1", which - // may be impossible to distinguish from a self-type and so remains an error. (See #1564) - def lhsIsTypedParamList() = t match { - case Parens(xs) if xs forall (_.isInstanceOf[Typed]) => true - case _ => false - } - if (in.token == ARROW && (location != InTemplate || lhsIsTypedParamList)) { - t = atPos(t.pos.startOrPoint, in.skipToken()) { - Function(convertToParams(t), if (location != InBlock) expr() else block()) + if (in.token == ARROW && (location != InTemplate || lhsIsTypedParamList)) { + t = atPos(t.pos.startOrPoint, in.skipToken()) { + Function(convertToParams(t), if (location != InBlock) expr() else block()) + } } + stripParens(t) } - stripParens(t) + parseOther } /** {{{ @@ -2093,7 +2110,7 @@ self => def annotationExpr(): Tree = atPos(in.offset) { val t = exprSimpleType() if (in.token == LPAREN) New(t, multipleArgumentExprs()) - else New(t, List(Nil)) + else New(t, ListOfNil) } /* -------- PARAMETERS ------------------------------------------- */ @@ -2732,10 +2749,10 @@ self => def templateParents(isTrait: Boolean): (List[Tree], List[List[Tree]]) = { val parents = new ListBuffer[Tree] += startAnnotType() val argss = ( - // TODO: the insertion of List(Nil) here is where "new Foo" becomes + // TODO: the insertion of ListOfNil here is where "new Foo" becomes // indistinguishable from "new Foo()". if (in.token == LPAREN && !isTrait) multipleArgumentExprs() - else List(Nil) + else ListOfNil ) while (in.token == WITH) { @@ -2773,7 +2790,7 @@ self => val (self1, body1) = templateBodyOpt(traitParentSeen = isTrait) (parents, argss, self1, earlyDefs ::: body1) } else { - (List(), List(List()), self, body) + (List(), ListOfNil, self, body) } } else { val (parents, argss) = templateParents(isTrait = isTrait) @@ -2800,7 +2817,7 @@ self => else { newLineOptWhenFollowedBy(LBRACE) val (self, body) = templateBodyOpt(traitParentSeen = false) - (List(), List(List()), self, body) + (List(), ListOfNil, self, body) } ) def anyrefParents() = { @@ -2813,7 +2830,7 @@ self => def anyvalConstructor() = ( // Not a well-formed constructor, has to be finished later - see note // regarding AnyVal constructor in AddInterfaces. - DefDef(NoMods, nme.CONSTRUCTOR, Nil, List(Nil), TypeTree(), Block(Nil, Literal(Constant()))) + DefDef(NoMods, nme.CONSTRUCTOR, Nil, ListOfNil, TypeTree(), Block(Nil, Literal(Constant()))) ) val tstart0 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart @@ -2834,7 +2851,7 @@ self => * @param isPre specifies whether in early initializer (true) or not (false) */ def templateBody(isPre: Boolean) = inBraces(templateStatSeq(isPre = isPre)) match { - case (self, Nil) => (self, List(EmptyTree)) + case (self, Nil) => (self, EmptyTree.asList) case result => result } def templateBodyOpt(traitParentSeen: Boolean): (ValDef, List[Tree]) = { @@ -2938,7 +2955,7 @@ self => /** Informal - for the repl and other direct parser accessors. */ def templateStats(): List[Tree] = templateStatSeq(isPre = false)._2 match { - case Nil => List(EmptyTree) + case Nil => EmptyTree.asList case stats => stats } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index f99b9a66c9..e6bf43fe93 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -360,16 +360,19 @@ trait Scanners extends ScannersCommon { if (ch == '"' && token == IDENTIFIER) token = INTERPOLATIONID case '<' => // is XMLSTART? - val last = if (charOffset >= 2) buf(charOffset - 2) else ' ' - nextChar() - last match { - case ' '|'\t'|'\n'|'{'|'('|'>' if isNameStart(ch) || ch == '!' || ch == '?' => - token = XMLSTART - case _ => - // Console.println("found '<', but last is '"+in.last+"'"); // DEBUG - putChar('<') - getOperatorRest() + def fetchLT = { + val last = if (charOffset >= 2) buf(charOffset - 2) else ' ' + nextChar() + last match { + case ' ' | '\t' | '\n' | '{' | '(' | '>' if isNameStart(ch) || ch == '!' || ch == '?' => + token = XMLSTART + case _ => + // Console.println("found '<', but last is '"+in.last+"'"); // DEBUG + putChar('<') + getOperatorRest() + } } + fetchLT case '~' | '!' | '@' | '#' | '%' | '^' | '*' | '+' | '-' | /*'<' | */ '>' | '?' | ':' | '=' | '&' | @@ -386,78 +389,87 @@ trait Scanners extends ScannersCommon { getOperatorRest() } case '0' => - putChar(ch) - nextChar() - if (ch == 'x' || ch == 'X') { + def fetchZero = { + putChar(ch) nextChar() - base = 16 - } - else { - /** What should leading 0 be in the future? It is potentially dangerous - * to let it be base-10 because of history. Should it be an error? Is - * there a realistic situation where one would need it? - */ - if (isDigit(ch)) { - if (opt.future) syntaxError("Non-zero numbers may not have a leading zero.") - else deprecationWarning("Treating numbers with a leading zero as octal is deprecated.") + if (ch == 'x' || ch == 'X') { + nextChar() + base = 16 + } else { + /** + * What should leading 0 be in the future? It is potentially dangerous + * to let it be base-10 because of history. Should it be an error? Is + * there a realistic situation where one would need it? + */ + if (isDigit(ch)) { + if (opt.future) syntaxError("Non-zero numbers may not have a leading zero.") + else deprecationWarning("Treating numbers with a leading zero as octal is deprecated.") + } + base = 8 } - base = 8 + getNumber() } - getNumber() + fetchZero case '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => base = 10 getNumber() case '`' => getBackquotedIdent() case '\"' => - if (token == INTERPOLATIONID) { - nextRawChar() - if (ch == '\"') { + def fetchDoubleQuote = { + if (token == INTERPOLATIONID) { nextRawChar() if (ch == '\"') { nextRawChar() - getStringPart(multiLine = true) - sepRegions = STRINGPART :: sepRegions // indicate string part - sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part + if (ch == '\"') { + nextRawChar() + getStringPart(multiLine = true) + sepRegions = STRINGPART :: sepRegions // indicate string part + sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part + } else { + token = STRINGLIT + strVal = "" + } } else { - token = STRINGLIT - strVal = "" + getStringPart(multiLine = false) + sepRegions = STRINGLIT :: sepRegions // indicate single line string part } } else { - getStringPart(multiLine = false) - sepRegions = STRINGLIT :: sepRegions // indicate single line string part - } - } else { - nextChar() - if (ch == '\"') { nextChar() if (ch == '\"') { - nextRawChar() - getRawStringLit() + nextChar() + if (ch == '\"') { + nextRawChar() + getRawStringLit() + } else { + token = STRINGLIT + strVal = "" + } } else { - token = STRINGLIT - strVal = "" + getStringLit() } - } else { - getStringLit() } } + fetchDoubleQuote case '\'' => - nextChar() - if (isIdentifierStart(ch)) - charLitOr(getIdentRest) - else if (isOperatorPart(ch) && (ch != '\\')) - charLitOr(getOperatorRest) - else { - getLitChar() - if (ch == '\'') { - nextChar() - token = CHARLIT - setStrVal() - } else { - syntaxError("unclosed character literal") + def fetchSingleQuote = { + nextChar() + if (isIdentifierStart(ch)) + charLitOr(getIdentRest) + else if (isOperatorPart(ch) && (ch != '\\')) + charLitOr(getOperatorRest) + else { + getLitChar() + if (ch == '\'') { + nextChar() + token = CHARLIT + setStrVal() + } else { + syntaxError("unclosed character literal") + } } } + fetchSingleQuote case '.' => nextChar() if ('0' <= ch && ch <= '9') { @@ -488,22 +500,25 @@ trait Scanners extends ScannersCommon { nextChar() } case _ => - if (ch == '\u21D2') { - nextChar(); token = ARROW - } else if (ch == '\u2190') { - nextChar(); token = LARROW - } else if (Character.isUnicodeIdentifierStart(ch)) { - putChar(ch) - nextChar() - getIdentRest() - } else if (isSpecial(ch)) { - putChar(ch) - nextChar() - getOperatorRest() - } else { - syntaxError("illegal character '" + ("" + '\\' + 'u' + "%04x".format(ch: Int)) + "'") - nextChar() + def fetchOther = { + if (ch == '\u21D2') { + nextChar(); token = ARROW + } else if (ch == '\u2190') { + nextChar(); token = LARROW + } else if (Character.isUnicodeIdentifierStart(ch)) { + putChar(ch) + nextChar() + getIdentRest() + } else if (isSpecial(ch)) { + putChar(ch) + nextChar() + getOperatorRest() + } else { + syntaxError("illegal character '" + ("" + '\\' + 'u' + "%04x".format(ch: Int)) + "'") + nextChar() + } } + fetchOther } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala index 5afec611e9..146329183c 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala @@ -162,7 +162,7 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) { /** could optimize if args.length == 0, args.length == 1 AND args(0) is <: Node. */ def makeXMLseq(pos: Position, args: Seq[Tree]) = { - val buffer = ValDef(NoMods, _buf, TypeTree(), New(_scala_xml_NodeBuffer, List(Nil))) + val buffer = ValDef(NoMods, _buf, TypeTree(), New(_scala_xml_NodeBuffer, ListOfNil)) val applies = args filterNot isEmptyText map (t => Apply(Select(Ident(_buf), _plus), List(t))) atPos(pos)( Block(buffer :: applies.toList, Ident(_buf)) ) diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 898045e410..edf747486a 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -217,12 +217,12 @@ abstract class TreeBuilder { atPos(cpos) { ClassDef( Modifiers(FINAL), x, Nil, - Template(parents, self, NoMods, List(Nil), argss, stats, cpos.focus)) + Template(parents, self, NoMods, ListOfNil, argss, stats, cpos.focus)) }), atPos(npos) { New( Ident(x) setPos npos.focus, - List(Nil)) + ListOfNil) } ) } diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index 3f0cef6703..486a43614b 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -20,6 +20,12 @@ trait BasicBlocks { import global.{ ifDebug, settings, log, nme } import nme.isExceptionResultName + /** Override Array creation for efficiency (to not go through reflection). */ + private implicit val instructionTag: scala.reflect.ClassTag[Instruction] = new scala.reflect.ClassTag[Instruction] { + def runtimeClass: java.lang.Class[Instruction] = classOf[Instruction] + final override def newArray(len: Int): Array[Instruction] = new Array[Instruction](len) + } + object NoBasicBlock extends BasicBlock(-1, null) /** This class represents a basic block. Each @@ -36,10 +42,14 @@ trait BasicBlocks { private final class SuccessorList() { private var successors: List[BasicBlock] = Nil + /** This method is very hot! Handle with care. */ private def updateConserve() { - var lb: ListBuffer[BasicBlock] = null - var matches = 0 - var remaining = successors + var lb: ListBuffer[BasicBlock] = null + var matches = 0 + var remaining = successors + val direct = directSuccessors + var scratchHandlers: List[ExceptionHandler] = method.exh + var scratchBlocks: List[BasicBlock] = direct def addBlock(bb: BasicBlock) { if (matches < 0) @@ -54,25 +64,27 @@ trait BasicBlocks { } } - // exceptionSuccessors - method.exh foreach { handler => - if (handler covers outer) - addBlock(handler.startBlock) + while (scratchBlocks ne Nil) { + addBlock(scratchBlocks.head) + scratchBlocks = scratchBlocks.tail } - // directSuccessors - val direct = directSuccessors - direct foreach addBlock - /** Return a list of successors for 'b' that come from exception handlers * covering b's (non-exceptional) successors. These exception handlers * might not cover 'b' itself. This situation corresponds to an * exception being thrown as the first thing of one of b's successors. */ - method.exh foreach { handler => - direct foreach { block => - if (handler covers block) + while (scratchHandlers ne Nil) { + val handler = scratchHandlers.head + if (handler covers outer) + addBlock(handler.startBlock) + + scratchBlocks = direct + while (scratchBlocks ne Nil) { + if (handler covers scratchBlocks.head) addBlock(handler.startBlock) + scratchBlocks = scratchBlocks.tail } + scratchHandlers = scratchHandlers.tail } // Blocks did not align: create a new list. if (matches < 0) @@ -95,7 +107,7 @@ trait BasicBlocks { } /** Flags of this basic block. */ - private var flags: Int = 0 + private[this] var flags: Int = 0 /** Does this block have the given flag? */ def hasFlag(flag: Int): Boolean = (flags & flag) != 0 diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index a480429026..431802d185 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -628,48 +628,54 @@ abstract class GenICode extends SubComponent { val resCtx: Context = tree match { case LabelDef(name, params, rhs) => - val ctx1 = ctx.newBlock - if (nme.isLoopHeaderLabel(name)) - ctx1.bb.loopHeader = true - - ctx1.labels.get(tree.symbol) match { - case Some(label) => - debuglog("Found existing label for " + tree.symbol.fullLocationString) - label.anchor(ctx1.bb) - label.patch(ctx.method.code) - - case None => - val pair = (tree.symbol -> (new Label(tree.symbol) anchor ctx1.bb setParams (params map (_.symbol)))) - debuglog("Adding label " + tree.symbol.fullLocationString + " in genLoad.") - ctx1.labels += pair - ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info), false))); - } - - ctx.bb.closeWith(JUMP(ctx1.bb), tree.pos) - genLoad(rhs, ctx1, expectedType /*toTypeKind(tree.symbol.info.resultType)*/) - - case ValDef(_, nme.THIS, _, _) => - debuglog("skipping trivial assign to _$this: " + tree) - ctx - - case ValDef(_, _, _, rhs) => - val sym = tree.symbol - val local = ctx.method.addLocal(new Local(sym, toTypeKind(sym.info), false)) + def genLoadLabelDef = { + val ctx1 = ctx.newBlock + if (nme.isLoopHeaderLabel(name)) + ctx1.bb.loopHeader = true + + ctx1.labels.get(tree.symbol) match { + case Some(label) => + debuglog("Found existing label for " + tree.symbol.fullLocationString) + label.anchor(ctx1.bb) + label.patch(ctx.method.code) + + case None => + val pair = (tree.symbol -> (new Label(tree.symbol) anchor ctx1.bb setParams (params map (_.symbol)))) + debuglog("Adding label " + tree.symbol.fullLocationString + " in genLoad.") + ctx1.labels += pair + ctx.method.addLocals(params map (p => new Local(p.symbol, toTypeKind(p.symbol.info), false))); + } - if (rhs == EmptyTree) { - debuglog("Uninitialized variable " + tree + " at: " + (tree.pos)); - ctx.bb.emit(getZeroOf(local.kind)) + ctx.bb.closeWith(JUMP(ctx1.bb), tree.pos) + genLoad(rhs, ctx1, expectedType /*toTypeKind(tree.symbol.info.resultType)*/) } - - var ctx1 = ctx - if (rhs != EmptyTree) - ctx1 = genLoad(rhs, ctx, local.kind); - - ctx1.bb.emit(STORE_LOCAL(local), tree.pos) - ctx1.scope.add(local) - ctx1.bb.emit(SCOPE_ENTER(local)) - generatedType = UNIT - ctx1 + genLoadLabelDef + + case ValDef(_, name, _, rhs) => + def genLoadValDef = + if (name == nme.THIS) { + debuglog("skipping trivial assign to _$this: " + tree) + ctx + } else { + val sym = tree.symbol + val local = ctx.method.addLocal(new Local(sym, toTypeKind(sym.info), false)) + + if (rhs == EmptyTree) { + debuglog("Uninitialized variable " + tree + " at: " + (tree.pos)); + ctx.bb.emit(getZeroOf(local.kind)) + } + + var ctx1 = ctx + if (rhs != EmptyTree) + ctx1 = genLoad(rhs, ctx, local.kind); + + ctx1.bb.emit(STORE_LOCAL(local), tree.pos) + ctx1.scope.add(local) + ctx1.bb.emit(SCOPE_ENTER(local)) + generatedType = UNIT + ctx1 + } + genLoadValDef case t @ If(cond, thenp, elsep) => val (newCtx, resKind) = genLoadIf(t, ctx, expectedType) @@ -677,51 +683,55 @@ abstract class GenICode extends SubComponent { newCtx case Return(expr) => - val returnedKind = toTypeKind(expr.tpe) - debuglog("Return(" + expr + ") with returnedKind = " + returnedKind) - - var ctx1 = genLoad(expr, ctx, returnedKind) - lazy val tmp = ctx1.makeLocal(tree.pos, expr.tpe, "tmp") - val saved = savingCleanups(ctx1) { - var savedFinalizer = false - ctx1.cleanups foreach { - case MonitorRelease(m) => - debuglog("removing " + m + " from cleanups: " + ctx1.cleanups) - ctx1.bb.emit(Seq(LOAD_LOCAL(m), MONITOR_EXIT())) - ctx1.exitSynchronized(m) - - case Finalizer(f, finalizerCtx) => - debuglog("removing " + f + " from cleanups: " + ctx1.cleanups) - if (returnedKind != UNIT && mayCleanStack(f)) { - log("Emitting STORE_LOCAL for " + tmp + " to save finalizer.") - ctx1.bb.emit(STORE_LOCAL(tmp)) - savedFinalizer = true - } + def genLoadReturn = { + val returnedKind = toTypeKind(expr.tpe) + debuglog("Return(" + expr + ") with returnedKind = " + returnedKind) + + var ctx1 = genLoad(expr, ctx, returnedKind) + lazy val tmp = ctx1.makeLocal(tree.pos, expr.tpe, "tmp") + val saved = savingCleanups(ctx1) { + var savedFinalizer = false + ctx1.cleanups foreach { + case MonitorRelease(m) => + debuglog("removing " + m + " from cleanups: " + ctx1.cleanups) + ctx1.bb.emit(Seq(LOAD_LOCAL(m), MONITOR_EXIT())) + ctx1.exitSynchronized(m) + + case Finalizer(f, finalizerCtx) => + debuglog("removing " + f + " from cleanups: " + ctx1.cleanups) + if (returnedKind != UNIT && mayCleanStack(f)) { + log("Emitting STORE_LOCAL for " + tmp + " to save finalizer.") + ctx1.bb.emit(STORE_LOCAL(tmp)) + savedFinalizer = true + } - // duplicate finalizer (takes care of anchored labels) - val f1 = duplicateFinalizer(Set.empty ++ ctx1.labels.keySet, ctx1, f) + // duplicate finalizer (takes care of anchored labels) + val f1 = duplicateFinalizer(Set.empty ++ ctx1.labels.keySet, ctx1, f) - // we have to run this without the same finalizer in - // the list, otherwise infinite recursion happens for - // finalizers that contain 'return' - val fctx = finalizerCtx.newBlock - ctx1.bb.closeWith(JUMP(fctx.bb)) - ctx1 = genLoad(f1, fctx, UNIT) + // we have to run this without the same finalizer in + // the list, otherwise infinite recursion happens for + // finalizers that contain 'return' + val fctx = finalizerCtx.newBlock + ctx1.bb.closeWith(JUMP(fctx.bb)) + ctx1 = genLoad(f1, fctx, UNIT) + } + savedFinalizer } - savedFinalizer - } - if (saved) { - log("Emitting LOAD_LOCAL for " + tmp + " after saving finalizer.") - ctx1.bb.emit(LOAD_LOCAL(tmp)) - } - adapt(returnedKind, ctx1.method.returnType, ctx1, tree.pos) - ctx1.bb.emit(RETURN(ctx.method.returnType), tree.pos) - ctx1.bb.enterIgnoreMode - generatedType = expectedType - ctx1 + if (saved) { + log("Emitting LOAD_LOCAL for " + tmp + " after saving finalizer.") + ctx1.bb.emit(LOAD_LOCAL(tmp)) + } + adapt(returnedKind, ctx1.method.returnType, ctx1, tree.pos) + ctx1.bb.emit(RETURN(ctx.method.returnType), tree.pos) + ctx1.bb.enterIgnoreMode + generatedType = expectedType + ctx1 + } + genLoadReturn - case t @ Try(_, _, _) => genLoadTry(t, ctx, generatedType = _) + case t @ Try(_, _, _) => + genLoadTry(t, ctx, generatedType = _) case Throw(expr) => val (ctx1, expectedType) = genThrow(expr, ctx) @@ -733,41 +743,42 @@ abstract class GenICode extends SubComponent { " Call was genLoad" + ((tree, ctx, expectedType))) case Apply(TypeApply(fun, targs), _) => - val sym = fun.symbol - val cast = sym match { - case Object_isInstanceOf => false - case Object_asInstanceOf => true - case _ => abort("Unexpected type application " + fun + "[sym: " + sym.fullName + "]" + " in: " + tree) - } - - val Select(obj, _) = fun - val l = toTypeKind(obj.tpe) - val r = toTypeKind(targs.head.tpe) - val ctx1 = genLoadQualifier(fun, ctx) - - if (l.isValueType && r.isValueType) - genConversion(l, r, ctx1, cast) - else if (l.isValueType) { - ctx1.bb.emit(DROP(l), fun.pos) - if (cast) { - ctx1.bb.emit(Seq( - NEW(REFERENCE(definitions.ClassCastExceptionClass)), - DUP(ObjectReference), - THROW(definitions.ClassCastExceptionClass) - )) - } else - ctx1.bb.emit(CONSTANT(Constant(false))) - } - else if (r.isValueType && cast) { - assert(false, tree) /* Erasure should have added an unboxing operation to prevent that. */ + def genLoadApply1 = { + val sym = fun.symbol + val cast = sym match { + case Object_isInstanceOf => false + case Object_asInstanceOf => true + case _ => abort("Unexpected type application " + fun + "[sym: " + sym.fullName + "]" + " in: " + tree) + } + + val Select(obj, _) = fun + val l = toTypeKind(obj.tpe) + val r = toTypeKind(targs.head.tpe) + val ctx1 = genLoadQualifier(fun, ctx) + + if (l.isValueType && r.isValueType) + genConversion(l, r, ctx1, cast) + else if (l.isValueType) { + ctx1.bb.emit(DROP(l), fun.pos) + if (cast) { + ctx1.bb.emit(Seq( + NEW(REFERENCE(definitions.ClassCastExceptionClass)), + DUP(ObjectReference), + THROW(definitions.ClassCastExceptionClass) + )) + } else + ctx1.bb.emit(CONSTANT(Constant(false))) + } else if (r.isValueType && cast) { + assert(false, tree) /* Erasure should have added an unboxing operation to prevent that. */ + } else if (r.isValueType) { + ctx.bb.emit(IS_INSTANCE(REFERENCE(definitions.boxedClass(r.toType.typeSymbol)))) + } else { + genCast(l, r, ctx1, cast) + } + generatedType = if (cast) r else BOOL; + ctx1 } - else if (r.isValueType) - ctx.bb.emit(IS_INSTANCE(REFERENCE(definitions.boxedClass(r.toType.typeSymbol)))) - else - genCast(l, r, ctx1, cast); - - generatedType = if (cast) r else BOOL; - ctx1 + genLoadApply1 // 'super' call: Note: since constructors are supposed to // return an instance of what they construct, we have to take @@ -776,93 +787,102 @@ abstract class GenICode extends SubComponent { // therefore, we can ignore this fact, and generate code that leaves nothing // on the stack (contrary to what the type in the AST says). case Apply(fun @ Select(Super(_, mix), _), args) => - debuglog("Call to super: " + tree); - val invokeStyle = SuperCall(mix) -// if (fun.symbol.isConstructor) Static(true) else SuperCall(mix); + def genLoadApply2 = { + debuglog("Call to super: " + tree); + val invokeStyle = SuperCall(mix) + // if (fun.symbol.isConstructor) Static(true) else SuperCall(mix); - ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos) - val ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx) - - ctx1.bb.emit(CALL_METHOD(fun.symbol, invokeStyle), tree.pos) - generatedType = - if (fun.symbol.isConstructor) UNIT - else toTypeKind(fun.symbol.info.resultType) - ctx1 + ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos) + val ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx) + + ctx1.bb.emit(CALL_METHOD(fun.symbol, invokeStyle), tree.pos) + generatedType = + if (fun.symbol.isConstructor) UNIT + else toTypeKind(fun.symbol.info.resultType) + ctx1 + } + genLoadApply2 // 'new' constructor call: Note: since constructors are // thought to return an instance of what they construct, // we have to 'simulate' it by DUPlicating the freshly created // instance (on JVM, <init> methods return VOID). case Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) => - val ctor = fun.symbol - debugassert(ctor.isClassConstructor, - "'new' call to non-constructor: " + ctor.name) - - generatedType = toTypeKind(tpt.tpe) - debugassert(generatedType.isReferenceType || generatedType.isArrayType, - "Non reference type cannot be instantiated: " + generatedType) - - generatedType match { - case arr @ ARRAY(elem) => - val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) - val dims = arr.dimensions - var elemKind = arr.elementKind - if (args.length > dims) - unit.error(tree.pos, "too many arguments for array constructor: found " + args.length + - " but array has only " + dims + " dimension(s)") - if (args.length != dims) - for (i <- args.length until dims) elemKind = ARRAY(elemKind) - ctx1.bb.emit(CREATE_ARRAY(elemKind, args.length), tree.pos) - ctx1 + def genLoadApply3 = { + val ctor = fun.symbol + debugassert(ctor.isClassConstructor, + "'new' call to non-constructor: " + ctor.name) + + generatedType = toTypeKind(tpt.tpe) + debugassert(generatedType.isReferenceType || generatedType.isArrayType, + "Non reference type cannot be instantiated: " + generatedType) - case rt @ REFERENCE(cls) => - debugassert(ctor.owner == cls, - "Symbol " + ctor.owner.fullName + " is different than " + tpt) - - val ctx2 = if (forMSIL && loaders.clrTypes.isNonEnumValuetype(cls)) { - /* parameterful constructors are the only possible custom constructors, - a default constructor can't be defined for valuetypes, CLR dixit */ - val isDefaultConstructor = args.isEmpty - if (isDefaultConstructor) { - msil_genLoadZeroOfNonEnumValuetype(ctx, rt, tree.pos, leaveAddressOnStackInstead = false) - ctx + generatedType match { + case arr @ ARRAY(elem) => + val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) + val dims = arr.dimensions + var elemKind = arr.elementKind + if (args.length > dims) + unit.error(tree.pos, "too many arguments for array constructor: found " + args.length + + " but array has only " + dims + " dimension(s)") + if (args.length != dims) + for (i <- args.length until dims) elemKind = ARRAY(elemKind) + ctx1.bb.emit(CREATE_ARRAY(elemKind, args.length), tree.pos) + ctx1 + + case rt @ REFERENCE(cls) => + debugassert(ctor.owner == cls, + "Symbol " + ctor.owner.fullName + " is different than " + tpt) + + val ctx2 = if (forMSIL && loaders.clrTypes.isNonEnumValuetype(cls)) { + /* parameterful constructors are the only possible custom constructors, + a default constructor can't be defined for valuetypes, CLR dixit */ + val isDefaultConstructor = args.isEmpty + if (isDefaultConstructor) { + msil_genLoadZeroOfNonEnumValuetype(ctx, rt, tree.pos, leaveAddressOnStackInstead = false) + ctx + } else { + val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) + ctx1.bb.emit(CIL_NEWOBJ(ctor), tree.pos) + ctx1 + } } else { + val nw = NEW(rt) + ctx.bb.emit(nw, tree.pos) + ctx.bb.emit(DUP(generatedType)) val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) - ctx1.bb.emit(CIL_NEWOBJ(ctor), tree.pos) + + val init = CALL_METHOD(ctor, Static(true)) + nw.init = init + ctx1.bb.emit(init, tree.pos) ctx1 } - } else { - val nw = NEW(rt) - ctx.bb.emit(nw, tree.pos) - ctx.bb.emit(DUP(generatedType)) - val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) - - val init = CALL_METHOD(ctor, Static(true)) - nw.init = init - ctx1.bb.emit(init, tree.pos) - ctx1 - } - ctx2 - - case _ => - abort("Cannot instantiate " + tpt + " of kind: " + generatedType) + ctx2 + + case _ => + abort("Cannot instantiate " + tpt + " of kind: " + generatedType) + } } + genLoadApply3 case Apply(fun @ _, List(expr)) if (definitions.isBox(fun.symbol)) => - debuglog("BOX : " + fun.symbol.fullName); - val ctx1 = genLoad(expr, ctx, toTypeKind(expr.tpe)) - val nativeKind = toTypeKind(expr.tpe) - if (settings.Xdce.value) { - // we store this boxed value to a local, even if not really needed. - // boxing optimization might use it, and dead code elimination will - // take care of unnecessary stores - var loc1 = ctx.makeLocal(tree.pos, expr.tpe, "boxed") - ctx1.bb.emit(STORE_LOCAL(loc1)) - ctx1.bb.emit(LOAD_LOCAL(loc1)) + def genLoadApply4 = { + debuglog("BOX : " + fun.symbol.fullName); + val ctx1 = genLoad(expr, ctx, toTypeKind(expr.tpe)) + val nativeKind = toTypeKind(expr.tpe) + if (settings.Xdce.value) { + // we store this boxed value to a local, even if not really needed. + // boxing optimization might use it, and dead code elimination will + // take care of unnecessary stores + var loc1 = ctx.makeLocal(tree.pos, expr.tpe, "boxed") + ctx1.bb.emit(STORE_LOCAL(loc1)) + ctx1.bb.emit(LOAD_LOCAL(loc1)) + } + ctx1.bb.emit(BOX(nativeKind), expr.pos) + generatedType = toTypeKind(fun.symbol.tpe.resultType) + ctx1 } - ctx1.bb.emit(BOX(nativeKind), expr.pos) - generatedType = toTypeKind(fun.symbol.tpe.resultType) - ctx1 + genLoadApply4 case Apply(fun @ _, List(expr)) if (definitions.isUnbox(fun.symbol)) => debuglog("UNBOX : " + fun.symbol.fullName) @@ -880,14 +900,30 @@ abstract class GenICode extends SubComponent { case app @ Apply(fun @ Select(qual, _), args) if !ctx.method.symbol.isStaticConstructor - && fun.symbol.isAccessor && fun.symbol.accessed.hasStaticAnnotation => + && fun.symbol.isAccessor && fun.symbol.accessed.hasStaticAnnotation + && qual.tpe.typeSymbol.orElse(fun.symbol.owner).companionClass != NoSymbol => // bypass the accessor to the companion object and load the static field directly - // the only place were this bypass is not done, is the static intializer for the static field itself + // this bypass is not done: + // - if the static intializer for the static field itself + // - if there is no companion class of the object owner - this happens in the REPL + def genLoadApply5 = { val sym = fun.symbol generatedType = toTypeKind(sym.accessed.info) - val hostClass = qual.tpe.typeSymbol.orElse(sym.owner).companionClass - val staticfield = hostClass.info.findMember(sym.accessed.name, NoFlags, NoFlags, false) - + val hostOwner = qual.tpe.typeSymbol.orElse(sym.owner) + val hostClass = hostOwner.companionClass + val staticfield = hostClass.info.findMember(sym.accessed.name, NoFlags, NoFlags, false) orElse { + if (!currentRun.compiles(hostOwner)) { + // hostOwner was separately compiled -- the static field symbol needs to be recreated in hostClass + import Flags._ + debuglog("recreating sym.accessed.name: " + sym.accessed.name) + val objectfield = hostOwner.info.findMember(sym.accessed.name, NoFlags, NoFlags, false) + val staticfield = hostClass.newVariable(newTermName(sym.accessed.name.toString), tree.pos, STATIC | SYNTHETIC | FINAL) setInfo objectfield.tpe + staticfield.addAnnotation(definitions.StaticClass) + hostClass.info.decls enter staticfield + staticfield + } else NoSymbol + } + if (sym.isGetter) { ctx.bb.emit(LOAD_FIELD(staticfield, true) setHostClass hostClass, tree.pos) ctx @@ -900,84 +936,89 @@ abstract class GenICode extends SubComponent { assert(false, "supposedly unreachable") ctx } + } + genLoadApply5 case app @ Apply(fun, args) => - val sym = fun.symbol + def genLoadApply6 = { + val sym = fun.symbol - if (sym.isLabel) { // jump to a label - val label = ctx.labels.getOrElse(sym, { - // it is a forward jump, scan for labels - resolveForwardLabel(ctx.defdef, ctx, sym) - ctx.labels.get(sym) match { - case Some(l) => - log("Forward jump for " + sym.fullLocationString + ": scan found label " + l) - l - case _ => - abort("Unknown label target: " + sym + " at: " + (fun.pos) + ": ctx: " + ctx) - } - }) - // note: when one of the args to genLoadLabelArguments is a jump to a label, - // it will call back into genLoad and arrive at this case, which will then set ctx1.bb.ignore to true, - // this is okay, since we're jumping unconditionally, so the loads and jumps emitted by the outer - // call to genLoad (by calling genLoadLabelArguments and emitOnly) can safely be ignored, - // however, as emitOnly will close the block, which reverses its instructions (when it's still open), - // we better not reverse when the block has already been closed but is in ignore mode - // (if it's not in ignore mode, double-closing is an error) - val ctx1 = genLoadLabelArguments(args, label, ctx) - ctx1.bb.emitOnly(if (label.anchored) JUMP(label.block) else PJUMP(label)) - ctx1.bb.enterIgnoreMode - ctx1 - } else if (isPrimitive(sym)) { // primitive method call - val (newCtx, resKind) = genPrimitiveOp(app, ctx, expectedType) - generatedType = resKind - newCtx - } else { // normal method call - debuglog("Gen CALL_METHOD with sym: " + sym + " isStaticSymbol: " + sym.isStaticMember); - val invokeStyle = - if (sym.isStaticMember) - Static(false) - else if (sym.isPrivate || sym.isClassConstructor) - Static(true) - else - Dynamic - - var ctx1 = - if (invokeStyle.hasInstance) { - if (forMSIL && !(invokeStyle.isInstanceOf[SuperCall]) && msil_IsValuetypeInstMethod(sym)) - msil_genLoadQualifierAddress(fun, ctx) + if (sym.isLabel) { // jump to a label + val label = ctx.labels.getOrElse(sym, { + // it is a forward jump, scan for labels + resolveForwardLabel(ctx.defdef, ctx, sym) + ctx.labels.get(sym) match { + case Some(l) => + log("Forward jump for " + sym.fullLocationString + ": scan found label " + l) + l + case _ => + abort("Unknown label target: " + sym + " at: " + (fun.pos) + ": ctx: " + ctx) + } + }) + // note: when one of the args to genLoadLabelArguments is a jump to a label, + // it will call back into genLoad and arrive at this case, which will then set ctx1.bb.ignore to true, + // this is okay, since we're jumping unconditionally, so the loads and jumps emitted by the outer + // call to genLoad (by calling genLoadLabelArguments and emitOnly) can safely be ignored, + // however, as emitOnly will close the block, which reverses its instructions (when it's still open), + // we better not reverse when the block has already been closed but is in ignore mode + // (if it's not in ignore mode, double-closing is an error) + val ctx1 = genLoadLabelArguments(args, label, ctx) + ctx1.bb.emitOnly(if (label.anchored) JUMP(label.block) else PJUMP(label)) + ctx1.bb.enterIgnoreMode + ctx1 + } else if (isPrimitive(sym)) { // primitive method call + val (newCtx, resKind) = genPrimitiveOp(app, ctx, expectedType) + generatedType = resKind + newCtx + } else { // normal method call + debuglog("Gen CALL_METHOD with sym: " + sym + " isStaticSymbol: " + sym.isStaticMember); + val invokeStyle = + if (sym.isStaticMember) + Static(false) + else if (sym.isPrivate || sym.isClassConstructor) + Static(true) else - genLoadQualifier(fun, ctx) - } else ctx - - ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx1) - val cm = CALL_METHOD(sym, invokeStyle) - - /** In a couple cases, squirrel away a little extra information in the - * CALL_METHOD for use by GenJVM. - */ - fun match { - case Select(qual, _) => - val qualSym = findHostClass(qual.tpe, sym) - - if (qualSym == ArrayClass) cm setTargetTypeKind toTypeKind(qual.tpe) - else cm setHostClass qualSym - - log( - if (qualSym == ArrayClass) "Stored target type kind " + toTypeKind(qual.tpe) + " for " + sym.fullName - else s"Set more precise host class for ${sym.fullName} hostClass: $qualSym" - ) - case _ => - } - ctx1.bb.emit(cm, tree.pos) + Dynamic + + var ctx1 = + if (invokeStyle.hasInstance) { + if (forMSIL && !(invokeStyle.isInstanceOf[SuperCall]) && msil_IsValuetypeInstMethod(sym)) + msil_genLoadQualifierAddress(fun, ctx) + else + genLoadQualifier(fun, ctx) + } else ctx + + ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx1) + val cm = CALL_METHOD(sym, invokeStyle) + + /** In a couple cases, squirrel away a little extra information in the + * CALL_METHOD for use by GenJVM. + */ + fun match { + case Select(qual, _) => + val qualSym = findHostClass(qual.tpe, sym) + + if (qualSym == ArrayClass) cm setTargetTypeKind toTypeKind(qual.tpe) + else cm setHostClass qualSym + + log( + if (qualSym == ArrayClass) "Stored target type kind " + toTypeKind(qual.tpe) + " for " + sym.fullName + else s"Set more precise host class for ${sym.fullName} hostClass: $qualSym" + ) + case _ => + } + ctx1.bb.emit(cm, tree.pos) - if (sym == ctx1.method.symbol) { - ctx1.method.recursive = true + if (sym == ctx1.method.symbol) { + ctx1.method.recursive = true + } + generatedType = + if (sym.isClassConstructor) UNIT + else toTypeKind(sym.info.resultType); + ctx1 } - generatedType = - if (sym.isClassConstructor) UNIT - else toTypeKind(sym.info.resultType); - ctx1 } + genLoadApply6 case ApplyDynamic(qual, args) => assert(!forMSIL, tree) @@ -989,20 +1030,22 @@ abstract class GenICode extends SubComponent { // ctx1 case This(qual) => - assert(tree.symbol == ctx.clazz.symbol || tree.symbol.isModuleClass, - "Trying to access the this of another class: " + - "tree.symbol = " + tree.symbol + ", ctx.clazz.symbol = " + ctx.clazz.symbol + " compilation unit:"+unit) - if (tree.symbol.isModuleClass && tree.symbol != ctx.clazz.symbol) { - genLoadModule(ctx, tree) - generatedType = REFERENCE(tree.symbol) - } - else { - ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos) - generatedType = REFERENCE( - if (tree.symbol == ArrayClass) ObjectClass else ctx.clazz.symbol - ) + def genLoadThis = { + assert(tree.symbol == ctx.clazz.symbol || tree.symbol.isModuleClass, + "Trying to access the this of another class: " + + "tree.symbol = " + tree.symbol + ", ctx.clazz.symbol = " + ctx.clazz.symbol + " compilation unit:"+unit) + if (tree.symbol.isModuleClass && tree.symbol != ctx.clazz.symbol) { + genLoadModule(ctx, tree) + generatedType = REFERENCE(tree.symbol) + } else { + ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos) + generatedType = REFERENCE( + if (tree.symbol == ArrayClass) ObjectClass else ctx.clazz.symbol + ) + } + ctx } - ctx + genLoadThis case Select(Ident(nme.EMPTY_PACKAGE_NAME), module) => debugassert(tree.symbol.isModule, @@ -1012,60 +1055,67 @@ abstract class GenICode extends SubComponent { genLoadModule(ctx, tree) case Select(qualifier, selector) => - val sym = tree.symbol - generatedType = toTypeKind(sym.info) - val hostClass = findHostClass(qualifier.tpe, sym) - log(s"Host class of $sym with qual $qualifier (${qualifier.tpe}) is $hostClass") - - if (sym.isModule) { - genLoadModule(ctx, tree) - } - else if (sym.isStaticMember) { - ctx.bb.emit(LOAD_FIELD(sym, true) setHostClass hostClass, tree.pos) - ctx - } - else { - val ctx1 = genLoadQualifier(tree, ctx) - ctx1.bb.emit(LOAD_FIELD(sym, false) setHostClass hostClass, tree.pos) - ctx1 - } + def genLoadSelect = { + val sym = tree.symbol + generatedType = toTypeKind(sym.info) + val hostClass = findHostClass(qualifier.tpe, sym) + log(s"Host class of $sym with qual $qualifier (${qualifier.tpe}) is $hostClass") - case Ident(name) => - val sym = tree.symbol - if (!sym.isPackage) { if (sym.isModule) { genLoadModule(ctx, tree) - generatedType = toTypeKind(sym.info) } - else { - try { - val Some(l) = ctx.method.lookupLocal(sym) - ctx.bb.emit(LOAD_LOCAL(l), tree.pos) - generatedType = l.kind - } catch { - case ex: MatchError => - abort("symbol " + sym + " does not exist in " + ctx.method) + else if (sym.isStaticMember) { + ctx.bb.emit(LOAD_FIELD(sym, true) setHostClass hostClass, tree.pos) + ctx + } else { + val ctx1 = genLoadQualifier(tree, ctx) + ctx1.bb.emit(LOAD_FIELD(sym, false) setHostClass hostClass, tree.pos) + ctx1 + } + } + genLoadSelect + + case Ident(name) => + def genLoadIdent = { + val sym = tree.symbol + if (!sym.isPackage) { + if (sym.isModule) { + genLoadModule(ctx, tree) + generatedType = toTypeKind(sym.info) + } else { + try { + val Some(l) = ctx.method.lookupLocal(sym) + ctx.bb.emit(LOAD_LOCAL(l), tree.pos) + generatedType = l.kind + } catch { + case ex: MatchError => + abort("symbol " + sym + " does not exist in " + ctx.method) + } } } + ctx } - ctx + genLoadIdent case Literal(value) => - if (value.tag != UnitTag) (value.tag, expectedType) match { - case (IntTag, LONG) => - ctx.bb.emit(CONSTANT(Constant(value.longValue)), tree.pos); - generatedType = LONG - case (FloatTag, DOUBLE) => - ctx.bb.emit(CONSTANT(Constant(value.doubleValue)), tree.pos); - generatedType = DOUBLE - case (NullTag, _) => - ctx.bb.emit(CONSTANT(value), tree.pos); - generatedType = NullReference - case _ => - ctx.bb.emit(CONSTANT(value), tree.pos); - generatedType = toTypeKind(tree.tpe) + def genLoadLiteral = { + if (value.tag != UnitTag) (value.tag, expectedType) match { + case (IntTag, LONG) => + ctx.bb.emit(CONSTANT(Constant(value.longValue)), tree.pos); + generatedType = LONG + case (FloatTag, DOUBLE) => + ctx.bb.emit(CONSTANT(Constant(value.doubleValue)), tree.pos); + generatedType = DOUBLE + case (NullTag, _) => + ctx.bb.emit(CONSTANT(value), tree.pos); + generatedType = NullReference + case _ => + ctx.bb.emit(CONSTANT(value), tree.pos); + generatedType = toTypeKind(tree.tpe) + } + ctx } - ctx + genLoadLiteral case Block(stats, expr) => ctx.enterScope @@ -1085,66 +1135,72 @@ abstract class GenICode extends SubComponent { genStat(tree, ctx) case ArrayValue(tpt @ TypeTree(), _elems) => - var ctx1 = ctx - val elmKind = toTypeKind(tpt.tpe) - generatedType = ARRAY(elmKind) - val elems = _elems.toIndexedSeq - - ctx1.bb.emit(CONSTANT(new Constant(elems.length)), tree.pos) - ctx1.bb.emit(CREATE_ARRAY(elmKind, 1)) - // inline array literals - var i = 0 - while (i < elems.length) { - ctx1.bb.emit(DUP(generatedType), tree.pos) - ctx1.bb.emit(CONSTANT(new Constant(i))) - ctx1 = genLoad(elems(i), ctx1, elmKind) - ctx1.bb.emit(STORE_ARRAY_ITEM(elmKind)) - i = i + 1 + def genLoadArrayValue = { + var ctx1 = ctx + val elmKind = toTypeKind(tpt.tpe) + generatedType = ARRAY(elmKind) + val elems = _elems.toIndexedSeq + + ctx1.bb.emit(CONSTANT(new Constant(elems.length)), tree.pos) + ctx1.bb.emit(CREATE_ARRAY(elmKind, 1)) + // inline array literals + var i = 0 + while (i < elems.length) { + ctx1.bb.emit(DUP(generatedType), tree.pos) + ctx1.bb.emit(CONSTANT(new Constant(i))) + ctx1 = genLoad(elems(i), ctx1, elmKind) + ctx1.bb.emit(STORE_ARRAY_ITEM(elmKind)) + i = i + 1 + } + ctx1 } - ctx1 + genLoadArrayValue case Match(selector, cases) => - debuglog("Generating SWITCH statement."); - var ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue) - val afterCtx = ctx1.newBlock - var caseCtx: Context = null - generatedType = toTypeKind(tree.tpe) - - var targets: List[BasicBlock] = Nil - var tags: List[Int] = Nil - var default: BasicBlock = afterCtx.bb - - for (caze @ CaseDef(pat, guard, body) <- cases) { - assert(guard == EmptyTree, guard) - val tmpCtx = ctx1.newBlock - pat match { - case Literal(value) => - tags = value.intValue :: tags - targets = tmpCtx.bb :: targets - case Ident(nme.WILDCARD) => - default = tmpCtx.bb - case Alternative(alts) => - alts foreach { - case Literal(value) => - tags = value.intValue :: tags - targets = tmpCtx.bb :: targets - case _ => - abort("Invalid case in alternative in switch-like pattern match: " + - tree + " at: " + tree.pos) - } - case _ => - abort("Invalid case statement in switch-like pattern match: " + - tree + " at: " + (tree.pos)) + def genLoadMatch = { + debuglog("Generating SWITCH statement."); + var ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue) + val afterCtx = ctx1.newBlock + var caseCtx: Context = null + generatedType = toTypeKind(tree.tpe) + + var targets: List[BasicBlock] = Nil + var tags: List[Int] = Nil + var default: BasicBlock = afterCtx.bb + + for (caze @ CaseDef(pat, guard, body) <- cases) { + assert(guard == EmptyTree, guard) + val tmpCtx = ctx1.newBlock + pat match { + case Literal(value) => + tags = value.intValue :: tags + targets = tmpCtx.bb :: targets + case Ident(nme.WILDCARD) => + default = tmpCtx.bb + case Alternative(alts) => + alts foreach { + case Literal(value) => + tags = value.intValue :: tags + targets = tmpCtx.bb :: targets + case _ => + abort("Invalid case in alternative in switch-like pattern match: " + + tree + " at: " + tree.pos) + } + case _ => + abort("Invalid case statement in switch-like pattern match: " + + tree + " at: " + (tree.pos)) + } + + caseCtx = genLoad(body, tmpCtx, generatedType) + // close the block unless it's already been closed by the body, which closes the block if it ends in a jump (which is emitted to have alternatives share their body) + caseCtx.bb.closeWith(JUMP(afterCtx.bb) setPos caze.pos) } - - caseCtx = genLoad(body, tmpCtx, generatedType) - // close the block unless it's already been closed by the body, which closes the block if it ends in a jump (which is emitted to have alternatives share their body) - caseCtx.bb.closeWith(JUMP(afterCtx.bb) setPos caze.pos) + ctx1.bb.emitOnly( + SWITCH(tags.reverse map (x => List(x)), (default :: targets).reverse) setPos tree.pos + ) + afterCtx } - ctx1.bb.emitOnly( - SWITCH(tags.reverse map (x => List(x)), (default :: targets).reverse) setPos tree.pos - ) - afterCtx + genLoadMatch case EmptyTree => if (expectedType != UNIT) diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index 81fd285cdb..d43013c644 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -2,11 +2,6 @@ * Copyright 2005-2012 LAMP/EPFL * @author Martin Odersky */ -/* NSC -- new scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Martin Odersky - */ - package scala.tools.nsc package backend diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index 0f7080ef3c..1fcb406e96 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -82,6 +82,8 @@ trait Opcodes { self: ICodes => final val jumpsCat = 10 final val retCat = 11 + private lazy val ObjectReferenceList = ObjectReference :: Nil + /** This class represents an instruction of the intermediate code. * Each case subclass will represent a specific operation. */ @@ -392,14 +394,13 @@ trait Opcodes { self: ICodes => else args } - override def produced = - if (producedType == UNIT || method.isConstructor) 0 - else 1 - - private def producedType: TypeKind = toTypeKind(method.info.resultType) - override def producedTypes = - if (produced == 0) Nil - else producedType :: Nil + private val producedList = toTypeKind(method.info.resultType) match { + case UNIT => Nil + case _ if method.isConstructor => Nil + case kind => kind :: Nil + } + override def produced = producedList.size + override def producedTypes = producedList /** object identity is equality for CALL_METHODs. Needed for * being able to store such instructions into maps, when more @@ -423,7 +424,7 @@ trait Opcodes { self: ICodes => assert(boxType.isValueType && !boxType.isInstanceOf[BOXED] && (boxType ne UNIT)) // documentation override def toString(): String = "UNBOX " + boxType override def consumed = 1 - override def consumedTypes = ObjectReference :: Nil + override def consumedTypes = ObjectReferenceList override def produced = 1 override def producedTypes = boxType :: Nil override def category = objsCat @@ -474,7 +475,7 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def produced = 1 - override def consumedTypes = ObjectReference :: Nil + override def consumedTypes = ObjectReferenceList override def producedTypes = BOOL :: Nil override def category = castsCat @@ -490,7 +491,7 @@ trait Opcodes { self: ICodes => override def consumed = 1 override def produced = 1 - override def consumedTypes = ObjectReference :: Nil + override def consumedTypes = ObjectReferenceList override def producedTypes = typ :: Nil override def category = castsCat @@ -576,7 +577,6 @@ trait Opcodes { self: ICodes => override def produced = 0 override def consumedTypes = kind :: Nil - override def category = jumpsCat } @@ -799,7 +799,7 @@ trait Opcodes { self: ICodes => case class CIL_UNBOX(valueType: TypeKind) extends Instruction { override def toString(): String = "CIL_UNBOX " + valueType override def consumed = 1 - override def consumedTypes = ObjectReference :: Nil // actually consumes a 'boxed valueType' + override def consumedTypes = ObjectReferenceList // actually consumes a 'boxed valueType' override def produced = 1 override def producedTypes = msil_mgdptr(valueType) :: Nil override def category = objsCat @@ -808,7 +808,7 @@ trait Opcodes { self: ICodes => case class CIL_INITOBJ(valueType: TypeKind) extends Instruction { override def toString(): String = "CIL_INITOBJ " + valueType override def consumed = 1 - override def consumedTypes = ObjectReference :: Nil // actually consumes a managed pointer + override def consumedTypes = ObjectReferenceList // actually consumes a managed pointer override def produced = 0 override def category = objsCat } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 17b479e5e5..b57f5e86a3 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -277,7 +277,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // Nested objects won't receive ACC_FINAL in order to allow for their overriding. val finalFlag = ( - (sym.hasFlag(Flags.FINAL) || isTopLevelModule(sym)) + (((sym.rawflags & Flags.FINAL) != 0) || isTopLevelModule(sym)) && !sym.enclClass.isInterface && !sym.isClassConstructor && !sym.isMutable // lazy vals and vars both @@ -448,6 +448,17 @@ abstract class GenASM extends SubComponent with BytecodeWriters { val JAVA_LANG_OBJECT = asm.Type.getObjectType("java/lang/Object") val JAVA_LANG_STRING = asm.Type.getObjectType("java/lang/String") + /** + * We call many Java varargs methods from ASM library that expect Arra[asm.Type] as argument so + * we override default (compiler-generated) ClassTag so we can provide specialized newArray implementation. + * + * Examples of methods that should pick our definition are: JBuilder.javaType and JPlainBuilder.genMethod. + */ + private implicit val asmTypeTag: scala.reflect.ClassTag[asm.Type] = new scala.reflect.ClassTag[asm.Type] { + def runtimeClass: java.lang.Class[asm.Type] = classOf[asm.Type] + final override def newArray(len: Int): Array[asm.Type] = new Array[asm.Type](len) + } + /** basic functionality for class file building */ abstract class JBuilder(bytecodeWriter: BytecodeWriter) { @@ -641,7 +652,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { def javaType(s: Symbol): asm.Type = { if (s.isMethod) { val resT: asm.Type = if (s.isClassConstructor) asm.Type.VOID_TYPE else javaType(s.tpe.resultType); - asm.Type.getMethodType( resT, (s.tpe.paramTypes map javaType): _* ) + asm.Type.getMethodType( resT, (s.tpe.paramTypes map javaType): _*) } else { javaType(s.tpe) } } @@ -2217,15 +2228,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters { def getMerged(): collection.Map[Local, List[Interval]] = { // TODO should but isn't: unbalanced start(s) of scope(s) val shouldBeEmpty = pending filter { p => val Pair(k, st) = p; st.nonEmpty }; - - val merged = mutable.Map.empty[Local, List[Interval]] - - def addToMerged(lv: Local, start: Label, end: Label) { - val ranges = merged.getOrElseUpdate(lv, Nil) - val coalesced = fuse(ranges, Interval(start, end)) - merged.update(lv, coalesced) - } - + val merged = mutable.Map[Local, List[Interval]]() + def addToMerged(lv: Local, start: Label, end: Label) { + val intv = Interval(start, end) + merged(lv) = if (merged contains lv) fuse(merged(lv), intv) else intv :: Nil + } for(LocVarEntry(lv, start, end) <- seen) { addToMerged(lv, start, end) } /* for each var with unbalanced start(s) of scope(s): @@ -2375,8 +2382,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { def genBlock(b: BasicBlock) { jmethod.visitLabel(labels(b)) - import asm.Opcodes; - debuglog("Generating code for block: " + b) // val lastInstr = b.lastInstruction @@ -2395,287 +2400,308 @@ abstract class GenASM extends SubComponent with BytecodeWriters { } } - (instr.category: @scala.annotation.switch) match { - - case icodes.localsCat => (instr: @unchecked) match { - case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0) - case LOAD_LOCAL(local) => jcode.load(indexOf(local), local.kind) - case STORE_LOCAL(local) => jcode.store(indexOf(local), local.kind) - case STORE_THIS(_) => - // this only works for impl classes because the self parameter comes first - // in the method signature. If that changes, this code has to be revisited. - jmethod.visitVarInsn(Opcodes.ASTORE, 0) - - case SCOPE_ENTER(lv) => - // locals removed by closelim (via CopyPropagation) may have left behind SCOPE_ENTER, SCOPE_EXIT that are to be ignored - val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv)) - if(relevant) { // TODO check: does GenICode emit SCOPE_ENTER, SCOPE_EXIT for synthetic vars? - // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes) - // similarly, these labels aren't tracked in the `labels` map. - val start = new asm.Label - jmethod.visitLabel(start) - scoping.pushScope(lv, start) - } + genInstr(instr, b) - case SCOPE_EXIT(lv) => - val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv)) - if(relevant) { - // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes) - // similarly, these labels aren't tracked in the `labels` map. - val end = new asm.Label - jmethod.visitLabel(end) - scoping.popScope(lv, end, instr.pos) - } - } + } - case icodes.stackCat => (instr: @unchecked) match { + } - case LOAD_MODULE(module) => - // assert(module.isModule, "Expected module: " + module) - debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags)); - if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) { - jmethod.visitVarInsn(Opcodes.ALOAD, 0) - } else { - jmethod.visitFieldInsn( - Opcodes.GETSTATIC, - javaName(module) /* + "$" */ , - strMODULE_INSTANCE_FIELD, - descriptor(module) - ) - } + def genInstr(instr: Instruction, b: BasicBlock) { + import asm.Opcodes + (instr.category: @scala.annotation.switch) match { + + case icodes.localsCat => + def genLocalInstr = (instr: @unchecked) match { + case THIS(_) => jmethod.visitVarInsn(Opcodes.ALOAD, 0) + case LOAD_LOCAL(local) => jcode.load(indexOf(local), local.kind) + case STORE_LOCAL(local) => jcode.store(indexOf(local), local.kind) + case STORE_THIS(_) => + // this only works for impl classes because the self parameter comes first + // in the method signature. If that changes, this code has to be revisited. + jmethod.visitVarInsn(Opcodes.ASTORE, 0) + + case SCOPE_ENTER(lv) => + // locals removed by closelim (via CopyPropagation) may have left behind SCOPE_ENTER, SCOPE_EXIT that are to be ignored + val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv)) + if (relevant) { // TODO check: does GenICode emit SCOPE_ENTER, SCOPE_EXIT for synthetic vars? + // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes) + // similarly, these labels aren't tracked in the `labels` map. + val start = new asm.Label + jmethod.visitLabel(start) + scoping.pushScope(lv, start) + } - case DROP(kind) => emit(if(kind.isWideType) Opcodes.POP2 else Opcodes.POP) + case SCOPE_EXIT(lv) => + val relevant = (!lv.sym.isSynthetic && m.locals.contains(lv)) + if (relevant) { + // this label will have DEBUG bit set in its flags (ie ASM ignores it for dataflow purposes) + // similarly, these labels aren't tracked in the `labels` map. + val end = new asm.Label + jmethod.visitLabel(end) + scoping.popScope(lv, end, instr.pos) + } + } + genLocalInstr + + case icodes.stackCat => + def genStackInstr = (instr: @unchecked) match { + + case LOAD_MODULE(module) => + // assert(module.isModule, "Expected module: " + module) + debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags)); + if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) { + jmethod.visitVarInsn(Opcodes.ALOAD, 0) + } else { + jmethod.visitFieldInsn( + Opcodes.GETSTATIC, + javaName(module) /* + "$" */ , + strMODULE_INSTANCE_FIELD, + descriptor(module)) + } - case DUP(kind) => emit(if(kind.isWideType) Opcodes.DUP2 else Opcodes.DUP) + case DROP(kind) => emit(if (kind.isWideType) Opcodes.POP2 else Opcodes.POP) - case LOAD_EXCEPTION(_) => () - } + case DUP(kind) => emit(if (kind.isWideType) Opcodes.DUP2 else Opcodes.DUP) - case icodes.constCat => genConstant(jmethod, instr.asInstanceOf[CONSTANT].constant) + case LOAD_EXCEPTION(_) => () + } + genStackInstr - case icodes.arilogCat => genPrimitive(instr.asInstanceOf[CALL_PRIMITIVE].primitive, instr.pos) + case icodes.constCat => genConstant(jmethod, instr.asInstanceOf[CONSTANT].constant) - case icodes.castsCat => (instr: @unchecked) match { + case icodes.arilogCat => genPrimitive(instr.asInstanceOf[CALL_PRIMITIVE].primitive, instr.pos) - case IS_INSTANCE(tpe) => - val jtyp: asm.Type = - tpe match { - case REFERENCE(cls) => asm.Type.getObjectType(javaName(cls)) - case ARRAY(elem) => javaArrayType(javaType(elem)) - case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) - } - jmethod.visitTypeInsn(Opcodes.INSTANCEOF, jtyp.getInternalName) + case icodes.castsCat => + def genCastInstr = (instr: @unchecked) match { - case CHECK_CAST(tpe) => + case IS_INSTANCE(tpe) => + val jtyp: asm.Type = tpe match { - - case REFERENCE(cls) => - if (cls != ObjectClass) { // No need to checkcast for Objects - jmethod.visitTypeInsn(Opcodes.CHECKCAST, javaName(cls)) - } - - case ARRAY(elem) => - val iname = javaArrayType(javaType(elem)).getInternalName - jmethod.visitTypeInsn(Opcodes.CHECKCAST, iname) - - case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) + case REFERENCE(cls) => asm.Type.getObjectType(javaName(cls)) + case ARRAY(elem) => javaArrayType(javaType(elem)) + case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) } + jmethod.visitTypeInsn(Opcodes.INSTANCEOF, jtyp.getInternalName) - } - - case icodes.objsCat => (instr: @unchecked) match { + case CHECK_CAST(tpe) => + tpe match { - case BOX(kind) => - val MethodNameAndType(mname, mdesc) = jBoxTo(kind) - jcode.invokestatic(BoxesRunTime, mname, mdesc) + case REFERENCE(cls) => + if (cls != ObjectClass) { // No need to checkcast for Objects + jmethod.visitTypeInsn(Opcodes.CHECKCAST, javaName(cls)) + } - case UNBOX(kind) => - val MethodNameAndType(mname, mdesc) = jUnboxTo(kind) - jcode.invokestatic(BoxesRunTime, mname, mdesc) + case ARRAY(elem) => + val iname = javaArrayType(javaType(elem)).getInternalName + jmethod.visitTypeInsn(Opcodes.CHECKCAST, iname) - case NEW(REFERENCE(cls)) => - val className = javaName(cls) - jmethod.visitTypeInsn(Opcodes.NEW, className) + case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe) + } - case MONITOR_ENTER() => emit(Opcodes.MONITORENTER) - case MONITOR_EXIT() => emit(Opcodes.MONITOREXIT) - } + } + genCastInstr - case icodes.fldsCat => (instr: @unchecked) match { + case icodes.objsCat => + def genObjsInstr = (instr: @unchecked) match { - case lf @ LOAD_FIELD(field, isStatic) => - var owner = javaName(lf.hostClass) - debuglog("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags)) - val fieldJName = javaName(field) - val fieldDescr = descriptor(field) - val opc = if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD - jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr) + case BOX(kind) => + val MethodNameAndType(mname, mdesc) = jBoxTo(kind) + jcode.invokestatic(BoxesRunTime, mname, mdesc) - case STORE_FIELD(field, isStatic) => - val owner = javaName(field.owner) - val fieldJName = javaName(field) - val fieldDescr = descriptor(field) - val opc = if (isStatic) Opcodes.PUTSTATIC else Opcodes.PUTFIELD - jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr) + case UNBOX(kind) => + val MethodNameAndType(mname, mdesc) = jUnboxTo(kind) + jcode.invokestatic(BoxesRunTime, mname, mdesc) - } + case NEW(REFERENCE(cls)) => + val className = javaName(cls) + jmethod.visitTypeInsn(Opcodes.NEW, className) - case icodes.mthdsCat => (instr: @unchecked) match { + case MONITOR_ENTER() => emit(Opcodes.MONITORENTER) + case MONITOR_EXIT() => emit(Opcodes.MONITOREXIT) + } + genObjsInstr + + case icodes.fldsCat => + def genFldsInstr = (instr: @unchecked) match { + + case lf @ LOAD_FIELD(field, isStatic) => + var owner = javaName(lf.hostClass) + debuglog("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags)) + val fieldJName = javaName(field) + val fieldDescr = descriptor(field) + val opc = if (isStatic) Opcodes.GETSTATIC else Opcodes.GETFIELD + jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr) + + case STORE_FIELD(field, isStatic) => + val owner = javaName(field.owner) + val fieldJName = javaName(field) + val fieldDescr = descriptor(field) + val opc = if (isStatic) Opcodes.PUTSTATIC else Opcodes.PUTFIELD + jmethod.visitFieldInsn(opc, owner, fieldJName, fieldDescr) - /** Special handling to access native Array.clone() */ - case call @ CALL_METHOD(definitions.Array_clone, Dynamic) => - val target: String = javaType(call.targetTypeKind).getInternalName - jcode.invokevirtual(target, "clone", mdesc_arrayClone) + } + genFldsInstr - case call @ CALL_METHOD(method, style) => genCallMethod(call) + case icodes.mthdsCat => + def genMethodsInstr = (instr: @unchecked) match { - } + /** Special handling to access native Array.clone() */ + case call @ CALL_METHOD(definitions.Array_clone, Dynamic) => + val target: String = javaType(call.targetTypeKind).getInternalName + jcode.invokevirtual(target, "clone", mdesc_arrayClone) - case icodes.arraysCat => (instr: @unchecked) match { - case LOAD_ARRAY_ITEM(kind) => jcode.aload(kind) - case STORE_ARRAY_ITEM(kind) => jcode.astore(kind) - case CREATE_ARRAY(elem, 1) => jcode newarray elem - case CREATE_ARRAY(elem, dims) => jmethod.visitMultiANewArrayInsn(descriptor(ArrayN(elem, dims)), dims) - } + case call @ CALL_METHOD(method, style) => genCallMethod(call) - case icodes.jumpsCat => (instr: @unchecked) match { - - case sw @ SWITCH(tagss, branches) => - assert(branches.length == tagss.length + 1, sw) - val flatSize = sw.flatTagsCount - val flatKeys = new Array[Int](flatSize) - val flatBranches = new Array[asm.Label](flatSize) - - var restTagss = tagss - var restBranches = branches - var k = 0 // ranges over flatKeys and flatBranches - while(restTagss.nonEmpty) { - val currLabel = labels(restBranches.head) - for(cTag <- restTagss.head) { - flatKeys(k) = cTag; - flatBranches(k) = currLabel - k += 1 - } - restTagss = restTagss.tail - restBranches = restBranches.tail + } + genMethodsInstr + + case icodes.arraysCat => + def genArraysInstr = (instr: @unchecked) match { + case LOAD_ARRAY_ITEM(kind) => jcode.aload(kind) + case STORE_ARRAY_ITEM(kind) => jcode.astore(kind) + case CREATE_ARRAY(elem, 1) => jcode newarray elem + case CREATE_ARRAY(elem, dims) => jmethod.visitMultiANewArrayInsn(descriptor(ArrayN(elem, dims)), dims) + } + genArraysInstr + + case icodes.jumpsCat => + def genJumpInstr = (instr: @unchecked) match { + + case sw @ SWITCH(tagss, branches) => + assert(branches.length == tagss.length + 1, sw) + val flatSize = sw.flatTagsCount + val flatKeys = new Array[Int](flatSize) + val flatBranches = new Array[asm.Label](flatSize) + + var restTagss = tagss + var restBranches = branches + var k = 0 // ranges over flatKeys and flatBranches + while (restTagss.nonEmpty) { + val currLabel = labels(restBranches.head) + for (cTag <- restTagss.head) { + flatKeys(k) = cTag; + flatBranches(k) = currLabel + k += 1 } - val defaultLabel = labels(restBranches.head) - assert(restBranches.tail.isEmpty) - debuglog("Emitting SWITCH:\ntags: " + tagss + "\nbranches: " + branches) - jcode.emitSWITCH(flatKeys, flatBranches, defaultLabel, MIN_SWITCH_DENSITY) - - case JUMP(whereto) => - if (nextBlock != whereto) { - jcode goTo labels(whereto) - } else if(m.exh.exists(eh => eh.covers(b))) { - // SI-6102: Determine whether eliding this JUMP results in an empty range being covered by some EH. - // If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range" - val isSthgLeft = b.toList.exists { - case _: LOAD_EXCEPTION => false - case _: SCOPE_ENTER => false - case _: SCOPE_EXIT => false - case _: JUMP => false - case _ => true - } - if(!isSthgLeft) { - emit(asm.Opcodes.NOP) - } + restTagss = restTagss.tail + restBranches = restBranches.tail + } + val defaultLabel = labels(restBranches.head) + assert(restBranches.tail.isEmpty) + debuglog("Emitting SWITCH:\ntags: " + tagss + "\nbranches: " + branches) + jcode.emitSWITCH(flatKeys, flatBranches, defaultLabel, MIN_SWITCH_DENSITY) + + case JUMP(whereto) => + if (nextBlock != whereto) { + jcode goTo labels(whereto) + } else if (m.exh.exists(eh => eh.covers(b))) { + // SI-6102: Determine whether eliding this JUMP results in an empty range being covered by some EH. + // If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range" + val isSthgLeft = b.toList.exists { + case _: LOAD_EXCEPTION => false + case _: SCOPE_ENTER => false + case _: SCOPE_EXIT => false + case _: JUMP => false + case _ => true + } + if (!isSthgLeft) { + emit(asm.Opcodes.NOP) } + } - case CJUMP(success, failure, cond, kind) => - if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT - if (nextBlock == success) { - jcode.emitIF_ICMP(cond.negate, labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF_ICMP(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } - } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) - if (nextBlock == success) { - jcode.emitIF_ACMP(cond.negate, labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF_ACMP(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } + case CJUMP(success, failure, cond, kind) => + if (kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT + if (nextBlock == success) { + jcode.emitIF_ICMP(cond.negate, labels(failure)) + // .. and fall through to success label } else { - (kind: @unchecked) match { - case LONG => emit(Opcodes.LCMP) - case FLOAT => - if (cond == LT || cond == LE) emit(Opcodes.FCMPG) - else emit(Opcodes.FCMPL) - case DOUBLE => - if (cond == LT || cond == LE) emit(Opcodes.DCMPG) - else emit(Opcodes.DCMPL) - } - if (nextBlock == success) { - jcode.emitIF(cond.negate, labels(failure)) - // .. and fall through to success label - } else { - jcode.emitIF(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } + jcode.emitIF_ICMP(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } } - - case CZJUMP(success, failure, cond, kind) => - if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT - if (nextBlock == success) { - jcode.emitIF(cond.negate, labels(failure)) - } else { - jcode.emitIF(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } - } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) - val Success = success - val Failure = failure - // @unchecked because references aren't compared with GT, GE, LT, LE. - ((cond, nextBlock) : @unchecked) match { - case (EQ, Success) => jcode emitIFNONNULL labels(failure) - case (NE, Failure) => jcode emitIFNONNULL labels(success) - case (EQ, Failure) => jcode emitIFNULL labels(success) - case (NE, Success) => jcode emitIFNULL labels(failure) - case (EQ, _) => - jcode emitIFNULL labels(success) - jcode goTo labels(failure) - case (NE, _) => - jcode emitIFNONNULL labels(success) - jcode goTo labels(failure) - } + } else if (kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) + if (nextBlock == success) { + jcode.emitIF_ACMP(cond.negate, labels(failure)) + // .. and fall through to success label } else { - (kind: @unchecked) match { - case LONG => - emit(Opcodes.LCONST_0) - emit(Opcodes.LCMP) - case FLOAT => - emit(Opcodes.FCONST_0) - if (cond == LT || cond == LE) emit(Opcodes.FCMPG) - else emit(Opcodes.FCMPL) - case DOUBLE => - emit(Opcodes.DCONST_0) - if (cond == LT || cond == LE) emit(Opcodes.DCMPG) - else emit(Opcodes.DCMPL) - } - if (nextBlock == success) { - jcode.emitIF(cond.negate, labels(failure)) - } else { - jcode.emitIF(cond, labels(success)) - if (nextBlock != failure) { jcode goTo labels(failure) } - } + jcode.emitIF_ACMP(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } } + } else { + (kind: @unchecked) match { + case LONG => emit(Opcodes.LCMP) + case FLOAT => + if (cond == LT || cond == LE) emit(Opcodes.FCMPG) + else emit(Opcodes.FCMPL) + case DOUBLE => + if (cond == LT || cond == LE) emit(Opcodes.DCMPG) + else emit(Opcodes.DCMPL) + } + if (nextBlock == success) { + jcode.emitIF(cond.negate, labels(failure)) + // .. and fall through to success label + } else { + jcode.emitIF(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } + } + } - } - - case icodes.retCat => (instr: @unchecked) match { - case RETURN(kind) => jcode emitRETURN kind - case THROW(_) => emit(Opcodes.ATHROW) - } + case CZJUMP(success, failure, cond, kind) => + if (kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT + if (nextBlock == success) { + jcode.emitIF(cond.negate, labels(failure)) + } else { + jcode.emitIF(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } + } + } else if (kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) + val Success = success + val Failure = failure + // @unchecked because references aren't compared with GT, GE, LT, LE. + ((cond, nextBlock): @unchecked) match { + case (EQ, Success) => jcode emitIFNONNULL labels(failure) + case (NE, Failure) => jcode emitIFNONNULL labels(success) + case (EQ, Failure) => jcode emitIFNULL labels(success) + case (NE, Success) => jcode emitIFNULL labels(failure) + case (EQ, _) => + jcode emitIFNULL labels(success) + jcode goTo labels(failure) + case (NE, _) => + jcode emitIFNONNULL labels(success) + jcode goTo labels(failure) + } + } else { + (kind: @unchecked) match { + case LONG => + emit(Opcodes.LCONST_0) + emit(Opcodes.LCMP) + case FLOAT => + emit(Opcodes.FCONST_0) + if (cond == LT || cond == LE) emit(Opcodes.FCMPG) + else emit(Opcodes.FCMPL) + case DOUBLE => + emit(Opcodes.DCONST_0) + if (cond == LT || cond == LE) emit(Opcodes.DCMPG) + else emit(Opcodes.DCMPL) + } + if (nextBlock == success) { + jcode.emitIF(cond.negate, labels(failure)) + } else { + jcode.emitIF(cond, labels(success)) + if (nextBlock != failure) { jcode goTo labels(failure) } + } + } } + genJumpInstr + case icodes.retCat => + def genRetInstr = (instr: @unchecked) match { + case RETURN(kind) => jcode emitRETURN kind + case THROW(_) => emit(Opcodes.ATHROW) + } + genRetInstr } - - } // end of genCode()'s genBlock() + } /** * Emits one or more conversion instructions based on the types given as arguments. @@ -2759,6 +2785,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { case Negation(kind) => jcode.neg(kind) case Arithmetic(op, kind) => + def genArith = { op match { case ADD => jcode.add(kind) @@ -2781,57 +2808,89 @@ abstract class GenASM extends SubComponent with BytecodeWriters { case _ => abort("Unknown arithmetic primitive " + primitive) } + } + genArith // TODO Logical's 2nd elem should be declared ValueTypeKind, to better approximate its allowed values (isIntSized, its comments appears to convey) // TODO GenICode uses `toTypeKind` to define that elem, `toValueTypeKind` would be needed instead. // TODO How about adding some asserts to Logical and similar ones to capture the remaining constraint (UNIT not allowed). - case Logical(op, kind) => ((op, kind): @unchecked) match { - case (AND, LONG) => emit(Opcodes.LAND) - case (AND, INT) => emit(Opcodes.IAND) - case (AND, _) => - emit(Opcodes.IAND) - if (kind != BOOL) { emitT2T(INT, kind) } - - case (OR, LONG) => emit(Opcodes.LOR) - case (OR, INT) => emit(Opcodes.IOR) - case (OR, _) => - emit(Opcodes.IOR) - if (kind != BOOL) { emitT2T(INT, kind) } - - case (XOR, LONG) => emit(Opcodes.LXOR) - case (XOR, INT) => emit(Opcodes.IXOR) - case (XOR, _) => - emit(Opcodes.IXOR) - if (kind != BOOL) { emitT2T(INT, kind) } - } - - case Shift(op, kind) => ((op, kind): @unchecked) match { - case (LSL, LONG) => emit(Opcodes.LSHL) - case (LSL, INT) => emit(Opcodes.ISHL) - case (LSL, _) => - emit(Opcodes.ISHL) - emitT2T(INT, kind) - - case (ASR, LONG) => emit(Opcodes.LSHR) - case (ASR, INT) => emit(Opcodes.ISHR) - case (ASR, _) => - emit(Opcodes.ISHR) - emitT2T(INT, kind) - - case (LSR, LONG) => emit(Opcodes.LUSHR) - case (LSR, INT) => emit(Opcodes.IUSHR) - case (LSR, _) => - emit(Opcodes.IUSHR) - emitT2T(INT, kind) - } + case Logical(op, kind) => + def genLogical = op match { + case AND => + kind match { + case LONG => emit(Opcodes.LAND) + case INT => emit(Opcodes.IAND) + case _ => + emit(Opcodes.IAND) + if (kind != BOOL) { emitT2T(INT, kind) } + } + case OR => + kind match { + case LONG => emit(Opcodes.LOR) + case INT => emit(Opcodes.IOR) + case _ => + emit(Opcodes.IOR) + if (kind != BOOL) { emitT2T(INT, kind) } + } + case XOR => + kind match { + case LONG => emit(Opcodes.LXOR) + case INT => emit(Opcodes.IXOR) + case _ => + emit(Opcodes.IXOR) + if (kind != BOOL) { emitT2T(INT, kind) } + } + } + genLogical + + case Shift(op, kind) => + def genShift = op match { + case LSL => + kind match { + case LONG => emit(Opcodes.LSHL) + case INT => emit(Opcodes.ISHL) + case _ => + emit(Opcodes.ISHL) + emitT2T(INT, kind) + } + case ASR => + kind match { + case LONG => emit(Opcodes.LSHR) + case INT => emit(Opcodes.ISHR) + case _ => + emit(Opcodes.ISHR) + emitT2T(INT, kind) + } + case LSR => + kind match { + case LONG => emit(Opcodes.LUSHR) + case INT => emit(Opcodes.IUSHR) + case _ => + emit(Opcodes.IUSHR) + emitT2T(INT, kind) + } + } + genShift - case Comparison(op, kind) => ((op, kind): @unchecked) match { - case (CMP, LONG) => emit(Opcodes.LCMP) - case (CMPL, FLOAT) => emit(Opcodes.FCMPL) - case (CMPG, FLOAT) => emit(Opcodes.FCMPG) - case (CMPL, DOUBLE) => emit(Opcodes.DCMPL) - case (CMPG, DOUBLE) => emit(Opcodes.DCMPL) // TODO bug? why not DCMPG? http://docs.oracle.com/javase/specs/jvms/se5.0/html/Instructions2.doc3.html - } + case Comparison(op, kind) => + def genCompare = op match { + case CMP => + (kind: @unchecked) match { + case LONG => emit(Opcodes.LCMP) + } + case CMPL => + (kind: @unchecked) match { + case FLOAT => emit(Opcodes.FCMPL) + case DOUBLE => emit(Opcodes.DCMPL) + } + case CMPG => + (kind: @unchecked) match { + case FLOAT => emit(Opcodes.FCMPG) + case DOUBLE => emit(Opcodes.DCMPL) // TODO bug? why not DCMPG? http://docs.oracle.com/javase/specs/jvms/se5.0/html/Instructions2.doc3.html + + } + } + genCompare case Conversion(src, dst) => debuglog("Converting from: " + src + " to: " + dst) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 11b0c40be7..930791d88d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -1960,7 +1960,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with // Nested objects won't receive ACC_FINAL in order to allow for their overriding. val finalFlag = ( - (sym.hasFlag(Flags.FINAL) || isTopLevelModule(sym)) + (((sym.rawflags & Flags.FINAL) != 0) || isTopLevelModule(sym)) && !sym.enclClass.isInterface && !sym.isClassConstructor && !sym.isMutable // lazy vals and vars both 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 1f68781777..24a26b2ad3 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -49,7 +49,11 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp if(top === self) {{ var url = '{ val p = templateToPath(tpl); "../" * (p.size - 1) + "index.html" }'; var hash = '{ val p = templateToPath(tpl); (p.tail.reverse ::: List(p.head.replace(".html", ""))).mkString(".") }'; - window.location.href = url + '#' + hash; + var anchor = window.location.hash; + var anchor_opt = ''; + if (anchor.length { scala.xml.Unparsed(">=") /* unless we use Unparsed, it gets escaped and crashes the script */ } 1) + anchor_opt = '@' + anchor.substring(1); + window.location.href = url + '#' + hash + anchor_opt; }} </script> </xml:group> diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala b/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala index 2c16a754cb..4a4a900e2b 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoopInit.scala @@ -8,6 +8,7 @@ package interpreter import scala.reflect.internal.util.Position import scala.util.control.Exception.ignoring +import scala.tools.nsc.util.stackTraceString /** * Machinery for the asynchronous initialization of the repl. @@ -94,9 +95,7 @@ trait ILoopInit { runThunks() } catch { case ex: Throwable => - val message = new java.io.StringWriter() - ex.printStackTrace(new java.io.PrintWriter(message)) - initError = message.toString + initError = stackTraceString(ex) throw ex } finally { initIsComplete = true diff --git a/src/compiler/scala/tools/nsc/interpreter/Imports.scala b/src/compiler/scala/tools/nsc/interpreter/Imports.scala index 5193166889..14d43bc6d5 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Imports.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Imports.scala @@ -23,7 +23,7 @@ trait Imports { val hd :: tl = sym.fullName.split('.').toList map newTermName val tree = Import( tl.foldLeft(Ident(hd): Tree)((x, y) => Select(x, y)), - List(ImportSelector(nme.WILDCARD, -1, null, -1)) + ImportSelector.wildList ) tree setSymbol sym new ImportHandler(tree) @@ -192,4 +192,4 @@ trait Imports { private def membersAtPickler(sym: Symbol): List[Symbol] = beforePickler(sym.info.nonPrivateMembers.toList) -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index d1c404b3e3..c5da8822d5 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -551,7 +551,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { if (parentToken == AT && in.token == DEFAULT) { val annot = atPos(pos) { - New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), List(List())) + New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), ListOfNil) } mods1 = mods1 withAnnotations List(annot) skipTo(SEMI) @@ -640,7 +640,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def importCompanionObject(cdef: ClassDef): Tree = atPos(cdef.pos) { - Import(Ident(cdef.name.toTermName), List(ImportSelector(nme.WILDCARD, -1, null, -1))) + Import(Ident(cdef.name.toTermName), ImportSelector.wildList) } // Importing the companion object members cannot be done uncritically: see @@ -841,7 +841,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val predefs = List( DefDef( Modifiers(Flags.JAVA | Flags.STATIC), nme.values, List(), - List(List()), + ListOfNil, arrayOf(enumType), blankExpr), DefDef( diff --git a/src/compiler/scala/tools/nsc/package.scala b/src/compiler/scala/tools/nsc/package.scala index 9ad0d9ba1f..9d593e5acc 100644 --- a/src/compiler/scala/tools/nsc/package.scala +++ b/src/compiler/scala/tools/nsc/package.scala @@ -14,4 +14,6 @@ package object nsc { type MissingRequirementError = scala.reflect.internal.MissingRequirementError val MissingRequirementError = scala.reflect.internal.MissingRequirementError + + val ListOfNil = List(Nil) } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 213a995e96..8e77f8b6f4 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -250,7 +250,7 @@ abstract class SymbolLoaders { protected def description = "class file "+ classfile.toString protected def doComplete(root: Symbol) { - val start = Statistics.startTimer(classReadNanos) + val start = if (Statistics.canEnable) Statistics.startTimer(classReadNanos) else null classfileParser.parse(classfile, root) if (root.associatedFile eq null) { root match { @@ -262,7 +262,7 @@ abstract class SymbolLoaders { debuglog("Not setting associatedFile to %s because %s is a %s".format(classfile, root.name, root.shortSymbolClass)) } } - Statistics.stopTimer(classReadNanos, start) + if (Statistics.canEnable) Statistics.stopTimer(classReadNanos, start) } override def sourcefile: Option[AbstractFile] = classfileParser.srcfile } diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 436867257a..570704f049 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -50,14 +50,14 @@ abstract class CleanUp extends Transform with ast.TreeDSL { private def transformTemplate(tree: Tree) = { val t @ Template(parents, self, body) = tree clearStatics() - + val newBody = transformTrees(body) val templ = deriveTemplate(tree)(_ => transformTrees(newStaticMembers.toList) ::: newBody) try addStaticInits(templ) // postprocess to include static ctors finally clearStatics() } private def mkTerm(prefix: String): TermName = unit.freshTermName(prefix) - + //private val classConstantMeth = new HashMap[String, Symbol] //private val symbolStaticFields = new HashMap[String, (Symbol, Tree, Tree)] @@ -91,40 +91,9 @@ abstract class CleanUp extends Transform with ast.TreeDSL { */ def toBoxedType(tp: Type) = if (isJavaValueType(tp)) boxedClass(tp.typeSymbol).tpe else tp - override def transform(tree: Tree): Tree = tree match { - - /* Transforms dynamic calls (i.e. calls to methods that are undefined - * in the erased type space) to -- dynamically -- unsafe calls using - * reflection. This is used for structural sub-typing of refinement - * types, but may be used for other dynamic calls in the future. - * For 'a.f(b)' it will generate something like: - * 'a.getClass(). - * ' getMethod("f", Array(classOf[b.type])). - * ' invoke(a, Array(b)) - * plus all the necessary casting/boxing/etc. machinery required - * for type-compatibility (see fixResult). - * - * USAGE CONTRACT: - * There are a number of assumptions made on the way a dynamic apply - * is used. Assumptions relative to type are handled by the erasure - * phase. - * - The applied arguments are compatible with AnyRef, which means - * that an argument tree typed as AnyVal has already been extended - * with the necessary boxing calls. This implies that passed - * arguments might not be strictly compatible with the method's - * parameter types (a boxed integer while int is expected). - * - The expected return type is an AnyRef, even when the method's - * return type is an AnyVal. This means that the tree containing the - * call has already been extended with the necessary unboxing calls - * (or is happy with the boxed type). - * - The type-checker has prevented dynamic applies on methods which - * parameter's erased types are not statically known at the call site. - * This is necessary to allow dispatching the call to the correct - * method (dispatching on parameters is static in Scala). In practice, - * this limitation only arises when the called method is defined as a - * refinement, where the refinement defines a parameter based on a - * type variable. */ - case ad@ApplyDynamic(qual0, params) => + def transformApplyDynamic(ad: ApplyDynamic) = { + val qual0 = ad.qual + val params = ad.args if (settings.logReflectiveCalls.value) unit.echo(ad.pos, "method invocation uses reflection") @@ -516,6 +485,44 @@ abstract class CleanUp extends Transform with ast.TreeDSL { transform(t) } /* ### END OF DYNAMIC APPLY TRANSFORM ### */ + } + + override def transform(tree: Tree): Tree = tree match { + + /* Transforms dynamic calls (i.e. calls to methods that are undefined + * in the erased type space) to -- dynamically -- unsafe calls using + * reflection. This is used for structural sub-typing of refinement + * types, but may be used for other dynamic calls in the future. + * For 'a.f(b)' it will generate something like: + * 'a.getClass(). + * ' getMethod("f", Array(classOf[b.type])). + * ' invoke(a, Array(b)) + * plus all the necessary casting/boxing/etc. machinery required + * for type-compatibility (see fixResult). + * + * USAGE CONTRACT: + * There are a number of assumptions made on the way a dynamic apply + * is used. Assumptions relative to type are handled by the erasure + * phase. + * - The applied arguments are compatible with AnyRef, which means + * that an argument tree typed as AnyVal has already been extended + * with the necessary boxing calls. This implies that passed + * arguments might not be strictly compatible with the method's + * parameter types (a boxed integer while int is expected). + * - The expected return type is an AnyRef, even when the method's + * return type is an AnyVal. This means that the tree containing the + * call has already been extended with the necessary unboxing calls + * (or is happy with the boxed type). + * - The type-checker has prevented dynamic applies on methods which + * parameter's erased types are not statically known at the call site. + * This is necessary to allow dispatching the call to the correct + * method (dispatching on parameters is static in Scala). In practice, + * this limitation only arises when the called method is defined as a + * refinement, where the refinement defines a parameter based on a + * type variable. */ + + case tree: ApplyDynamic => + transformApplyDynamic(tree) /* Some cleanup transformations add members to templates (classes, traits, etc). * When inside a template (i.e. the body of one of its members), two maps @@ -542,12 +549,13 @@ abstract class CleanUp extends Transform with ast.TreeDSL { else tree } - + case ValDef(mods, name, tpt, rhs) if tree.symbol.hasStaticAnnotation => + def transformStaticValDef = { log("moving @static valdef field: " + name + ", in: " + tree.symbol.owner) val sym = tree.symbol val owner = sym.owner - + val staticBeforeLifting = atPhase(currentRun.erasurePhase) { owner.isStatic } val isPrivate = atPhase(currentRun.typerPhase) { sym.getter(owner).hasFlag(PRIVATE) } val isProtected = atPhase(currentRun.typerPhase) { sym.getter(owner).hasFlag(PROTECTED) } @@ -574,19 +582,19 @@ abstract class CleanUp extends Transform with ast.TreeDSL { val compclass = enclosing.newClass(newTypeName(owner.name.toString)) compclass setInfo ClassInfoType(List(ObjectClass.tpe), newScope, compclass) enclosing.info.decls enter compclass - - val compclstree = ClassDef(compclass, NoMods, List(List()), List(List()), List(), tree.pos) - + + val compclstree = ClassDef(compclass, NoMods, ListOfNil, ListOfNil, List(), tree.pos) + syntheticClasses.getOrElseUpdate(enclosing, mutable.Set()) += compclstree - + compclass case comp => comp } - + // create a static field in the companion class for this @static field val stfieldSym = linkedClass.newVariable(newTermName(name), tree.pos, STATIC | SYNTHETIC | FINAL) setInfo sym.tpe stfieldSym.addAnnotation(StaticClass) - + val names = classNames.getOrElseUpdate(linkedClass, linkedClass.info.decls.collect { case sym if sym.name.isTermName => sym.name } toSet) @@ -597,9 +605,9 @@ abstract class CleanUp extends Transform with ast.TreeDSL { ) } else { linkedClass.info.decls enter stfieldSym - + val initializerBody = rhs - + // static field was previously initialized in the companion object itself, like this: // staticBodies((linkedClass, stfieldSym)) = Select(This(owner), sym.getter(owner)) // instead, we move the initializer to the static ctor of the companion class @@ -608,12 +616,15 @@ abstract class CleanUp extends Transform with ast.TreeDSL { } } super.transform(tree) - + } + transformStaticValDef + /* MSIL requires that the stack is empty at the end of a try-block. * Hence, we here rewrite all try blocks with a result != {Unit, All} such that they * store their result in a local variable. The catch blocks are adjusted as well. * The try tree is subsituted by a block whose result expression is read of that variable. */ case theTry @ Try(block, catches, finalizer) if shouldRewriteTry(theTry) => + def transformTry = { val tpe = theTry.tpe.widen val tempVar = currentOwner.newVariable(mkTerm(nme.EXCEPTION_RESULT_PREFIX), theTry.pos).setInfo(tpe) def assignBlock(rhs: Tree) = super.transform(BLOCK(Ident(tempVar) === transform(rhs))) @@ -624,7 +635,8 @@ abstract class CleanUp extends Transform with ast.TreeDSL { val newTry = Try(newBlock, newCatches, super.transform(finalizer)) typedWithPos(theTry.pos)(BLOCK(VAL(tempVar) === EmptyTree, newTry, Ident(tempVar))) - + } + transformTry /* * This transformation should identify Scala symbol invocations in the tree and replace them * with references to a static member. Also, whenever a class has at least a single symbol invocation @@ -657,12 +669,15 @@ abstract class CleanUp extends Transform with ast.TreeDSL { * have little in common. */ case Apply(fn, (arg @ Literal(Constant(symname: String))) :: Nil) if fn.symbol == Symbol_apply => + def transformApply = { // add the symbol name to a map if it's not there already val rhs = gen.mkMethodCall(Symbol_apply, arg :: Nil) val staticFieldSym = getSymbolStaticField(tree.pos, symname, rhs, tree) // create a reference to a static field val ntree = typedWithPos(tree.pos)(REF(staticFieldSym)) super.transform(ntree) + } + transformApply // This transform replaces Array(Predef.wrapArray(Array(...)), <tag>) // with just Array(...) @@ -722,7 +737,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { case Block(stats, expr) => stats :+ expr case t => List(t) } - + val newCtor = findStaticCtor(template) match { // in case there already were static ctors - augment existing ones // currently, however, static ctors aren't being generated anywhere else @@ -746,7 +761,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { deriveTemplate(template)(newCtor :: _) } } - + private def addStaticDeclarations(tree: Template, clazz: Symbol) { // add static field initializer statements for each static field in clazz if (!clazz.isModuleClass) for { @@ -757,22 +772,23 @@ abstract class CleanUp extends Transform with ast.TreeDSL { val valdef = staticBodies((clazz, stfieldSym)) val ValDef(_, _, _, rhs) = valdef val fixedrhs = rhs.changeOwner((valdef.symbol, clazz.info.decl(nme.CONSTRUCTOR))) - + val stfieldDef = localTyper.typedPos(tree.pos)(VAL(stfieldSym) === EmptyTree) val flattenedInit = fixedrhs match { case Block(stats, expr) => Block(stats, REF(stfieldSym) === expr) case rhs => REF(stfieldSym) === rhs } val stfieldInit = localTyper.typedPos(tree.pos)(flattenedInit) - + // add field definition to new defs newStaticMembers append stfieldDef newStaticInits append stfieldInit + case _ => // ignore @static on other members } } - - - + + + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { super.transformStats(stats, exprOwner) ++ { // flush pending synthetic classes created in this owner @@ -785,22 +801,22 @@ abstract class CleanUp extends Transform with ast.TreeDSL { case clsdef @ ClassDef(mods, name, tparams, t @ Template(parent, self, body)) => // process all classes in the package again to add static initializers clearStatics() - + addStaticDeclarations(t, clsdef.symbol) - + val templ = deriveTemplate(t)(_ => transformTrees(newStaticMembers.toList) ::: body) val ntempl = try addStaticInits(templ) finally clearStatics() - + val derived = deriveClassDef(clsdef)(_ => ntempl) classNames.remove(clsdef.symbol) derived - + case stat => stat } } - + } // CleanUpTransformer } diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index f2f4a44b02..afc109c47a 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -506,14 +506,14 @@ abstract class Constructors extends Transform with ast.TreeDSL { val applyMethodDef = DefDef( sym = applyMethod, - vparamss = List(List()), + vparamss = ListOfNil, rhs = Block(applyMethodStats, gen.mkAttributedRef(BoxedUnit_UNIT))) ClassDef( sym = closureClass, constrMods = Modifiers(0), vparamss = List(List(outerFieldDef)), - argss = List(List()), + argss = ListOfNil, body = List(applyMethodDef), superPos = impl.pos) } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index eb3c965d7f..d97fbf5daa 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -271,8 +271,11 @@ abstract class Erasure extends AddInterfaces poly + jsig(restpe) case MethodType(params, restpe) => - "("+(params map (_.tpe) map (jsig(_))).mkString+")"+ - (if (restpe.typeSymbol == UnitClass || sym0.isConstructor) VOID_TAG.toString else jsig(restpe)) + val buf = new StringBuffer("(") + params foreach (p => buf append jsig(p.tpe)) + buf append ")" + buf append (if (restpe.typeSymbol == UnitClass || sym0.isConstructor) VOID_TAG.toString else jsig(restpe)) + buf.toString case RefinedType(parent :: _, decls) => boxedSig(parent) @@ -485,7 +488,7 @@ abstract class Erasure extends AddInterfaces private def isPrimitiveValueMember(sym: Symbol) = sym != NoSymbol && isPrimitiveValueClass(sym.owner) - private def box(tree: Tree, target: => String): Tree = { + @inline private def box(tree: Tree, target: => String): Tree = { val result = box1(tree) log("boxing "+tree+":"+tree.tpe+" to "+target+" = "+result+":"+result.tpe) result @@ -928,152 +931,175 @@ abstract class Erasure extends AddInterfaces * - Reset all other type attributes to null, thus enforcing a retyping. */ private val preTransformer = new TypingTransformer(unit) { - def preErase(tree: Tree): Tree = tree match { - case ClassDef(_,_,_,_) => - debuglog("defs of " + tree.symbol + " = " + tree.symbol.info.decls) - copyClassDef(tree)(tparams = Nil) - case DefDef(_,_,_,_,_,_) => - copyDefDef(tree)(tparams = Nil) - case TypeDef(_, _, _, _) => - EmptyTree - case Apply(instanceOf @ TypeApply(fun @ Select(qual, name), args @ List(arg)), List()) // !!! todo: simplify by having GenericArray also extract trees - if ((fun.symbol == Any_isInstanceOf || fun.symbol == Object_isInstanceOf) && - unboundedGenericArrayLevel(arg.tpe) > 0) => - val level = unboundedGenericArrayLevel(arg.tpe) - def isArrayTest(arg: Tree) = - gen.mkRuntimeCall(nme.isArray, List(arg, Literal(Constant(level)))) - - global.typer.typedPos(tree.pos) { - if (level == 1) isArrayTest(qual) - else gen.evalOnce(qual, currentOwner, unit) { qual1 => - gen.mkAnd( - gen.mkMethodCall( - qual1(), - fun.symbol, - List(specialErasure(fun.symbol)(arg.tpe)), - Nil - ), - isArrayTest(qual1()) - ) - } + + private def preEraseNormalApply(tree: Apply) = { + val fn = tree.fun + val args = tree.args + + def qualifier = fn match { + case Select(qual, _) => qual + case TypeApply(Select(qual, _), _) => qual + } + + def preEraseAsInstanceOf = { + (fn: @unchecked) match { + case TypeApply(Select(qual, _), List(targ)) => + if (qual.tpe <:< targ.tpe) + atPos(tree.pos) { Typed(qual, TypeTree(targ.tpe)) } + else if (isNumericValueClass(qual.tpe.typeSymbol) && isNumericValueClass(targ.tpe.typeSymbol)) + atPos(tree.pos)(numericConversion(qual, targ.tpe.typeSymbol)) + else + tree } - case TypeApply(fun, args) if (fun.symbol.owner != AnyClass && - fun.symbol != Object_asInstanceOf && - fun.symbol != Object_isInstanceOf) => - // leave all other type tests/type casts, remove all other type applications - preErase(fun) - case Apply(fn @ Select(qual, name), args) if fn.symbol.owner == ArrayClass => - // Have to also catch calls to abstract types which are bounded by Array. - if (unboundedGenericArrayLevel(qual.tpe.widen) == 1 || qual.tpe.typeSymbol.isAbstractType) { - // convert calls to apply/update/length on generic arrays to - // calls of ScalaRunTime.array_xxx method calls - global.typer.typedPos(tree.pos)({ - val arrayMethodName = name match { - case nme.apply => nme.array_apply - case nme.length => nme.array_length - case nme.update => nme.array_update - case nme.clone_ => nme.array_clone - case _ => unit.error(tree.pos, "Unexpected array member, no translation exists.") ; nme.NO_NAME + // todo: also handle the case where the singleton type is buried in a compound + } + + def preEraseIsInstanceOf = { + fn match { + case TypeApply(sel @ Select(qual, name), List(targ)) => + if (qual.tpe != null && isPrimitiveValueClass(qual.tpe.typeSymbol) && targ.tpe != null && targ.tpe <:< AnyRefClass.tpe) + unit.error(sel.pos, "isInstanceOf cannot test if value types are references.") + + def mkIsInstanceOf(q: () => Tree)(tp: Type): Tree = + Apply( + TypeApply( + Select(q(), Object_isInstanceOf) setPos sel.pos, + List(TypeTree(tp) setPos targ.pos)) setPos fn.pos, + List()) setPos tree.pos + targ.tpe match { + case SingleType(_, _) | ThisType(_) | SuperType(_, _) => + val cmpOp = if (targ.tpe <:< AnyValClass.tpe) Any_equals else Object_eq + atPos(tree.pos) { + Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe))) + } + case RefinedType(parents, decls) if (parents.length >= 2) => + // Optimization: don't generate isInstanceOf tests if the static type + // conforms, because it always succeeds. (Or at least it had better.) + // At this writing the pattern matcher generates some instance tests + // involving intersections where at least one parent is statically known true. + // That needs fixing, but filtering the parents here adds an additional + // level of robustness (in addition to the short term fix.) + val parentTests = parents filterNot (qual.tpe <:< _) + + if (parentTests.isEmpty) Literal(Constant(true)) + else gen.evalOnce(qual, currentOwner, unit) { q => + atPos(tree.pos) { + parentTests map mkIsInstanceOf(q) reduceRight gen.mkAnd + } + } + case _ => + tree } - gen.mkRuntimeCall(arrayMethodName, qual :: args) - }) + case _ => tree } - else { - // store exact array erasure in map to be retrieved later when we might - // need to do the cast in adaptMember - treeCopy.Apply( - tree, - SelectFromArray(qual, name, erasure(tree.symbol)(qual.tpe)).copyAttrs(fn), - args) - } - case Apply(fn @ Select(qual, _), Nil) if interceptedMethods(fn.symbol) => - if (fn.symbol == Any_## || fn.symbol == Object_##) { - // This is unattractive, but without it we crash here on ().## because after - // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int. - // This must be because some earlier transformation is being skipped on ##, but so - // far I don't know what. For null we now define null.## == 0. - qual.tpe.typeSymbol match { - case UnitClass | NullClass => LIT(0) - case IntClass => qual - case s @ (ShortClass | ByteClass | CharClass) => numericConversion(qual, s) - case BooleanClass => If(qual, LIT(true.##), LIT(false.##)) - case _ => - global.typer.typed(gen.mkRuntimeCall(nme.hash_, List(qual))) - } - } - // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) - else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) - global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen)))) - else - tree + } + if (fn.symbol == Any_asInstanceOf) { + preEraseAsInstanceOf + } else if (fn.symbol == Any_isInstanceOf) { + preEraseIsInstanceOf + } else if (fn.symbol.owner.isRefinementClass && !fn.symbol.isOverridingSymbol) { + ApplyDynamic(qualifier, args) setSymbol fn.symbol setPos tree.pos + } else if (fn.symbol.isMethodWithExtension) { + Apply(gen.mkAttributedRef(extensionMethods.extensionMethod(fn.symbol)), qualifier :: args) + } else { + tree + } + } - case Apply(Select(New(tpt), nme.CONSTRUCTOR), List(arg)) if (tpt.tpe.typeSymbol.isDerivedValueClass) => -// println("inject derived: "+arg+" "+tpt.tpe) - InjectDerivedValue(arg) addAttachment //@@@ setSymbol tpt.tpe.typeSymbol - new TypeRefAttachment(tree.tpe.asInstanceOf[TypeRef]) - case Apply(fn, args) => - def qualifier = fn match { - case Select(qual, _) => qual - case TypeApply(Select(qual, _), _) => qual - } - if (fn.symbol == Any_asInstanceOf) - (fn: @unchecked) match { - case TypeApply(Select(qual, _), List(targ)) => - if (qual.tpe <:< targ.tpe) - atPos(tree.pos) { Typed(qual, TypeTree(targ.tpe)) } - else if (isNumericValueClass(qual.tpe.typeSymbol) && isNumericValueClass(targ.tpe.typeSymbol)) - atPos(tree.pos)(numericConversion(qual, targ.tpe.typeSymbol)) - else - tree + private def preEraseApply(tree: Apply) = { + tree.fun match { + case TypeApply(fun @ Select(qual, name), args @ List(arg)) + if ((fun.symbol == Any_isInstanceOf || fun.symbol == Object_isInstanceOf) && + unboundedGenericArrayLevel(arg.tpe) > 0) => // !!! todo: simplify by having GenericArray also extract trees + val level = unboundedGenericArrayLevel(arg.tpe) + def isArrayTest(arg: Tree) = + gen.mkRuntimeCall(nme.isArray, List(arg, Literal(Constant(level)))) + + global.typer.typedPos(tree.pos) { + if (level == 1) isArrayTest(qual) + else gen.evalOnce(qual, currentOwner, unit) { qual1 => + gen.mkAnd( + gen.mkMethodCall( + qual1(), + fun.symbol, + List(specialErasure(fun.symbol)(arg.tpe)), + Nil + ), + isArrayTest(qual1()) + ) + } } - // todo: also handle the case where the singleton type is buried in a compound - else if (fn.symbol == Any_isInstanceOf) { - fn match { - case TypeApply(sel @ Select(qual, name), List(targ)) => - if (qual.tpe != null && isPrimitiveValueClass(qual.tpe.typeSymbol) && targ.tpe != null && targ.tpe <:< AnyRefClass.tpe) - unit.error(sel.pos, "isInstanceOf cannot test if value types are references.") - - def mkIsInstanceOf(q: () => Tree)(tp: Type): Tree = - Apply( - TypeApply( - Select(q(), Object_isInstanceOf) setPos sel.pos, - List(TypeTree(tp) setPos targ.pos)) setPos fn.pos, - List()) setPos tree.pos - targ.tpe match { - case SingleType(_, _) | ThisType(_) | SuperType(_, _) => - val cmpOp = if (targ.tpe <:< AnyValClass.tpe) Any_equals else Object_eq - atPos(tree.pos) { - Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe))) - } - case RefinedType(parents, decls) if (parents.length >= 2) => - // Optimization: don't generate isInstanceOf tests if the static type - // conforms, because it always succeeds. (Or at least it had better.) - // At this writing the pattern matcher generates some instance tests - // involving intersections where at least one parent is statically known true. - // That needs fixing, but filtering the parents here adds an additional - // level of robustness (in addition to the short term fix.) - val parentTests = parents filterNot (qual.tpe <:< _) - - if (parentTests.isEmpty) Literal(Constant(true)) - else gen.evalOnce(qual, currentOwner, unit) { q => - atPos(tree.pos) { - parentTests map mkIsInstanceOf(q) reduceRight gen.mkAnd - } - } - case _ => - tree + case fn @ Select(qual, name) => + val args = tree.args + if (fn.symbol.owner == ArrayClass) { + // Have to also catch calls to abstract types which are bounded by Array. + if (unboundedGenericArrayLevel(qual.tpe.widen) == 1 || qual.tpe.typeSymbol.isAbstractType) { + // convert calls to apply/update/length on generic arrays to + // calls of ScalaRunTime.array_xxx method calls + global.typer.typedPos(tree.pos) { + val arrayMethodName = name match { + case nme.apply => nme.array_apply + case nme.length => nme.array_length + case nme.update => nme.array_update + case nme.clone_ => nme.array_clone + case _ => unit.error(tree.pos, "Unexpected array member, no translation exists.") ; nme.NO_NAME + } + gen.mkRuntimeCall(arrayMethodName, qual :: args) } - case _ => tree - } - } else if (fn.symbol.owner.isRefinementClass && !fn.symbol.isOverridingSymbol) { - ApplyDynamic(qualifier, args) setSymbol fn.symbol setPos tree.pos - } else if (fn.symbol.isMethodWithExtension) { - Apply(gen.mkAttributedRef(extensionMethods.extensionMethod(fn.symbol)), qualifier :: args) - } else { + } else { + // store exact array erasure in map to be retrieved later when we might + // need to do the cast in adaptMember + treeCopy.Apply( + tree, + SelectFromArray(qual, name, erasure(tree.symbol)(qual.tpe)).copyAttrs(fn), + args) + } + } else if (args.isEmpty && interceptedMethods(fn.symbol)) { + if (fn.symbol == Any_## || fn.symbol == Object_##) { + // This is unattractive, but without it we crash here on ().## because after + // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int. + // This must be because some earlier transformation is being skipped on ##, but so + // far I don't know what. For null we now define null.## == 0. + qual.tpe.typeSymbol match { + case UnitClass | NullClass => LIT(0) + case IntClass => qual + case s @ (ShortClass | ByteClass | CharClass) => numericConversion(qual, s) + case BooleanClass => If(qual, LIT(true.##), LIT(false.##)) + case _ => + global.typer.typed(gen.mkRuntimeCall(nme.hash_, List(qual))) + } + } else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { + // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) + global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen)))) + } else { tree + } + } else qual match { + case New(tpt) if name == nme.CONSTRUCTOR && tpt.tpe.typeSymbol.isDerivedValueClass => + // println("inject derived: "+arg+" "+tpt.tpe) + val List(arg) = args + InjectDerivedValue(arg) addAttachment //@@@ setSymbol tpt.tpe.typeSymbol + new TypeRefAttachment(tree.tpe.asInstanceOf[TypeRef]) + case _ => + preEraseNormalApply(tree) } + case _ => + preEraseNormalApply(tree) + } + } + + def preErase(tree: Tree): Tree = tree match { + case tree: Apply => + preEraseApply(tree) + + case TypeApply(fun, args) if (fun.symbol.owner != AnyClass && + fun.symbol != Object_asInstanceOf && + fun.symbol != Object_isInstanceOf) => + // leave all other type tests/type casts, remove all other type applications + preErase(fun) + case Select(qual, name) => val owner = tree.symbol.owner // println("preXform: "+ (tree, tree.symbol, tree.symbol.owner, tree.symbol.owner.isRefinementClass)) @@ -1120,6 +1146,14 @@ abstract class Erasure extends AddInterfaces } treeCopy.Literal(tree, Constant(erased)) + case ClassDef(_,_,_,_) => + debuglog("defs of " + tree.symbol + " = " + tree.symbol.info.decls) + copyClassDef(tree)(tparams = Nil) + case DefDef(_,_,_,_,_,_) => + copyDefDef(tree)(tparams = Nil) + case TypeDef(_, _, _, _) => + EmptyTree + case _ => tree } diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index e8387c80f5..12e2433e0d 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -111,7 +111,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD var added = false val stats = for (stat <- body1) yield stat match { - case Block(_, _) | Apply(_, _) | If(_, _, _) if !added => + case Block(_, _) | Apply(_, _) | If(_, _, _) | Try(_, _, _) if !added => // Avoid adding bitmaps when they are fully overshadowed by those // that are added inside loops if (LocalLazyValFinder.find(stat)) { diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 3c828db7f3..79b24e826d 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -492,19 +492,19 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * fields count as fields defined by the class itself. */ private val fieldOffset = perRunCaches.newMap[Symbol, Int]() - + private val bitmapKindForCategory = perRunCaches.newMap[Name, ClassSymbol]() - + // ByteClass, IntClass, LongClass private def bitmapKind(field: Symbol): ClassSymbol = bitmapKindForCategory(bitmapCategory(field)) - + private def flagsPerBitmap(field: Symbol): Int = bitmapKind(field) match { case BooleanClass => 1 case ByteClass => 8 case IntClass => 32 case LongClass => 64 } - + /** The first transform; called in a pre-order traversal at phase mixin * (that is, every node is processed before its children). @@ -718,7 +718,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val sym = clazz0.info.decl(bitmapName) assert(!sym.isOverloaded, sym) - + def createBitmap: Symbol = { val bitmapKind = bitmapKindForCategory(category) val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo bitmapKind.tpe @@ -732,7 +732,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case BooleanClass => VAL(sym) === FALSE case _ => VAL(sym) === ZERO } - + sym setFlag PrivateLocal clazz0.info.decls.enter(sym) addDef(clazz0.pos, init) @@ -744,7 +744,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { else createBitmap } - + def maskForOffset(offset: Int, sym: Symbol, kind: ClassSymbol): Tree = { def realOffset = offset % flagsPerBitmap(sym) if (kind == LongClass ) LIT(1L << realOffset) else LIT(1 << realOffset) @@ -755,9 +755,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val bmp = bitmapFor(clazz, offset, valSym) def mask = maskForOffset(offset, valSym, kind) def x = This(clazz) DOT bmp - def newValue = if (kind == BooleanClass) TRUE else (x GEN_| (mask, kind)) + def newValue = if (kind == BooleanClass) TRUE else (x GEN_| (mask, kind)) - x === newValue + x === newValue } /** Return an (untyped) tree of the form 'clazz.this.bitmapSym & mask (==|!=) 0', the @@ -775,7 +775,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { else lhs GEN_!= (ZERO, kind) } } - + def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree, attrThis: Tree, args: List[Tree]): Symbol = { val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name), lzyVal.pos, PRIVATE) @@ -791,14 +791,14 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { stats: List[Tree], retVal: Tree): Tree = { mkFastPathBody(clazz, lzyVal, cond, syncBody, stats, retVal, gen.mkAttributedThis(clazz), List()) } - + def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree, attrThis: Tree, args: List[Tree]): Tree = { val slowPathSym: Symbol = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal, attrThis, args) If(cond, fn (This(clazz), slowPathSym, args.map(arg => Ident(arg.symbol)): _*), retVal) } - - + + /** Always copy the tree if we are going to perform sym substitution, * otherwise we will side-effect on the tree that is used in the fast path */ @@ -807,7 +807,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { if (tree.hasSymbol && from.contains(tree.symbol)) super.transform(tree.duplicate) else super.transform(tree.duplicate) - + override def apply[T <: Tree](tree: T): T = if (from.isEmpty) tree else super.apply(tree) } @@ -827,8 +827,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * The result will be a tree of the form * { if ((bitmap&n & MASK) == 0) this.l$compute() * else l$ - * - * ... + * + * ... * def l$compute() = { synchronized(this) { * if ((bitmap$n & MASK) == 0) { * init // l$ = <rhs> @@ -836,7 +836,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * }} * l$ * } - * + * * ... * this.f1 = null * ... this.fn = null @@ -846,7 +846,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * For Int bitmap it is 32 and then 'n' in the above code is: (offset / 32), * the MASK is (1 << (offset % 32)). * If the class contains only a single lazy val then the bitmap is represented - * as a Boolean and the condition checking is a simple bool test. + * as a Boolean and the condition checking is a simple bool test. */ def mkLazyDef(clazz: Symbol, lzyVal: Symbol, init: List[Tree], retVal: Tree, offset: Int): Tree = { def nullify(sym: Symbol) = Select(This(clazz), sym.accessedOrSelf) === LIT(null) @@ -878,7 +878,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = { val sym = fieldSym.getter(fieldSym.owner) val bitmapSym = bitmapFor(clazz, offset, sym) - val kind = bitmapKind(sym) + val kind = bitmapKind(sym) val mask = maskForOffset(offset, sym, kind) val msg = "Uninitialized field: " + unit.source + ": " + pos.line val result = @@ -966,7 +966,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { stats flatMap { case stat @ Assign(lhs @ Select(This(_), _), rhs) => stat :: checkedGetter(lhs) // remove initialization for default values - case Apply(lhs @ Select(Ident(self), _), List(EmptyTree)) if lhs.symbol.isSetter => Nil + case Apply(lhs @ Select(Ident(self), _), EmptyTree.asList) if lhs.symbol.isSetter => Nil case stat => List(stat) }, exprOwner diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 049157dcf1..fc9e611d20 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -111,7 +111,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case TypeRef(_, GroupOfSpecializable, arg :: Nil) => arg.typeArgs map (_.typeSymbol) case _ => - List(tp.typeSymbol) + tp.typeSymbol :: Nil } } } @@ -362,7 +362,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // creating each permutation of concrete types def loop(ctypes: List[List[Type]]): List[List[Type]] = ctypes match { case Nil => Nil - case set :: Nil => set map (x => List(x)) + case set :: Nil => set map (_ :: Nil) case set :: sets => for (x <- set ; xs <- loop(sets)) yield x :: xs } // zip the keys with each permutation to create a TypeEnv. @@ -424,7 +424,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case MethodType(argSyms, resTpe) => specializedTypeVars(resTpe :: argSyms.map(_.tpe)) case ExistentialType(_, res) => specializedTypeVars(res) case AnnotatedType(_, tp, _) => specializedTypeVars(tp) - case TypeBounds(lo, hi) => specializedTypeVars(List(lo, hi)) + case TypeBounds(lo, hi) => specializedTypeVars(lo :: hi :: Nil) case RefinedType(parents, _) => parents flatMap specializedTypeVars toSet case _ => Set() } @@ -1399,6 +1399,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { curTree = tree tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => + def transformNew = { debuglog("Attempting to specialize new %s(%s)".format(tpt, args.mkString(", "))) val found = findSpec(tpt.tpe) if (found.typeSymbol ne tpt.tpe.typeSymbol) { @@ -1410,9 +1411,26 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { _ => super.transform(tree) } } else super.transform(tree) + } + transformNew + + case Apply(sel @ Select(sup @ Super(qual, name), name1), args) + if (sup.symbol.info.parents != beforePrevPhase(sup.symbol.info.parents)) => + def transformSuperApply = { + + def parents = sup.symbol.info.parents + debuglog(tree + " parents changed from: " + beforePrevPhase(parents) + " to: " + parents) + + val res = localTyper.typed( + Apply(Select(Super(qual, name) setPos sup.pos, name1) setPos sel.pos, transformTrees(args)) setPos tree.pos) + debuglog("retyping call to super, from: " + symbol + " to " + res.symbol) + res + } + transformSuperApply case TypeApply(sel @ Select(qual, name), targs) if (!specializedTypeVars(symbol.info).isEmpty && name != nme.CONSTRUCTOR) => + def transformTypeApply = { debuglog("checking typeapp for rerouting: " + tree + " with sym.tpe: " + symbol.tpe + " tree.tpe: " + tree.tpe) val qual1 = transform(qual) // log(">>> TypeApply: " + tree + ", qual1: " + qual1) @@ -1445,14 +1463,19 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // See pos/exponential-spec.scala - can't call transform on the whole tree again. // super.transform(tree) } - - case Select(Super(_, _), name) if illegalSpecializedInheritance(currentClass) => - val pos = tree.pos - debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.") - debuglog(pos.lineContent) - tree + } + transformTypeApply case Select(qual, name) => + def transformSelect = { + qual match { + case _: Super if illegalSpecializedInheritance(currentClass) => + val pos = tree.pos + debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.") + debuglog(pos.lineContent) + tree + case _ => + debuglog("specializing Select %s [tree.tpe: %s]".format(symbol.defString, tree.tpe)) //log("!!! select " + tree + " -> " + symbol.info + " specTypeVars: " + specializedTypeVars(symbol.info)) @@ -1488,6 +1511,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case None => super.transform(tree) } + } + } + transformSelect case PackageDef(pid, stats) => tree.symbol.info // make sure specializations have been performed @@ -1497,6 +1523,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } case Template(parents, self, body) => + def transformTemplate = { val specMembers = makeSpecializedMembers(tree.symbol.enclClass) ::: (implSpecClasses(body) map localTyper.typed) if (!symbol.isPackageClass) (new CollectMethodBodies)(tree) @@ -1507,8 +1534,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { parents1 /*currentOwner.info.parents.map(tpe => TypeTree(tpe) setPos parents.head.pos)*/ , self, atOwner(currentOwner)(transformTrees(body ::: specMembers))) + } + transformTemplate case ddef @ DefDef(_, _, _, vparamss, _, _) if info.isDefinedAt(symbol) => + def transformDefDef = { // log("--> method: " + ddef + " in " + ddef.symbol.owner + ", " + info(symbol)) def reportTypeError(body: =>Tree) = reportError(body)(_ => ddef) @@ -1597,8 +1627,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { debuglog("abstract: " + targ) localTyper.typed(deriveDefDef(tree)(rhs => rhs)) } + } + transformDefDef case ValDef(_, _, _, _) if symbol.hasFlag(SPECIALIZED) && !symbol.isParamAccessor => + def transformValDef = { assert(body.isDefinedAt(symbol.alias), body) val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate) debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName) @@ -1612,17 +1645,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { typeEnv(symbol.alias) ++ typeEnv(tree.symbol) ) deriveValDef(newValDef)(transform) - - case Apply(sel @ Select(sup @ Super(qual, name), name1), args) - if (sup.symbol.info.parents != beforePrevPhase(sup.symbol.info.parents)) => - - def parents = sup.symbol.info.parents - debuglog(tree + " parents changed from: " + beforePrevPhase(parents) + " to: " + parents) - - val res = localTyper.typed( - Apply(Select(Super(qual, name) setPos sup.pos, name1) setPos sel.pos, transformTrees(args)) setPos tree.pos) - debuglog("retyping call to super, from: " + symbol + " to " + res.symbol) - res + } + transformValDef case _ => super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala index 9e681b321c..82e95523d9 100644 --- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala +++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala @@ -25,19 +25,14 @@ trait TypingTransformers { protected var curTree: Tree = _ protected def typedPos(pos: Position)(tree: Tree) = localTyper typed { atPos(pos)(tree) } - /** a typer for each enclosing class */ - val typers: mutable.Map[Symbol, analyzer.Typer] = new mutable.HashMap - - override def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans) + override final def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans) def atOwner[A](tree: Tree, owner: Symbol)(trans: => A): A = { val savedLocalTyper = localTyper // println("transformer atOwner: " + owner + " isPackage? " + owner.isPackage) localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner) - typers += Pair(owner, localTyper) val result = super.atOwner(owner)(trans) localTyper = savedLocalTyper - typers -= owner result } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index e98bf519fe..181463657b 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -35,8 +35,8 @@ import language.postfixOps * - convert non-local returns to throws with enclosing try statements. * - convert try-catch expressions in contexts where there might be values on the stack to * a local method and a call to it (since an exception empties the evaluation stack): - * - * meth(x_1,..., try { x_i } catch { ..}, .. x_b0) ==> + * + * meth(x_1,..., try { x_i } catch { ..}, .. x_b0) ==> * { * def liftedTry$1 = try { x_i } catch { .. } * meth(x_1, .., liftedTry$1(), .. ) @@ -271,7 +271,7 @@ abstract class UnCurry extends InfoTransform localTyper.typedPos(fun.pos) { Block( - List(ClassDef(anonClass, NoMods, List(List()), List(List()), List(applyMethodDef), fun.pos)), + List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, List(applyMethodDef), fun.pos)), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } @@ -396,7 +396,7 @@ abstract class UnCurry extends InfoTransform localTyper.typedPos(fun.pos) { Block( - List(ClassDef(anonClass, NoMods, List(List()), List(List()), List(applyOrElseMethodDef, isDefinedAtMethodDef), fun.pos)), + List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, List(applyOrElseMethodDef, isDefinedAtMethodDef), fun.pos)), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } } @@ -558,7 +558,7 @@ abstract class UnCurry extends InfoTransform sym.setInfo(MethodType(List(), tree.tpe)) tree.changeOwner(currentOwner -> sym) localTyper.typedPos(tree.pos)(Block( - List(DefDef(sym, List(Nil), tree)), + List(DefDef(sym, ListOfNil, tree)), Apply(Ident(sym), Nil) )) } @@ -641,7 +641,7 @@ abstract class UnCurry extends InfoTransform case ret @ Return(_) if (isNonLocalReturn(ret)) => withNeedLift(true) { super.transform(ret) } - case Try(_, Nil, _) => + case Try(_, Nil, _) => // try-finally does not need lifting: lifting is needed only for try-catch // expressions that are evaluated in a context where the stack might not be empty. // `finally` does not attempt to continue evaluation after an exception, so the fact @@ -693,6 +693,46 @@ abstract class UnCurry extends InfoTransform else tree } + + def isThrowable(pat: Tree): Boolean = pat match { + case Typed(Ident(nme.WILDCARD), tpt) => + tpt.tpe =:= ThrowableClass.tpe + case Bind(_, pat) => + isThrowable(pat) + case _ => + false + } + + def isDefaultCatch(cdef: CaseDef) = isThrowable(cdef.pat) && cdef.guard.isEmpty + + def postTransformTry(tree: Try) = { + val body = tree.block + val catches = tree.catches + val finalizer = tree.finalizer + if (opt.virtPatmat) { + if (catches exists (cd => !treeInfo.isCatchCase(cd))) + debugwarn("VPM BUG! illegal try/catch " + catches) + tree + } else if (catches forall treeInfo.isCatchCase) { + tree + } else { + val exname = unit.freshTermName("ex$") + val cases = + if ((catches exists treeInfo.isDefaultCase) || isDefaultCatch(catches.last)) catches + else catches :+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Throw(Ident(exname))) + val catchall = + atPos(tree.pos) { + CaseDef( + Bind(exname, Ident(nme.WILDCARD)), + EmptyTree, + Match(Ident(exname), cases)) + } + debuglog("rewrote try: " + catches + " ==> " + catchall); + val catches1 = localTyper.typedCases( + List(catchall), ThrowableClass.tpe, WildcardType) + treeCopy.Try(tree, body, catches1, finalizer) + } + } tree match { /* Some uncurry post transformations add members to templates. @@ -711,8 +751,12 @@ abstract class UnCurry extends InfoTransform } case dd @ DefDef(_, _, _, vparamss0, _, rhs0) => + val vparamss1 = vparamss0 match { + case _ :: Nil => vparamss0 + case _ => vparamss0.flatten :: Nil + } val flatdd = copyDefDef(dd)( - vparamss = List(vparamss0.flatten), + vparamss = vparamss1, rhs = nonLocalReturnKeys get dd.symbol match { case Some(k) => atPos(rhs0.pos)(nonLocalReturnTry(rhs0, k, dd.symbol)) case None => rhs0 @@ -720,35 +764,12 @@ abstract class UnCurry extends InfoTransform ) addJavaVarargsForwarders(dd, flatdd) - case Try(body, catches, finalizer) => - if (opt.virtPatmat) { if(catches exists (cd => !treeInfo.isCatchCase(cd))) debugwarn("VPM BUG! illegal try/catch "+ catches); tree } - else if (catches forall treeInfo.isCatchCase) tree - else { - val exname = unit.freshTermName("ex$") - val cases = - if ((catches exists treeInfo.isDefaultCase) || (catches.last match { // bq: handle try { } catch { ... case ex:Throwable => ...} - case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => - true - case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => - true - case _ => - false - })) catches - else catches :+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Throw(Ident(exname))) - val catchall = - atPos(tree.pos) { - CaseDef( - Bind(exname, Ident(nme.WILDCARD)), - EmptyTree, - Match(Ident(exname), cases)) - } - debuglog("rewrote try: " + catches + " ==> " + catchall); - val catches1 = localTyper.typedCases( - List(catchall), ThrowableClass.tpe, WildcardType) - treeCopy.Try(tree, body, catches1, finalizer) - } + case tree: Try => + postTransformTry(tree) + case Apply(Apply(fn, args), args1) => treeCopy.Apply(tree, fn, args ::: args1) + case Ident(name) => assert(name != tpnme.WILDCARD_STAR, tree) applyUnary() diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 7f4f61bf80..ab8836f339 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -85,13 +85,13 @@ trait Analyzer extends AnyRef // compiler run). This is good enough for the resident compiler, which was the most affected. undoLog.clear() override def run() { - val start = Statistics.startTimer(typerNanos) + val start = if (Statistics.canEnable) Statistics.startTimer(typerNanos) else null global.echoPhaseSummary(this) currentRun.units foreach applyPhase undoLog.clear() // need to clear it after as well or 10K+ accumulated entries are // uncollectable the rest of the way. - Statistics.stopTimer(typerNanos, start) + if (Statistics.canEnable) Statistics.stopTimer(typerNanos, start) } def apply(unit: CompilationUnit) { try { diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index c7728ce389..773d9a6f50 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -8,12 +8,19 @@ package typechecker import scala.collection.{ mutable, immutable } import scala.reflect.internal.util.StringOps.{ countElementsAsString, countAsString } -import symtab.Flags.{ PRIVATE, PROTECTED } +import symtab.Flags.{ PRIVATE, PROTECTED, IS_ERROR } +import scala.compat.Platform.EOL +import scala.reflect.runtime.ReflectionUtils +import scala.reflect.macros.runtime.AbortMacroException +import scala.util.control.NonFatal +import scala.tools.nsc.util.stackTraceString trait ContextErrors { self: Analyzer => import global._ + import definitions._ + import treeInfo._ object ErrorKinds extends Enumeration { type ErrorKind = Value @@ -320,16 +327,6 @@ trait ContextErrors { setError(tree) } - def MacroEtaError(tree: Tree) = { - issueNormalTypeError(tree, "macros cannot be eta-expanded") - setError(tree) - } - - def MacroPartialApplicationError(tree: Tree) = { - issueNormalTypeError(tree, "macros cannot be partially applied") - setError(tree) - } - //typedReturn def ReturnOutsideOfDefError(tree: Tree) = { issueNormalTypeError(tree, "return outside method definition") @@ -427,8 +424,11 @@ trait ContextErrors { def AbstractionFromVolatileTypeError(vd: ValDef) = issueNormalTypeError(vd, "illegal abstraction from value with volatile type "+vd.symbol.tpe) + private[ContextErrors] def TypedApplyWrongNumberOfTpeParametersErrorMessage(fun: Tree) = + "wrong number of type parameters for "+treeSymTypeMsg(fun) + def TypedApplyWrongNumberOfTpeParametersError(tree: Tree, fun: Tree) = { - issueNormalTypeError(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun)) + issueNormalTypeError(tree, TypedApplyWrongNumberOfTpeParametersErrorMessage(fun)) setError(tree) } @@ -625,11 +625,111 @@ trait ContextErrors { } // cyclic errors - def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) = - issueTypeError(PosAndMsgTypeError(errPos, "cyclic aliasing or subtyping involving "+sym0)) + def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) = + issueTypeError(PosAndMsgTypeError(errPos, "cyclic aliasing or subtyping involving "+sym0)) + + def CyclicReferenceError(errPos: Position, lockedSym: Symbol) = + issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym)) + + // macro-related errors (also see MacroErrors below) + + def MacroEtaError(tree: Tree) = { + issueNormalTypeError(tree, "macros cannot be eta-expanded") + setError(tree) + } + + // same reason as for MacroBodyTypecheckException + case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable + + private def macroExpansionError(expandee: Tree, msg: String = null, pos: Position = NoPosition) = { + def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg + macroLogLite("macro expansion has failed: %s".format(msgForLog)) + val errorPos = if (pos != NoPosition) pos else (if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition) + if (msg != null) context.error(pos, msg) // issueTypeError(PosAndMsgTypeError(..)) won't work => swallows positions + setError(expandee) + throw MacroExpansionException + } + + def MacroPartialApplicationError(expandee: Tree) = { + // macroExpansionError won't work => swallows positions, hence needed to do issueTypeError + // kinda contradictory to the comment in `macroExpansionError`, but this is how it works + issueNormalTypeError(expandee, "macros cannot be partially applied") + setError(expandee) + throw MacroExpansionException + } + + def MacroGeneratedAbort(expandee: Tree, ex: AbortMacroException) = { + // errors have been reported by the macro itself, so we do nothing here + macroLogVerbose("macro expansion has been aborted") + macroExpansionError(expandee, ex.msg, ex.pos) + } - def CyclicReferenceError(errPos: Position, lockedSym: Symbol) = - issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym)) + def MacroGeneratedTypeError(expandee: Tree, err: TypeError = null) = + if (err == null) { + // errors have been reported by the macro itself, so we do nothing here + macroExpansionError(expandee, null) + } else { + macroLogLite("macro expansion has failed: %s at %s".format(err.msg, err.pos)) + throw err // this error must be propagated, don't report + } + + def MacroGeneratedException(expandee: Tree, ex: Throwable) = { + val realex = ReflectionUtils.unwrapThrowable(ex) + val message = { + try { + // [Eugene] is there a better way? + // [Paul] See Exceptional.scala and Origins.scala. + val relevancyThreshold = realex.getStackTrace().indexWhere(_.getMethodName endsWith "macroExpand1") + if (relevancyThreshold == -1) None + else { + var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1) + def isMacroInvoker(este: StackTraceElement) = este.isNativeMethod || (este.getClassName != null && (este.getClassName contains "fastTrack")) + var threshold = relevantElements.reverse.indexWhere(isMacroInvoker) + 1 + while (threshold != relevantElements.length && isMacroInvoker(relevantElements(relevantElements.length - threshold - 1))) threshold += 1 + relevantElements = relevantElements dropRight threshold + + realex.setStackTrace(relevantElements) + Some(EOL + stackTraceString(realex)) + } + } catch { + // if the magic above goes boom, just fall back to uninformative, but better than nothing, getMessage + case NonFatal(ex) => + macroLogVerbose("got an exception when processing a macro generated exception\n" + + "offender = " + stackTraceString(realex) + "\n" + + "error = " + stackTraceString(ex)) + None + } + } getOrElse { + val msg = realex.getMessage + if (msg != null) msg else realex.getClass.getName + } + macroExpansionError(expandee, "exception during macro expansion: " + message) + } + + def MacroFreeSymbolError(expandee: Tree, sym: FreeSymbol) = { + def template(kind: String) = ( + s"Macro expansion contains free $kind variable %s. Have you forgotten to use %s? " + + s"If you have troubles tracking free $kind variables, consider using -Xlog-free-${kind}s" + ) + val forgotten = ( + if (sym.isTerm) "splice when splicing this variable into a reifee" + else "c.AbsTypeTag annotation for this type parameter" + ) + macroExpansionError(expandee, template(sym.name.nameKind).format(sym.name + " " + sym.origin, forgotten)) + } + + def MacroExpansionIsNotExprError(expandee: Tree, expanded: Any) = + macroExpansionError(expandee, + "macro must return a compiler-specific expr; returned value is " + ( + if (expanded == null) "null" + else if (expanded.isInstanceOf[Expr[_]]) " Expr, but it doesn't belong to this compiler's universe" + else " of " + expanded.getClass + )) + + def MacroImplementationNotFoundError(expandee: Tree) = + macroExpansionError(expandee, + "macro implementation not found: " + expandee.symbol.name + " " + + "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)") } } @@ -706,7 +806,7 @@ trait ContextErrors { // side-effect on the tree, break the overloaded type cycle in infer @inline private def setErrorOnLastTry(lastTry: Boolean, tree: Tree) = if (lastTry) setError(tree) - + def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type, lastTry: Boolean) = { issueNormalTypeError(tree, applyErrorMsg(tree, " cannot be applied to ", argtpes, pt)) @@ -719,7 +819,7 @@ trait ContextErrors { def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, argtpes: List[Type], pt: Type, lastTry: Boolean) = { - + if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) { val msg0 = "argument types " + argtpes.mkString("(", ",", ")") + @@ -729,7 +829,7 @@ trait ContextErrors { setErrorOnLastTry(lastTry, tree) } else setError(tree) // do not even try further attempts because they should all fail // even if this is not the last attempt (because of the SO's possibility on the horizon) - + } def NoBestExprAlternativeError(tree: Tree, pt: Type, lastTry: Boolean) = { @@ -753,21 +853,24 @@ trait ContextErrors { kindErrors.toList.mkString("\n", ", ", "")) } - def NotWithinBounds(tree: Tree, prefix: String, targs: List[Type], - tparams: List[Symbol], kindErrors: List[String]) = { - if (settings.explaintypes.value) { + private[ContextErrors] def NotWithinBoundsErrorMessage(prefix: String, targs: List[Type], tparams: List[Symbol], explaintypes: Boolean) = { + if (explaintypes) { val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds) (targs, bounds).zipped foreach ((targ, bound) => explainTypes(bound.lo, targ)) (targs, bounds).zipped foreach ((targ, bound) => explainTypes(targ, bound.hi)) () } - issueNormalTypeError(tree, - prefix + "type arguments " + targs.mkString("[", ",", "]") + - " do not conform to " + tparams.head.owner + "'s type parameter bounds " + - (tparams map (_.defString)).mkString("[", ",", "]")) + prefix + "type arguments " + targs.mkString("[", ",", "]") + + " do not conform to " + tparams.head.owner + "'s type parameter bounds " + + (tparams map (_.defString)).mkString("[", ",", "]") } + def NotWithinBounds(tree: Tree, prefix: String, targs: List[Type], + tparams: List[Symbol], kindErrors: List[String]) = + issueNormalTypeError(tree, + NotWithinBoundsErrorMessage(prefix, targs, tparams, settings.explaintypes.value)) + //substExpr def PolymorphicExpressionInstantiationError(tree: Tree, undetparams: List[Symbol], pt: Type) = issueNormalTypeError(tree, @@ -1061,7 +1164,7 @@ trait ContextErrors { setError(arg) } else arg } - + def WarnAfterNonSilentRecursiveInference(param: Symbol, arg: Tree)(implicit context: Context) = { val note = "type-checking the invocation of "+ param.owner +" checks if the named argument expression '"+ param.name + " = ...' is a valid assignment\n"+ "in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for "+ param.name +"." @@ -1087,4 +1190,133 @@ trait ContextErrors { setError(arg) } } + + // using an exception here is actually a good idea + // because the lifespan of this exception is extremely small and controlled + // moreover exceptions let us avoid an avalanche of "if (!hasError) do stuff" checks + case object MacroBodyTypecheckException extends Exception with scala.util.control.ControlThrowable + + trait MacroErrors { + self: MacroTyper => + + private implicit val context0 = typer.context + val context = typer.context + + // helpers + + private def lengthMsg(flavor: String, violation: String, extra: Symbol) = { + val noun = if (flavor == "value") "parameter" else "type parameter" + val message = noun + " lists have different length, " + violation + " extra " + noun + val suffix = if (extra ne NoSymbol) " " + extra.defString else "" + message + suffix + } + + private def abbreviateCoreAliases(s: String): String = List("AbsTypeTag", "Expr").foldLeft(s)((res, x) => res.replace("c.universe." + x, "c." + x)) + + private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean) = { + var argsPart = (pss map (ps => ps map (_.defString) mkString ("(", ", ", ")"))).mkString + if (abbreviate) argsPart = abbreviateCoreAliases(argsPart) + var retPart = restpe.toString + if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart) + argsPart + ": " + retPart + } + + // not exactly an error generator, but very related + // and I dearly wanted to push it away from Macros.scala + private def checkSubType(slot: String, rtpe: Type, atpe: Type) = { + val ok = if (macroDebugVerbose || settings.explaintypes.value) { + if (rtpe eq atpe) println(rtpe + " <: " + atpe + "?" + EOL + "true") + withTypesExplained(rtpe <:< atpe) + } else rtpe <:< atpe + if (!ok) { + compatibilityError("type mismatch for %s: %s does not conform to %s".format(slot, abbreviateCoreAliases(rtpe.toString), abbreviateCoreAliases(atpe.toString))) + } + } + + // errors + + private def fail() = { + // need to set the IS_ERROR flag to prohibit spurious expansions + if (macroDef != null) macroDef setFlag IS_ERROR + // not setting ErrorSymbol as in `infer.setError`, because we still need to know that it's a macro + // otherwise assignTypeToTree in Namers might fail if macroDdef.tpt == EmptyTree + macroDdef setType ErrorType + throw MacroBodyTypecheckException + } + + private def genericError(tree: Tree, message: String) = { + issueNormalTypeError(tree, message) + fail() + } + + private def implRefError(message: String) = genericError(methPart(macroDdef.rhs), message) + + private def compatibilityError(message: String) = + implRefError( + "macro implementation has wrong shape:"+ + "\n required: " + showMeth(rparamss, rret, abbreviate = true) + + "\n found : " + showMeth(aparamss, aret, abbreviate = false) + + "\n" + message) + + // Phase I: sanity checks + + def MacroDefIsFastTrack() = { + macroLogVerbose("typecheck terminated unexpectedly: macro is fast track") + assert(!macroDdef.tpt.isEmpty, "fast track macros must provide result type") + throw MacroBodyTypecheckException // don't call fail, because we don't need IS_ERROR + } + + def MacroFeatureNotEnabled() = { + macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled") + fail() + } + + // Phase II: typecheck the right-hand side of the macro def + + // do nothing, just fail. relevant typecheck errors have already been reported + def MacroDefUntypeableBodyError() = fail() + + def MacroDefInvalidBodyError() = genericError(macroDdef, "macro body has wrong shape:\n required: macro [<implementation object>].<method name>[[<type args>]]") + + def MacroImplNotPublicError() = implRefError("macro implementation must be public") + + def MacroImplOverloadedError() = implRefError("macro implementation cannot be overloaded") + + def MacroImplWrongNumberOfTypeArgumentsError(macroImplRef: Tree) = implRefError(typer.TyperErrorGen.TypedApplyWrongNumberOfTpeParametersErrorMessage(macroImplRef)) + + def MacroImplNotStaticError() = implRefError("macro implementation must be in statically accessible object") + + // Phase III: check compatibility between the macro def and its macro impl + // aXXX (e.g. aparams) => characteristics of the macro impl ("a" stands for "actual") + // rXXX (e.g. rparams) => characteristics of a reference macro impl signature synthesized from the macro def ("r" stands for "reference") + + def MacroImplNonTagImplicitParameters(params: List[Symbol]) = compatibilityError("macro implementations cannot have implicit parameters other than AbsTypeTag evidences") + + def MacroImplParamssMismatchError() = compatibilityError("number of parameter sections differ") + + def MacroImplExtraParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(lengthMsg("value", "found", aparams(rparams.length))) + + def MacroImplMissingParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(abbreviateCoreAliases(lengthMsg("value", "required", rparams(aparams.length)))) + + def checkMacroImplParamTypeMismatch(atpe: Type, rparam: Symbol) = checkSubType("parameter " + rparam.name, rparam.tpe, atpe) + + def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkSubType("return type", atpe, rret) + + def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name) + + def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = { + if (isRepeated(rparam) && !isRepeated(aparam)) + compatibilityError("types incompatible for parameter " + rparam.name + ": corresponding is not a vararg parameter") + if (!isRepeated(rparam) && isRepeated(aparam)) + compatibilityError("types incompatible for parameter " + aparam.name + ": corresponding is not a vararg parameter") + } + + def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) = + compatibilityError(typer.infer.InferErrorGen.NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value)) + + def MacroImplTparamInstantiationError(atparams: List[Symbol], ex: NoInstance) = + compatibilityError( + "type parameters "+(atparams map (_.defString) mkString ", ")+" cannot be instantiated\n"+ + ex.getMessage) + } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index dd5588e9a6..6a908c6c65 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -27,6 +27,13 @@ trait Contexts { self: Analyzer => override def implicitss: List[List[ImplicitInfo]] = Nil override def toString = "NoContext" } + private object RootImports { + import definitions._ + // Possible lists of root imports + val javaList = JavaLangPackage :: Nil + val javaAndScalaList = JavaLangPackage :: ScalaPackage :: Nil + val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil + } private val startContext = { NoContext.make( @@ -46,13 +53,12 @@ trait Contexts { self: Analyzer => * among its leading imports, or if the tree is [[scala.Predef]], `Predef` is not imported. */ protected def rootImports(unit: CompilationUnit): List[Symbol] = { - import definitions._ - assert(isDefinitionsInitialized, "definitions uninitialized") + assert(definitions.isDefinitionsInitialized, "definitions uninitialized") if (settings.noimports.value) Nil - else if (unit.isJava) List(JavaLangPackage) - else if (settings.nopredef.value || treeInfo.noPredefImportForUnit(unit.body)) List(JavaLangPackage, ScalaPackage) - else List(JavaLangPackage, ScalaPackage, PredefModule) + else if (unit.isJava) RootImports.javaList + else if (settings.nopredef.value || treeInfo.noPredefImportForUnit(unit.body)) RootImports.javaAndScalaList + else RootImports.completeList } def rootContext(unit: CompilationUnit): Context = rootContext(unit, EmptyTree, false) diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index e590e7aa30..070f083a89 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -283,14 +283,15 @@ abstract class Duplicators extends Analyzer { // the typer does not create the symbols for a LabelDef's params, so unless they were created before we need // to do it manually here -- but for the tailcalls-generated labels, ValDefs are created before the LabelDef, - // so we just need to plug in the name + // so we just need to change the tree to point to the updated symbols def newParam(p: Tree): Ident = if (isTailLabel) - Ident(p.symbol.name) // let the typer pick up the right symbol + Ident(updateSym(p.symbol)) else { 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 diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index a34fc71b8f..226e17f605 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -72,10 +72,10 @@ trait Implicits { ) indentTyping() - val rawTypeStart = Statistics.startCounter(rawTypeImpl) - val findMemberStart = Statistics.startCounter(findMemberImpl) - val subtypeStart = Statistics.startCounter(subtypeImpl) - val start = Statistics.startTimer(implicitNanos) + val rawTypeStart = if (Statistics.canEnable) Statistics.startCounter(rawTypeImpl) else null + val findMemberStart = if (Statistics.canEnable) Statistics.startCounter(findMemberImpl) else null + val subtypeStart = if (Statistics.canEnable) Statistics.startCounter(subtypeImpl) else null + val start = if (Statistics.canEnable) Statistics.startTimer(implicitNanos) else null if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty) printTyping("typing implicit: %s %s".format(tree, context.undetparamsString)) val implicitSearchContext = context.makeImplicit(reportAmbiguous) @@ -87,10 +87,10 @@ trait Implicits { printInference("[infer implicit] inferred " + result) context.undetparams = context.undetparams filterNot result.subst.from.contains - Statistics.stopTimer(implicitNanos, start) - Statistics.stopCounter(rawTypeImpl, rawTypeStart) - Statistics.stopCounter(findMemberImpl, findMemberStart) - Statistics.stopCounter(subtypeImpl, subtypeStart) + if (Statistics.canEnable) Statistics.stopTimer(implicitNanos, start) + if (Statistics.canEnable) Statistics.stopCounter(rawTypeImpl, rawTypeStart) + if (Statistics.canEnable) Statistics.stopCounter(findMemberImpl, findMemberStart) + if (Statistics.canEnable) Statistics.stopCounter(subtypeImpl, subtypeStart) deindentTyping() printTyping("Implicit search yielded: "+ result) result @@ -181,8 +181,8 @@ trait Implicits { containsError(restpe) case NullaryMethodType(restpe) => containsError(restpe) - case MethodType(params, restpe) => - params.exists(_.tpe.isError) || containsError(restpe) + case mt @ MethodType(_, restpe) => + (mt.paramTypes exists typeIsError) || containsError(restpe) case _ => tp.isError } @@ -308,12 +308,12 @@ trait Implicits { /** Is implicit info `info1` better than implicit info `info2`? */ def improves(info1: ImplicitInfo, info2: ImplicitInfo) = { - Statistics.incCounter(improvesCount) + if (Statistics.canEnable) Statistics.incCounter(improvesCount) (info2 == NoImplicitInfo) || (info1 != NoImplicitInfo) && { if (info1.sym.isStatic && info2.sym.isStatic) { improvesCache get (info1, info2) match { - case Some(b) => Statistics.incCounter(improvesCachedCount); b + case Some(b) => if (Statistics.canEnable) Statistics.incCounter(improvesCachedCount); b case None => val result = isStrictlyMoreSpecific(info1.tpe, info2.tpe, info1.sym, info2.sym) improvesCache((info1, info2)) = result @@ -377,7 +377,7 @@ trait Implicits { overlaps(dtor1, dted1) && (dtor1 =:= dted1 || complexity(dtor1) > complexity(dted1)) } - Statistics.incCounter(implicitSearchCount) + if (Statistics.canEnable) Statistics.incCounter(implicitSearchCount) /** The type parameters to instantiate */ val undetParams = if (isView) List() else context.outer.undetparams @@ -429,7 +429,7 @@ trait Implicits { * This method is performance critical: 5-8% of typechecking time. */ private def matchesPt(tp: Type, pt: Type, undet: List[Symbol]): Boolean = { - val start = Statistics.startTimer(matchesPtNanos) + val start = if (Statistics.canEnable) Statistics.startTimer(matchesPtNanos) else null val result = normSubType(tp, pt) || isView && { pt match { case TypeRef(_, Function1.Sym, args) => @@ -438,7 +438,7 @@ trait Implicits { false } } - Statistics.stopTimer(matchesPtNanos, start) + if (Statistics.canEnable) Statistics.stopTimer(matchesPtNanos, start) result } private def matchesPt(info: ImplicitInfo): Boolean = ( @@ -537,7 +537,7 @@ trait Implicits { } private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean, isLocal: Boolean): SearchResult = { - Statistics.incCounter(plausiblyCompatibleImplicits) + if (Statistics.canEnable) Statistics.incCounter(plausiblyCompatibleImplicits) printTyping ( ptBlock("typedImplicit0", "info.name" -> info.name, @@ -557,7 +557,7 @@ trait Implicits { } private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = { - Statistics.incCounter(matchingImplicits) + if (Statistics.canEnable) Statistics.incCounter(matchingImplicits) val itree = atPos(pos.focus) { // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints @@ -595,7 +595,7 @@ trait Implicits { if (context.hasErrors) return fail("typed implicit %s has errors".format(info.sym.fullLocationString)) - Statistics.incCounter(typedImplicits) + if (Statistics.canEnable) Statistics.incCounter(typedImplicits) printTyping("typed implicit %s:%s, pt=%s".format(itree1, itree1.tpe, wildPt)) val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun } @@ -678,7 +678,7 @@ trait Implicits { fail("typing TypeApply reported errors for the implicit tree") else { val result = new SearchResult(itree2, subst) - Statistics.incCounter(foundImplicits) + if (Statistics.canEnable) Statistics.incCounter(foundImplicits) printInference("[success] found %s for pt %s".format(result, ptInstantiated)) result } @@ -905,11 +905,11 @@ trait Implicits { * @return map from infos to search results */ def applicableInfos(iss: Infoss, isLocal: Boolean): Map[ImplicitInfo, SearchResult] = { - val start = Statistics.startCounter(subtypeAppInfos) + val start = if (Statistics.canEnable) Statistics.startCounter(subtypeAppInfos) else null val computation = new ImplicitComputation(iss, isLocal) { } val applicable = computation.findAll() - Statistics.stopCounter(subtypeAppInfos, start) + if (Statistics.canEnable) Statistics.stopCounter(subtypeAppInfos, start) applicable } @@ -1125,13 +1125,13 @@ trait Implicits { * such that some part of `tp` has C as one of its superclasses. */ private def implicitsOfExpectedType: Infoss = { - Statistics.incCounter(implicitCacheAccs) + if (Statistics.canEnable) Statistics.incCounter(implicitCacheAccs) implicitsCache get pt match { case Some(implicitInfoss) => - Statistics.incCounter(implicitCacheHits) + if (Statistics.canEnable) Statistics.incCounter(implicitCacheHits) implicitInfoss case None => - val start = Statistics.startTimer(subtypeETNanos) + val start = if (Statistics.canEnable) Statistics.startTimer(subtypeETNanos) else null // val implicitInfoss = companionImplicits(pt) val implicitInfoss1 = companionImplicitMap(pt).valuesIterator.toList // val is1 = implicitInfoss.flatten.toSet @@ -1140,7 +1140,7 @@ trait Implicits { // if (!(is2 contains i)) println("!!! implicit infos of "+pt+" differ, new does not contain "+i+",\nold: "+implicitInfoss+",\nnew: "+implicitInfoss1) // for (i <- is2) // if (!(is1 contains i)) println("!!! implicit infos of "+pt+" differ, old does not contain "+i+",\nold: "+implicitInfoss+",\nnew: "+implicitInfoss1) - Statistics.stopTimer(subtypeETNanos, start) + if (Statistics.canEnable) Statistics.stopTimer(subtypeETNanos, start) implicitsCache(pt) = implicitInfoss1 if (implicitsCache.size >= sizeLimit) implicitsCache -= implicitsCache.keysIterator.next @@ -1372,21 +1372,21 @@ trait Implicits { * If all fails return SearchFailure */ def bestImplicit: SearchResult = { - val failstart = Statistics.startTimer(inscopeFailNanos) - val succstart = Statistics.startTimer(inscopeSucceedNanos) + val failstart = if (Statistics.canEnable) Statistics.startTimer(inscopeFailNanos) else null + val succstart = if (Statistics.canEnable) Statistics.startTimer(inscopeSucceedNanos) else null var result = searchImplicit(context.implicitss, true) if (result == SearchFailure) { - Statistics.stopTimer(inscopeFailNanos, failstart) + if (Statistics.canEnable) Statistics.stopTimer(inscopeFailNanos, failstart) } else { - Statistics.stopTimer(inscopeSucceedNanos, succstart) - Statistics.incCounter(inscopeImplicitHits) + if (Statistics.canEnable) Statistics.stopTimer(inscopeSucceedNanos, succstart) + if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits) } if (result == SearchFailure) { val previousErrs = context.flushAndReturnBuffer() - val failstart = Statistics.startTimer(oftypeFailNanos) - val succstart = Statistics.startTimer(oftypeSucceedNanos) + val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null + val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null result = materializeImplicit(pt) @@ -1396,10 +1396,10 @@ trait Implicits { if (result == SearchFailure) { context.updateBuffer(previousErrs) - Statistics.stopTimer(oftypeFailNanos, failstart) + if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) } else { - Statistics.stopTimer(oftypeSucceedNanos, succstart) - Statistics.incCounter(oftypeImplicitHits) + if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) + if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index d724164715..28636fc76e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -240,8 +240,8 @@ trait Infer { def normalize(tp: Type): Type = tp match { case mt @ MethodType(params, restpe) if mt.isImplicit => normalize(restpe) - case mt @ MethodType(params, restpe) if !restpe.isDependent => - functionType(params map (_.tpe), normalize(restpe)) + case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => + functionType(mt.paramTypes, normalize(restpe)) case NullaryMethodType(restpe) => normalize(restpe) case ExistentialType(tparams, qtpe) => @@ -661,7 +661,13 @@ trait Infer { val restp1 = followApply(restp) if (restp1 eq restp) tp else restp1 case _ => - val appmeth = tp.nonPrivateMember(nme.apply) filter (_.isPublic) + val appmeth = { + //OPT cut down on #closures by special casing non-overloaded case + // was: tp.nonPrivateMember(nme.apply) filter (_.isPublic) + val result = tp.nonPrivateMember(nme.apply) + if ((result eq NoSymbol) || !result.isOverloaded && result.isPublic) result + else result filter (_.isPublic) + } if (appmeth == NoSymbol) tp else OverloadedType(tp, appmeth.alternatives) } @@ -747,8 +753,8 @@ trait Infer { alts exists (alt => isApplicable(undetparams, pre.memberType(alt), argtpes0, pt)) case ExistentialType(tparams, qtpe) => isApplicable(undetparams, qtpe, argtpes0, pt) - case MethodType(params, _) => - val formals = formalTypes(params map { _.tpe }, argtpes0.length, removeByName = false) + case mt @ MethodType(params, _) => + val formals = formalTypes(mt.paramTypes, argtpes0.length, removeByName = false) def tryTupleApply: Boolean = { // if 1 formal, 1 argtpe (a tuple), otherwise unmodified argtpes0 @@ -854,8 +860,8 @@ trait Infer { isAsSpecific(res, ftpe2) case mt: MethodType if mt.isImplicit => isAsSpecific(ftpe1.resultType, ftpe2) - case MethodType(params, _) if params.nonEmpty => - var argtpes = params map (_.tpe) + case mt @ MethodType(params, _) if params.nonEmpty => + var argtpes = mt.paramTypes if (isVarArgsList(params) && isVarArgsList(ftpe2.params)) argtpes = argtpes map (argtpe => if (isRepeatedParamType(argtpe)) argtpe.typeArgs.head else argtpe) @@ -864,8 +870,8 @@ trait Infer { isAsSpecific(PolyType(tparams, res), ftpe2) case PolyType(tparams, mt: MethodType) if mt.isImplicit => isAsSpecific(PolyType(tparams, mt.resultType), ftpe2) - case PolyType(_, MethodType(params, _)) if params.nonEmpty => - isApplicable(List(), ftpe2, params map (_.tpe), WildcardType) + case PolyType(_, (mt @ MethodType(params, _))) if params.nonEmpty => + isApplicable(List(), ftpe2, mt.paramTypes, WildcardType) // case NullaryMethodType(res) => // isAsSpecific(res, ftpe2) case ErrorType => @@ -1111,10 +1117,10 @@ trait Infer { */ def inferMethodInstance(fn: Tree, undetparams: List[Symbol], args: List[Tree], pt0: Type): List[Symbol] = fn.tpe match { - case MethodType(params0, _) => + case mt @ MethodType(params0, _) => try { val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 - val formals = formalTypes(params0 map (_.tpe), args.length) + val formals = formalTypes(mt.paramTypes, args.length) val argtpes = actualTypes(args map (x => elimAnonymousClass(x.tpe.deconst)), formals.length) val restpe = fn.tpe.resultType(argtpes) @@ -1367,14 +1373,17 @@ trait Infer { else =:= ) (arg hasAnnotation UncheckedClass) || { - val TypeRef(_, sym, args) = arg.withoutAnnotations - - ( isLocalBinding(sym) - || arg.typeSymbol.isTypeParameterOrSkolem - || (sym.name == tpnme.WILDCARD) // avoid spurious warnings on HK types - || check(arg, param.tpe, conforms) - || warn("non-variable type argument " + arg) - ) + arg.withoutAnnotations match { + case TypeRef(_, sym, args) => + ( isLocalBinding(sym) + || arg.typeSymbol.isTypeParameterOrSkolem + || (sym.name == tpnme.WILDCARD) // avoid spurious warnings on HK types + || check(arg, param.tpeHK, conforms) + || warn("non-variable type argument " + arg) + ) + case _ => + warn("non-variable type argument " + arg) + } } } @@ -1641,7 +1650,7 @@ trait Infer { // for functional values, the `apply` method might be overloaded val mtypes = followApply(alt.tpe) match { case OverloadedType(_, alts) => alts map (_.tpe) - case t => List(t) + case t => t :: Nil } // Drop those that use a default; keep those that use vararg/tupling conversion. mtypes exists (t => diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 4765bbabc9..01e773e528 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -12,6 +12,8 @@ import scala.reflect.macros.util._ import java.lang.{Class => jClass} import java.lang.reflect.{Array => jArray, Method => jMethod} import scala.reflect.internal.util.Collections._ +import scala.util.control.ControlThrowable +import scala.reflect.macros.runtime.AbortMacroException /** * Code to deal with macros, namely with: @@ -24,7 +26,7 @@ import scala.reflect.internal.util.Collections._ * * Then fooBar needs to point to a static method of the following form: * - * def fooBar[T: c.AbsTypeTag] + * def fooBar[T: c.AbsTypeTag] // type tag annotation is optional * (c: scala.reflect.macros.Context) * (xs: c.Expr[List[T]]) * : c.Expr[T] = { @@ -32,7 +34,7 @@ import scala.reflect.internal.util.Collections._ * } * * Then, if foo is called in qual.foo[Int](elems), where qual: D, - * the macro application is expanded to a reflective invocation of fooBar with parameters + * the macro application is expanded to a reflective invocation of fooBar with parameters: * * (simpleMacroContext{ type PrefixType = D; val prefix = qual }) * (Expr(elems)) @@ -43,6 +45,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { import global._ import definitions._ + import treeInfo.{isRepeatedParamType => _, _} import MacrosStats._ def globalSettings = global.settings @@ -212,11 +215,86 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { MacroImplBinding.unpickle(pickle) } + /** Transforms parameters lists of a macro impl. + * The `transform` function is invoked only for AbsTypeTag evidence parameters. + * + * The transformer takes two arguments: a value parameter from the parameter list + * and a type parameter that is witnesses by the value parameter. + * + * If the transformer returns a NoSymbol, the value parameter is not included from the result. + * If the transformer returns something else, this something else is included in the result instead of the value parameter. + * + * Despite of being highly esoteric, this function significantly simplifies signature analysis. + * For example, it can be used to strip macroImpl.paramss from the evidences (necessary when checking def <-> impl correspondence) + * or to streamline creation of the list of macro arguments. + */ + private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Symbol): List[List[Symbol]] = { + if (paramss.isEmpty || paramss.last.isEmpty) return paramss // no implicit parameters in the signature => nothing to do + if (paramss.head.isEmpty || !(paramss.head.head.tpe <:< MacroContextClass.tpe)) return paramss // no context parameter in the signature => nothing to do + def transformTag(param: Symbol): Symbol = param.tpe.dealias match { + case TypeRef(SingleType(SingleType(NoPrefix, c), universe), AbsTypeTagClass, targ :: Nil) + if c == paramss.head.head && universe == MacroContextUniverse => + transform(param, targ.typeSymbol) + case _ => + param + } + val transformed = paramss.last map transformTag filter (_ ne NoSymbol) + if (transformed.isEmpty) paramss.init else paramss.init :+ transformed + } + + def computeMacroDefTypeFromMacroImpl(macroDdef: DefDef, macroImpl: Symbol): Type = { + // Step I. Transform c.Expr[T] to T + var runtimeType = macroImpl.tpe.finalResultType.dealias match { + case TypeRef(_, ExprClass, runtimeType :: Nil) => runtimeType + case _ => AnyTpe // so that macro impls with rhs = ??? don't screw up our inference + } + + // Step II. Transform type parameters of a macro implementation into type arguments in a macro definition's body + runtimeType = runtimeType.substituteTypes(macroImpl.typeParams, loadMacroImplBinding(macroDdef.symbol).targs.map(_.tpe)) + + // Step III. Transform c.prefix.value.XXX to this.XXX and implParam.value.YYY to defParam.YYY + def unsigma(tpe: Type): Type = + transformTypeTagEvidenceParams(macroImpl.paramss, (param, tparam) => NoSymbol) match { + case (implCtxParam :: Nil) :: implParamss => + val implToDef = flatMap2(implParamss, macroDdef.vparamss)(map2(_, _)((_, _))).toMap + object UnsigmaTypeMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = pre match { + case SingleType(SingleType(SingleType(NoPrefix, c), prefix), value) if c == implCtxParam && prefix == MacroContextPrefix && value == ExprValue => + ThisType(macroDdef.symbol.owner) + case SingleType(SingleType(NoPrefix, implParam), value) if value == ExprValue => + implToDef get implParam map (defParam => SingleType(NoPrefix, defParam.symbol)) getOrElse pre + case _ => + pre + } + val args1 = args map mapOver + TypeRef(pre1, sym, args1) + case _ => + mapOver(tp) + } + } + + UnsigmaTypeMap(tpe) + case _ => + tpe + } + + unsigma(runtimeType) + } + /** A reference macro implementation signature compatible with a given macro definition. * - * In the example above: + * In the example above for the following macro def: + * def foo[T](xs: List[T]): T = macro fooBar + * + * This function will return: * (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]]): c.Expr[T] * + * Note that type tag evidence parameters are not included into the result. + * Type tag context bounds for macro impl tparams are optional. + * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. + * * @param macroDef The macro definition symbol * @param tparams The type parameters of the macro definition * @param vparamss The value parameters of the macro definition @@ -225,33 +303,22 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { private def macroImplSig(macroDef: Symbol, tparams: List[TypeDef], vparamss: List[List[ValDef]], retTpe: Type): (List[List[Symbol]], Type) = { // had to move method's body to an object because of the recursive dependencies between sigma and param object SigGenerator { - val hasThis = macroDef.owner.isClass - val ownerTpe = macroDef.owner match { - case owner if owner.isModuleClass => new UniqueThisType(macroDef.owner) - case owner if owner.isClass => macroDef.owner.tpe - case _ => NoType - } - val hasTparams = !tparams.isEmpty - def sigma(tpe: Type): Type = { class SigmaTypeMap extends TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, args) => val pre1 = pre match { case ThisType(sym) if sym == macroDef.owner => - SingleType(SingleType(SingleType(NoPrefix, paramsCtx(0)), MacroContextPrefix), ExprValue) + SingleType(SingleType(SingleType(NoPrefix, ctxParam), MacroContextPrefix), ExprValue) case SingleType(NoPrefix, sym) => mfind(vparamss)(_.symbol == sym) match { - case Some(macroDefParam) => - SingleType(SingleType(NoPrefix, param(macroDefParam)), ExprValue) - case _ => - pre + case Some(macroDefParam) => SingleType(SingleType(NoPrefix, param(macroDefParam)), ExprValue) + case _ => pre } case _ => pre } - val args1 = args map mapOver - TypeRef(pre1, sym, args1) + TypeRef(pre1, sym, args map mapOver) case _ => mapOver(tp) } @@ -281,11 +348,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { sigParam }) - val paramsCtx = List(ctxParam) - val paramsThis = List(makeParam(nme.macroThis, macroDef.pos, implType(false, ownerTpe), SYNTHETIC)) - val paramsTparams = tparams map param - val paramssParams = mmap(vparamss)(param) - val paramss = paramsCtx :: paramssParams + val paramss = List(ctxParam) :: mmap(vparamss)(param) val implRetTpe = typeRef(singleType(NoPrefix, ctxParam), getMember(MacroContextClass, tpnme.Expr), List(sigma(retTpe))) } @@ -297,142 +360,23 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { macroTraceVerbose("macroImplSig is: ")(paramss, implRetTpe) } - /** Transforms parameters lists of a macro impl. - * The `transform` function is invoked only for AbsTypeTag evidence parameters. - * - * The transformer takes two arguments: a value parameter from the parameter list - * and a type parameter that is witnesses by the value parameter. - * - * If the transformer returns a NoSymbol, the value parameter is not included from the result. - * If the transformer returns something else, this something else is included in the result instead of the value parameter. - * - * Despite of being highly esoteric, this function significantly simplifies signature analysis. - * For example, it can be used to strip macroImpl.paramss from the evidences (necessary when checking def <-> impl correspondence) - * or to streamline creation of the list of macro arguments. - */ - private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Symbol): List[List[Symbol]] = { - if (paramss.isEmpty || paramss.last.isEmpty) return paramss // no implicit parameters in the signature => nothing to do - if (paramss.head.isEmpty || !(paramss.head.head.tpe <:< MacroContextClass.tpe)) return paramss // no context parameter in the signature => nothing to do - def transformTag(param: Symbol): Symbol = param.tpe.dealias match { - case TypeRef(SingleType(SingleType(NoPrefix, c), universe), typetag, targ :: Nil) - if c == paramss.head.head && universe == MacroContextUniverse && typetag == AbsTypeTagClass => - transform(param, targ.typeSymbol) - case _ => - param - } - val transformed = paramss.last map transformTag filter (_ ne NoSymbol) - if (transformed.isEmpty) paramss.init else paramss.init :+ transformed - } - - /** As specified above, body of a macro definition must reference its implementation. - * This function verifies that the body indeed refers to a method, and that - * the referenced macro implementation is compatible with the given macro definition. + /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method, + * and that that method is signature-wise compatible with the given macro definition. * - * This means that macro implementation (fooBar in example above) must: - * 1) Refer to a statically accessible, non-overloaded method. - * 2) Have the right parameter lists as outlined in the SIP / in the doc comment of this class. - * - * @return typechecked rhs of the given macro definition + * @return Typechecked rhs of the given macro definition if everything is okay. + * EmptyTree if an error occurs. */ - def typedMacroBody(typer: Typer, ddef: DefDef): Tree = { - import typer.context - macroLogVerbose("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) - - val macroDef = ddef.symbol - val defpos = macroDef.pos - val implpos = ddef.rhs.pos - assert(macroDef.isTermMacro, ddef) - - if (fastTrack contains ddef.symbol) { - macroLogVerbose("typecheck terminated unexpectedly: macro is hardwired") - assert(!ddef.tpt.isEmpty, "hardwired macros must provide result type") - return EmptyTree - } - - if (!typer.checkFeature(ddef.pos, MacrosFeature, immediate = true)) { - macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled") - ddef.symbol setFlag IS_ERROR - return EmptyTree - } - - implicit class AugmentedString(s: String) { - def abbreviateCoreAliases: String = { // hack! - var result = s - result = result.replace("c.universe.AbsTypeTag", "c.AbsTypeTag") - result = result.replace("c.universe.Expr", "c.Expr") - result - } - } - - var _hasError = false - def hasError = _hasError - def setError(): Unit = { - _hasError = true - macroDef setFlag IS_ERROR - } - def reportError(pos: Position, msg: String) = { - setError() - context.error(pos, msg) - } - - def invalidBodyError() = - reportError(defpos, - "macro body has wrong shape:" + - "\n required: macro <reference to implementation object>.<implementation method name>" + - "\n or : macro <implementation method name>") - def validatePreTyper(rhs: Tree): Unit = rhs match { - // we do allow macro invocations inside macro bodies - // personally I don't mind if pre-typer tree is a macro invocation - // that later resolves to a valid reference to a macro implementation - // however, I don't think that invalidBodyError() should hint at that - // let this be an Easter Egg :) - case Apply(_, _) => ; - case TypeApply(_, _) => ; - case Super(_, _) => ; - case This(_) => ; - case Ident(_) => ; - case Select(_, _) => ; - case _ => invalidBodyError() - } - def validatePostTyper(rhs1: Tree): Unit = { - def loop(tree: Tree): Unit = { - def errorNotStatic() = - reportError(implpos, "macro implementation must be in statically accessible object") - - def ensureRoot(sym: Symbol) = - if (!sym.isModule && !sym.isModuleClass) errorNotStatic() - - def ensureModule(sym: Symbol) = - if (!sym.isModule) errorNotStatic() - - tree match { - case TypeApply(fun, _) => - loop(fun) - case Super(qual, _) => - ensureRoot(macroDef.owner) - loop(qual) - case This(_) => - ensureRoot(tree.symbol) - case Select(qual, name) if name.isTypeName => - loop(qual) - case Select(qual, name) if name.isTermName => - if (tree.symbol != rhs1.symbol) ensureModule(tree.symbol) - loop(qual) - case Ident(name) if name.isTypeName => - ; - case Ident(name) if name.isTermName => - if (tree.symbol != rhs1.symbol) ensureModule(tree.symbol) - case _ => - invalidBodyError() - } - } - - loop(rhs1) - } - - val rhs = ddef.rhs - validatePreTyper(rhs) - if (hasError) macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) + def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = + try new MacroTyper(typer, macroDdef).typed + catch { case MacroBodyTypecheckException => EmptyTree } + + class MacroTyper(val typer: Typer, val macroDdef: DefDef) extends MacroErrors { + // Phase I: sanity checks + val macroDef = macroDdef.symbol + macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos)) + assert(macroDef.isTermMacro, macroDdef) + if (fastTrack contains macroDef) MacroDefIsFastTrack() + if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) MacroFeatureNotEnabled() // we use typed1 instead of typed, because otherwise adapt is going to mess us up // if adapt sees <qualifier>.<method>, it will want to perform eta-expansion and will fail @@ -440,11 +384,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // because it's adapt which is responsible for automatic expansion during typechecking def typecheckRhs(rhs: Tree): Tree = { try { + // interestingly enough, just checking isErroneous doesn't cut it + // e.g. a "type arguments [U] do not conform to method foo's type parameter bounds" error + // doesn't manifest itself as an error in the resulting tree val prevNumErrors = reporter.ERROR.count - var rhs1 = if (hasError) EmptyTree else typer.typed1(rhs, EXPRmode, WildcardType) - def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors + var rhs1 = typer.typed1(rhs, EXPRmode, WildcardType) def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous - while (!typecheckedWithErrors && rhsNeedsMacroExpansion) { + while (rhsNeedsMacroExpansion) { rhs1 = macroExpand1(typer, rhs1) match { case Success(expanded) => try { @@ -452,7 +398,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked))) typechecked } finally { - openMacros = openMacros.tail + popMacroContext() } case Fallback(fallback) => typer.typed1(fallback, EXPRmode, WildcardType) @@ -460,233 +406,80 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { result } } + val typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors + if (typecheckedWithErrors) MacroDefUntypeableBodyError() rhs1 } catch { case ex: TypeError => typer.reportTypeError(context, rhs.pos, ex) - typer.infer.setError(rhs) + MacroDefUntypeableBodyError() } } - val prevNumErrors = reporter.ERROR.count // funnily enough, the isErroneous check is not enough - var rhs1 = typecheckRhs(rhs) - val macroImpl = rhs1.symbol - def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors - if (typecheckedWithErrors) { - setError() - macroTraceVerbose("body of a macro def failed to typecheck: ")(ddef) - } else { - if (!hasError) { - if (macroImpl == null) invalidBodyError() - else { - if (!macroImpl.isMethod) invalidBodyError() - if (!macroImpl.isPublic) reportError(implpos, "macro implementation must be public") - if (macroImpl.isOverloaded) reportError(implpos, "macro implementation cannot be overloaded") - if (!macroImpl.typeParams.isEmpty && !rhs1.isInstanceOf[TypeApply]) reportError(implpos, "macro implementation reference needs type arguments") - if (!hasError) validatePostTyper(rhs1) - } - } - if (!hasError) { - bindMacroImpl(macroDef, rhs1) // we must bind right over here, because return type inference needs this info - } + // Phase II: typecheck the right-hand side of the macro def + val typed = typecheckRhs(macroDdef.rhs) + typed match { + case MacroImplReference(owner, meth, targs) => + if (!meth.isMethod) MacroDefInvalidBodyError() + if (!meth.isPublic) MacroImplNotPublicError() + if (meth.isOverloaded) MacroImplOverloadedError() + if (!owner.isStaticOwner && !owner.moduleClass.isStaticOwner) MacroImplNotStaticError() + if (meth.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError(typed) + bindMacroImpl(macroDef, typed) + case _ => + MacroDefInvalidBodyError() } - if (!hasError) { - def checkCompatibility(reqparamss: List[List[Symbol]], actparamss: List[List[Symbol]], reqres: Type, actres: Type): List[String] = { - var hasError = false - var errors = List[String]() - def compatibilityError(msg: String) { - hasError = true - errors :+= msg - } - - val flatreqparams = reqparamss.flatten - val flatactparams = actparamss.flatten - val tparams = macroImpl.typeParams - val tvars = tparams map freshVar - def lengthMsg(which: String, extra: Symbol) = - "parameter lists have different length, "+which+" extra parameter "+extra.defString - if (actparamss.length != reqparamss.length) - compatibilityError("number of parameter sections differ") - - def checkSubType(slot: String, reqtpe: Type, acttpe: Type): Unit = { - val ok = if (macroDebugVerbose) { - if (reqtpe eq acttpe) println(reqtpe + " <: " + acttpe + "?" + EOL + "true") - withTypesExplained(reqtpe <:< acttpe) - } else reqtpe <:< acttpe - if (!ok) { - compatibilityError("type mismatch for %s: %s does not conform to %s".format(slot, reqtpe.toString.abbreviateCoreAliases, acttpe.toString.abbreviateCoreAliases)) - } - } + // Phase III: check compatibility between the macro def and its macro impl + // this check ignores type tag evidence parameters, because type tag context bounds are optional + // aXXX (e.g. aparamss) => characteristics of the macro impl ("a" stands for "actual") + // rXXX (e.g. rparamss) => characteristics of a reference macro impl signature synthesized from the macro def ("r" stands for "reference") + val macroImpl = typed.symbol + val aparamss = transformTypeTagEvidenceParams(macroImpl.paramss, (param, tparam) => NoSymbol) + val aret = macroImpl.tpe.finalResultType + val macroDefRet = + if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe + else computeMacroDefTypeFromMacroImpl(macroDdef, macroImpl) + val (rparamss, rret) = macroImplSig(macroDef, macroDdef.tparams, macroDdef.vparamss, macroDefRet) + + val implicitParams = aparamss.flatten filter (_.isImplicit) + if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams) + if (aparamss.length != rparamss.length) MacroImplParamssMismatchError() + + val atparams = macroImpl.typeParams + val atvars = atparams map freshVar + def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars) - if (!hasError) { - try { - for ((rparams, aparams) <- reqparamss zip actparamss) { - if (rparams.length < aparams.length) - compatibilityError(lengthMsg("found", aparams(rparams.length))) - if (aparams.length < rparams.length) - compatibilityError(lengthMsg("required", rparams(aparams.length)).abbreviateCoreAliases) - } - // if the implementation signature is already deemed to be incompatible, we bail out - // otherwise, high-order type magic employed below might crash in weird ways - if (!hasError) { - for ((rparams, aparams) <- reqparamss zip actparamss) { - for ((rparam, aparam) <- rparams zip aparams) { - def isRepeated(param: Symbol) = param.tpe.typeSymbol == RepeatedParamClass - if (rparam.name != aparam.name && !rparam.isSynthetic) { - val rparam1 = rparam - val aparam1 = aparam - compatibilityError("parameter names differ: "+rparam.name+" != "+aparam.name) - } - if (isRepeated(rparam) && !isRepeated(aparam)) - compatibilityError("types incompatible for parameter "+rparam.name+": corresponding is not a vararg parameter") - if (!isRepeated(rparam) && isRepeated(aparam)) - compatibilityError("types incompatible for parameter "+aparam.name+": corresponding is not a vararg parameter") - if (!hasError) { - var atpe = aparam.tpe.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars) - atpe = atpe.dealias // SI-5706 - // strip the { type PrefixType = ... } refinement off the Context or otherwise we get compatibility errors - atpe = atpe match { - case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe - case _ => atpe - } - checkSubType("parameter " + rparam.name, rparam.tpe, atpe) - } - } - } - } - if (!hasError) { - val atpe = actres.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars) - checkSubType("return type", atpe, reqres) - } - if (!hasError) { - val targs = solvedTypes(tvars, tparams, tparams map varianceInType(actres), false, - lubDepth(flatactparams map (_.tpe)) max lubDepth(flatreqparams map (_.tpe))) - val boundsOk = typer.silent(_.infer.checkBounds(ddef, NoPrefix, NoSymbol, tparams, targs, "")) - boundsOk match { - case SilentResultValue(true) => ; - case SilentResultValue(false) | SilentTypeError(_) => - val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds) - compatibilityError("type arguments " + targs.mkString("[", ",", "]") + - " do not conform to " + tparams.head.owner + "'s type parameter bounds " + - (tparams map (_.defString)).mkString("[", ",", "]")) - } - } - } catch { - case ex: NoInstance => - compatibilityError( - "type parameters "+(tparams map (_.defString) mkString ", ")+" cannot be instantiated\n"+ - ex.getMessage) - } - } - - errors.toList - } - - var actparamss = macroImpl.paramss - actparamss = transformTypeTagEvidenceParams(actparamss, (param, tparam) => NoSymbol) - val actres = macroImpl.tpe.finalResultType - val implicitParams = actparamss.flatten filter (_.isImplicit) - if (implicitParams.length > 0) { - // prohibit implicit params on macro implementations - // we don't have to do this, but it appears to be more clear than allowing them - reportError(implicitParams.head.pos, "macro implementations cannot have implicit parameters other than AbsTypeTag evidences") - macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) - } - - if (!hasError) { - val rettpe = if (!ddef.tpt.isEmpty) typer.typedType(ddef.tpt).tpe else computeMacroDefTypeFromMacroImpl(ddef, macroDef, macroImpl) - val (reqparamss, reqres) = macroImplSig(macroDef, ddef.tparams, ddef.vparamss, rettpe) + try { + map2(aparamss, rparamss)((aparams, rparams) => { + if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams) + if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams) + }) - def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean) = { - var argsPart = (pss map (ps => ps map (_.defString) mkString ("(", ", ", ")"))).mkString - if (abbreviate) argsPart = argsPart.abbreviateCoreAliases - var retPart = restpe.toString - if (abbreviate || ddef.tpt.tpe == null) retPart = retPart.abbreviateCoreAliases - argsPart + ": " + retPart + // cannot fuse these loops because if aparamss.flatten != rparamss.flatten + // then `atpeToRtpe` is going to fail with an unsound substitution + map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => { + if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam) + if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam) + val aparamtpe = aparam.tpe.dealias match { + case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe + case tpe => tpe } - def compatibilityError(addendum: String) = - reportError(implpos, - "macro implementation has wrong shape:"+ - "\n required: "+showMeth(reqparamss, reqres, true) + - "\n found : "+showMeth(actparamss, actres, false)+ - "\n"+addendum) - - val errors = checkCompatibility(reqparamss, actparamss, reqres, actres) - if (errors.nonEmpty) compatibilityError(errors mkString "\n") - } - } - - rhs1 - } - - def computeMacroDefTypeFromMacroImpl(macroDdef: DefDef, macroDef: Symbol, macroImpl: Symbol): Type = { - // downgrade from metalevel-0 to metalevel-1 - var runtimeType = macroImpl.tpe.finalResultType.dealias match { - case TypeRef(_, ExprClass, runtimeType :: Nil) => runtimeType - case _ => AnyTpe - } + checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam) + }) - // transform type parameters of a macro implementation into type parameters of a macro definition - runtimeType = runtimeType map { - case TypeRef(pre, sym, args) => - // sym.paramPos is unreliable (see an example in `macroArgs`) - val tparams = macroImpl.typeParams map (_.deSkolemize) - val paramPos = tparams indexOf sym.deSkolemize - val sym1 = - if (paramPos == -1) sym - else loadMacroImplBinding(macroDef).targs(paramPos).tpe.typeSymbol - TypeRef(pre, sym1, args) - case tpe => - tpe - } + checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret) - // as stated in the spec, before being matched to macroimpl, type and value parameters of macrodef - // undergo a special transformation, sigma, that adapts them to the different metalevel macroimpl lives in - // as a result, we need to reverse this transformation when inferring macrodef ret from macroimpl ret - def unsigma(tpe: Type): Type = { - // unfortunately, we cannot dereference ``paramss'', because we're in the middle of inferring a type for ``macroDef'' -// val defParamss = macroDef.paramss - val defParamss = mmap(macroDdef.vparamss)(_.symbol) - var implParamss = macroImpl.paramss - implParamss = transformTypeTagEvidenceParams(implParamss, (param, tparam) => NoSymbol) - - val implCtxParam = if (implParamss.length > 0 && implParamss(0).length > 0) implParamss(0)(0) else null - def implParamToDefParam(implParam: Symbol): Symbol = { - val indices = (((implParamss drop 1).zipWithIndex) map { case (implParams, index) => (index, implParams indexOf implParam) } filter (_._2 != -1)).headOption - val defParam = indices flatMap { - case (plistIndex, pIndex) => - if (defParamss.length <= plistIndex) None - else if (defParamss(plistIndex).length <= pIndex) None - else Some(defParamss(plistIndex)(pIndex)) - } - defParam.orNull + val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe)) + val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, depth = maxLubDepth) + val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, "")) + boundsOk match { + case SilentResultValue(true) => // do nothing, success + case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams) } - - class UnsigmaTypeMap extends TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) => - val pre1 = pre match { - case SingleType(SingleType(SingleType(NoPrefix, param), prefix), value) if param == implCtxParam && prefix == MacroContextPrefix && value == ExprValue => - ThisType(macroDef.owner) - case SingleType(SingleType(NoPrefix, param), value) if implParamToDefParam(param) != null && value == ExprValue => - val macroDefParam = implParamToDefParam(param) - SingleType(NoPrefix, macroDefParam) - case _ => - pre - } - val args1 = args map mapOver - TypeRef(pre1, sym, args1) - case _ => - mapOver(tp) - } - } - - new UnsigmaTypeMap() apply tpe + } catch { + case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex) } - runtimeType = unsigma(runtimeType) - - runtimeType } /** Macro classloader that is used to resolve and run macro implementations. @@ -719,9 +512,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * 4) Resolves macro implementation within the loaded companion. * * @return Requested runtime if macro implementation can be loaded successfully from either of the mirrors, - * null otherwise. + * `null` otherwise. */ - type MacroRuntime = List[Any] => Any + type MacroRuntime = MacroArgs => Any private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime] private def macroRuntime(macroDef: Symbol): MacroRuntime = { macroTraceVerbose("looking for macro implementation: ")(macroDef) @@ -749,7 +542,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val implMeths = implObj.getClass.getDeclaredMethods.find(_.getName == methName) val implMeth = implMeths getOrElse { throw new NoSuchMethodException(s"$className.$methName") } macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMeth)) - (args: List[Any]) => implMeth.invoke(implObj, (args map (_.asInstanceOf[AnyRef])): _*) + args => implMeth.invoke(implObj, ((args.c +: args.others) map (_.asInstanceOf[AnyRef])): _*) } catch { case ex: Exception => macroTraceVerbose(s"macro runtime failed to load: ")(ex.toString) @@ -766,19 +559,14 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] val expandee = expandeeTree } with UnaffiliatedMacroContext { - // todo. infer precise typetag for this Expr, namely the PrefixType member of the Context refinement val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing) override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */) } /** Calculate the arguments to pass to a macro implementation when expanding the provided tree. - * - * This includes inferring the exact type and instance of the macro context to pass, and also - * allowing for missing parameter sections in macro implementation (see ``macroImplParamsss'' for more info). - * - * @return list of runtime objects to pass to the implementation obtained by ``macroRuntime'' */ - private def macroArgs(typer: Typer, expandee: Tree): Option[List[Any]] = { + case class MacroArgs(c: MacroContext, others: List[Any]) + private def macroArgs(typer: Typer, expandee: Tree): MacroArgs = { val macroDef = expandee.symbol val prefixTree = expandee.collect{ case Select(qual, name) => qual }.headOption.getOrElse(EmptyTree) val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee)) @@ -797,28 +585,18 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { collectMacroArgs(expandee) val argcDoesntMatch = macroDef.paramss.length != exprArgs.length - val nullaryArgsEmptyParams = exprArgs.isEmpty && macroDef.paramss == List(List()) - if (argcDoesntMatch && !nullaryArgsEmptyParams) { typer.TyperErrorGen.MacroPartialApplicationError(expandee); return None } + val nullaryArgsEmptyParams = exprArgs.isEmpty && macroDef.paramss == ListOfNil + if (argcDoesntMatch && !nullaryArgsEmptyParams) { typer.TyperErrorGen.MacroPartialApplicationError(expandee) } - var argss: List[List[Any]] = List(context) :: exprArgs.toList + val argss: List[List[Any]] = exprArgs.toList + macroTraceVerbose("context: ")(context) macroTraceVerbose("argss: ")(argss) - val rawArgss = + + val preparedArgss: List[List[Any]] = if (fastTrack contains macroDef) { - if (fastTrack(macroDef) validate argss) argss - else { - // if you're getting here, it's not necessarily partial application that is at fault - // for example, if a signature of a hardwired macro has been changed without updated FastTrack - // then the corresponding partial function in FastTrack will refuse to process the expandee - // validation will return false, and control flow will end up here - // however, for simplicity sake, I didn't introduce the notion of error handling to FastTrack - // so all kinds of validation errors produce `MacroPartialApplicationError` - typer.TyperErrorGen.MacroPartialApplicationError(expandee) - return None - } + if (fastTrack(macroDef) validate context) argss + else typer.TyperErrorGen.MacroPartialApplicationError(expandee) } else { - val binding = loadMacroImplBinding(macroDef) - macroTraceVerbose("binding: ")(binding) - // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences // consider the following example: // @@ -832,9 +610,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // val outer2 = new outer1.C[String] // outer2.foo[Boolean] // - // then T and U need to be inferred from the lexical scope of the call using ``asSeenFrom'' - // whereas V won't be resolved by asSeenFrom and need to be loaded directly from ``expandee'' which needs to contain a TypeApply node + // then T and U need to be inferred from the lexical scope of the call using `asSeenFrom` + // whereas V won't be resolved by asSeenFrom and need to be loaded directly from `expandee` which needs to contain a TypeApply node // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim + val binding = loadMacroImplBinding(macroDef) + macroTraceVerbose("binding: ")(binding) val tags = binding.signature filter (_ != -1) map (paramPos => { val targ = binding.targs(paramPos).tpe.typeSymbol val tpe = if (targ.isTypeParameterOrSkolem) { @@ -852,39 +632,48 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { targ.tpe if (tpe.isConcrete) context.TypeTag(tpe) else context.AbsTypeTag(tpe) }) - val hasImplicitParams = macroDef.paramss.flatten.lastOption exists (_.isImplicit) - argss = if (hasImplicitParams) argss.dropRight(1) :+ (tags ++ argss.last) else argss :+ tags + macroTraceVerbose("tags: ")(tags) // transforms argss taking into account varargness of paramss - // not all argument lists in argss map to macroDef.paramss, so we need to apply extra care - // namely: - // 1) the first argument list represents (c: Context) in macroImpl, so it doesn't have correspondence in macroDef - // 2) typetag context bounds are only declared on macroImpls, so this optional arglist also doesn't match macroDef + // note that typetag context bounds are only declared on macroImpls + // so this optional arglist might not match macroDef's paramlist // nb! varargs can apply to any parameter section, not necessarily to the last one - mapWithIndex(argss)((as, i_argss) => { - val i_paramss = i_argss - 1 - val mapsToParamss = 0 <= i_paramss && i_paramss < macroDef.paramss.length + mapWithIndex(argss :+ tags)((as, i) => { + val mapsToParamss = macroDef.paramss.indices contains i if (mapsToParamss) { - val ps = macroDef.paramss(i_paramss) - if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) - else as + val ps = macroDef.paramss(i) + if (isVarArgsList(ps)) { + val (normal, varargs) = as splitAt (ps.length - 1) + normal :+ varargs // pack all varargs into a single List argument + } else as } else as }) } - val rawArgs = rawArgss.flatten - macroTraceVerbose("rawArgs: ")(rawArgs) - Some(rawArgs) + macroTraceVerbose("preparedArgss: ")(preparedArgss) + MacroArgs(context, preparedArgss.flatten) } /** Keeps track of macros in-flight. - * See more informations in comments to ``openMacros'' in ``scala.reflect.macros.Context''. + * See more informations in comments to `openMacros` in `scala.reflect.macros.Context`. */ - var openMacros = List[MacroContext]() + private var _openMacros = List[MacroContext]() + def openMacros = _openMacros + private def pushMacroContext(c: MacroContext) = _openMacros ::= c + private def popMacroContext() = _openMacros = _openMacros.tail def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition + private sealed abstract class MacroExpansionResult + private case class Success(expanded: Tree) extends MacroExpansionResult + private case class Fallback(fallback: Tree) extends MacroExpansionResult { currentRun.seenMacroExpansionsFallingBack = true } + private case class Other(result: Tree) extends MacroExpansionResult + private def Delay(expanded: Tree) = Other(expanded) + private def Skip(expanded: Tree) = Other(expanded) + private def Cancel(expandee: Tree) = Other(expandee) + private def Failure(expandee: Tree) = Other(expandee) + /** Performs macro expansion: - * 1) Checks whether the expansion needs to be delayed (see ``mustDelayMacroExpansion'') - * 2) Loads macro implementation using ``macroMirror'' + * 1) Checks whether the expansion needs to be delayed (see `mustDelayMacroExpansion`) + * 2) Loads macro implementation using `macroMirror` * 3) Synthesizes invocation arguments for the macro implementation * 4) Checks that the result is a tree bound to this universe * 5) Typechecks the result against the return type of the macro definition @@ -905,51 +694,28 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * the expandee with an error marker set if there has been an error */ def macroExpand(typer: Typer, expandee: Tree, mode: Int = EXPRmode, pt: Type = WildcardType): Tree = { - def fail(what: String, tree: Tree): Tree = { - val err = typer.context.errBuffer.head - this.fail(typer, tree, err.errPos, "failed to %s: %s".format(what, err.errMsg)) - return expandee - } - val start = Statistics.startTimer(macroExpandNanos) - Statistics.incCounter(macroExpandCount) + val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null + if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) try { macroExpand1(typer, expandee) match { - case Success(expanded0) => + case Success(expanded) => try { - val expanded = expanded0 // virtpatmat swallows the local for expandee from the match - // so I added this dummy local for the ease of debugging - var expectedTpe = expandee.tpe - - val isNullaryInvocation = expandee match { - case TypeApply(Select(_, _), _) => true - case TypeApply(Ident(_), _) => true - case Select(_, _) => true - case Ident(_) => true - case _ => false - } - if (isNullaryInvocation) expectedTpe match { - case NullaryMethodType(restpe) => - macroTraceVerbose("nullary invocation of a nullary method. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe) - expectedTpe = restpe - case MethodType(Nil, restpe) => - macroTraceVerbose("nullary invocation of a method with an empty parameter list. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe) - expectedTpe = restpe - case _ => ; + def typecheck(phase: String, tree: Tree, pt: Type): Tree = { + if (tree.isErroneous) return tree + macroLogVerbose(s"typechecking against $phase $pt: $expanded") + val numErrors = reporter.ERROR.count + def hasNewErrors = reporter.ERROR.count > numErrors + val result = typer.context.withImplicitsEnabled(typer.typed(tree, EXPRmode, pt)) + macroTraceVerbose(s"""${if (hasNewErrors) "failed to typecheck" else "successfully typechecked"} against $phase $pt:\n$result\n""")(result) } - macroLogVerbose("typechecking1 against %s: %s".format(expectedTpe, expanded)) - var typechecked = typer.context.withImplicitsEnabled(typer.typed(expanded, EXPRmode, expectedTpe)) - if (typer.context.hasErrors) fail("typecheck against macro def return type", expanded) - macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked))) - - macroLogVerbose("typechecking2 against %s: %s".format(pt, expanded)) - typechecked = typer.context.withImplicitsEnabled(typer.typed(typechecked, EXPRmode, pt)) - if (typer.context.hasErrors) fail("typecheck against expected type", expanded) - macroLogVerbose("typechecked2:%n%s%n%s".format(typechecked, showRaw(typechecked))) - + var expectedTpe = expandee.tpe + if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType + var typechecked = typecheck("macro def return type", expanded, expectedTpe) + typechecked = typecheck("expected type", typechecked, pt) typechecked addAttachment MacroExpansionAttachment(expandee) } finally { - openMacros = openMacros.tail + popMacroContext() } case Fallback(fallback) => typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt)) @@ -957,234 +723,98 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { result } } finally { - Statistics.stopTimer(macroExpandNanos, start) + if (Statistics.canEnable) Statistics.stopTimer(macroExpandNanos, start) } } - private sealed abstract class MacroExpansionResult extends Product with Serializable - private case class Success(expanded: Tree) extends MacroExpansionResult - private case class Fallback(fallback: Tree) extends MacroExpansionResult - private case class Other(result: Tree) extends MacroExpansionResult - private def Delay(expanded: Tree) = Other(expanded) - private def Skip(expanded: Tree) = Other(expanded) - private def Cancel(expandee: Tree) = Other(expandee) - private def Failure(expandee: Tree) = Other(expandee) - private def fail(typer: Typer, expandee: Tree, pos: Position = NoPosition, msg: String = null) = { - def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg - macroLogLite("macro expansion has failed: %s".format(msgForLog)) - val errorPos = if (pos != NoPosition) pos else (if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition) - if (msg != null) typer.context.error(errorPos, msg) - typer.infer.setError(expandee) - Failure(expandee) - } - - /** Does the same as ``macroExpand'', but without typechecking the expansion + /** Does the same as `macroExpand`, but without typechecking the expansion * Meant for internal use within the macro infrastructure, don't use it elsewhere. */ private def macroExpand1(typer: Typer, expandee: Tree): MacroExpansionResult = - // InfoLevel.Verbose examines and prints out infos of symbols - // by the means of this'es these symbols can climb up the lexical scope - // when these symbols will be examined by a node printer - // they will enumerate and analyze their children (ask for infos and tpes) - // if one of those children involves macro expansion, things might get nasty - // that's why I'm temporarily turning this behavior off + // verbose printing might cause recursive macro expansions, so I'm shutting it down here withInfoLevel(nodePrinters.InfoLevel.Quiet) { - // if a macro implementation is incompatible or any of the arguments are erroneous - // there is no sense to expand the macro itself => it will only make matters worse if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" macroTraceVerbose("cancelled macro expansion because of %s: ".format(reason))(expandee) return Cancel(typer.infer.setError(expandee)) } - val runtime = macroRuntime(expandee.symbol) - if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) - else macroExpandWithoutRuntime(typer, expandee) + try { + val runtime = macroRuntime(expandee.symbol) + if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) + else macroExpandWithoutRuntime(typer, expandee) + } catch { + case typer.TyperErrorGen.MacroExpansionException => Failure(expandee) + } } /** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded * Meant for internal use within the macro infrastructure, don't use it elsewhere. */ private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroExpansionResult = { - def issueFreeError(sym: FreeSymbol) = { - val template = ( - "Macro expansion contains free @kind@ variable %s. Have you forgotten to use %s? " - + "If you have troubles tracking free @kind@ variables, consider using -Xlog-free-@kind@s" - ) - val forgotten = ( - if (sym.isTerm) "splice when splicing this variable into a reifee" - else "c.AbsTypeTag annotation for this type parameter" - ) - typer.context.error(expandee.pos, - template.replaceAllLiterally("@kind@", sym.name.nameKind).format( - sym.name + " " + sym.origin, forgotten) - ) - } - def macroExpandInternal = { - val wasDelayed = isDelayed(expandee) - val undetparams = calculateUndetparams(expandee) - val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty - - def failExpansion(msg: String = null) = fail(typer, expandee, msg = msg) - def performExpansion(args: List[Any]): MacroExpansionResult = { - val numErrors = reporter.ERROR.count - def hasNewErrors = reporter.ERROR.count > numErrors - - val expanded = runtime(args) - - if (hasNewErrors) - failExpansion() // errors have been reported by the macro itself - else expanded match { - case expanded: Expr[_] => - macroLogVerbose("original:") - macroLogLite("" + expanded.tree + "\n" + showRaw(expanded.tree)) - - expanded.tree.freeTerms foreach issueFreeError - expanded.tree.freeTypes foreach issueFreeError - if (hasNewErrors) failExpansion() - - // inherit the position from the first position-ful expandee in macro callstack - // this is essential for sane error messages - // now macro expansion gets typechecked against the macro definition return type - // however, this happens in macroExpand, not here in macroExpand1 - else Success(atPos(enclosingMacroPosition.focus)(expanded.tree)) - case _ => - failExpansion( - "macro must return a compiler-specific expr; returned value is " + ( - if (expanded.isInstanceOf[Expr[_]]) " Expr, but it doesn't belong to this compiler's universe" - else " of " + expanded.getClass - ) - ) - } - } - - if (wasDelayed) { - if (nowDelayed) Delay(expandee) - else Skip(macroExpandAll(typer, expandee)) - } - else { - macroLogLite("typechecking macro expansion %s at %s".format(expandee, expandee.pos)) - macroArgs(typer, expandee).fold(failExpansion(): MacroExpansionResult) { - args => (args: @unchecked) match { - // crashes virtpatmat: - // case args @ ((context: MacroContext) :: _) => - // todo. extract a minimized test case - case args @ (context0 :: _) => - val context = context0.asInstanceOf[MacroContext] - if (nowDelayed) { - macroLogLite("macro expansion is delayed: %s".format(expandee)) - delayed += expandee -> undetparams - // need to save typer context for `macroExpandAll` - // need to save macro context to preserve enclosures - expandee addAttachment MacroRuntimeAttachment(delayed = true, typerContext = typer.context, macroContext = Some(context.asInstanceOf[MacroContext])) - Delay(expandee) - } - else { - // adding stuff to openMacros is easy, but removing it is a nightmare - // it needs to be sprinkled over several different code locations - // why? https://github.com/scala/scala/commit/bd3eacbae21f39b1ac7fe8ade4ed71fa98e1a28d#L2R1137 - // todo. will be improved - openMacros ::= context - var isSuccess = false - try performExpansion(args) match { - case x: Success => isSuccess = true ; x - case x => x - } - finally { - expandee.removeAttachment[MacroRuntimeAttachment] - if (!isSuccess) openMacros = openMacros.tail - } - } + val wasDelayed = isDelayed(expandee) + val undetparams = calculateUndetparams(expandee) + val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty + + (wasDelayed, nowDelayed) match { + case (true, true) => Delay(expandee) + case (true, false) => Skip(macroExpandAll(typer, expandee)) + case (false, true) => + macroLogLite("macro expansion is delayed: %s".format(expandee)) + delayed += expandee -> undetparams + expandee addAttachment MacroRuntimeAttachment(delayed = true, typerContext = typer.context, macroContext = Some(macroArgs(typer, expandee).c)) + Delay(expandee) + case (false, false) => + import typer.TyperErrorGen._ + macroLogLite("performing macro expansion %s at %s".format(expandee, expandee.pos)) + val args = macroArgs(typer, expandee) + try { + val numErrors = reporter.ERROR.count + def hasNewErrors = reporter.ERROR.count > numErrors + val expanded = { pushMacroContext(args.c); runtime(args) } + if (hasNewErrors) MacroGeneratedTypeError(expandee) + expanded match { + case expanded: Expr[_] => + macroLogVerbose("original:") + macroLogLite("" + expanded.tree + "\n" + showRaw(expanded.tree)) + val freeSyms = expanded.tree.freeTerms ++ expanded.tree.freeTypes + freeSyms foreach (sym => MacroFreeSymbolError(expandee, sym)) + Success(atPos(enclosingMacroPosition.focus)(expanded.tree)) + case _ => + MacroExpansionIsNotExprError(expandee, expanded) } + } catch { + case ex: Throwable => + popMacroContext() + val realex = ReflectionUtils.unwrapThrowable(ex) + realex match { + case ex: AbortMacroException => MacroGeneratedAbort(expandee, ex) + case ex: ControlThrowable => throw ex + case ex: TypeError => MacroGeneratedTypeError(expandee, ex) + case _ => MacroGeneratedException(expandee, realex) + } + } finally { + expandee.removeAttachment[MacroRuntimeAttachment] } - } } - - try macroExpandInternal - catch { case ex: Throwable => handleMacroExpansionException(typer, expandee, ex) } } + /** Expands a macro when a runtime (i.e. the macro implementation) cannot be loaded + * Meant for internal use within the macro infrastructure, don't use it elsewhere. + */ private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroExpansionResult = { - val macroDef = expandee.symbol - def notFound() = { - typer.context.error(expandee.pos, "macro implementation not found: " + macroDef.name + " " + - "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)") - None - } - def fallBackToOverridden(tree: Tree): Option[Tree] = { + import typer.TyperErrorGen._ + val fallbackSym = expandee.symbol.nextOverriddenSymbol orElse MacroImplementationNotFoundError(expandee) + macroTraceLite("falling back to: ")(fallbackSym) + + def mkFallbackTree(tree: Tree): Tree = { tree match { - case Select(qual, name) if (macroDef.isTermMacro) => - macroDef.allOverriddenSymbols match { - case first :: _ => - Some(Select(qual, name) setPos tree.pos setSymbol first) - case _ => - macroTraceVerbose("macro is not overridden: ")(tree) - notFound() - } - case Apply(fn, args) => - fallBackToOverridden(fn) match { - case Some(fn1) => Some(Apply(fn1, args) setPos tree.pos) - case _ => None - } - case TypeApply(fn, args) => - fallBackToOverridden(fn) match { - case Some(fn1) => Some(TypeApply(fn1, args) setPos tree.pos) - case _ => None - } - case _ => - macroTraceVerbose("unexpected tree in fallback: ")(tree) - notFound() + case Select(qual, name) => Select(qual, name) setPos tree.pos setSymbol fallbackSym + case Apply(fn, args) => Apply(mkFallbackTree(fn), args) setPos tree.pos + case TypeApply(fn, args) => TypeApply(mkFallbackTree(fn), args) setPos tree.pos } } - fallBackToOverridden(expandee) match { - case Some(tree1) => - macroTraceLite("falling back to: ")(tree1) - currentRun.macroExpansionFailed = true - Fallback(tree1) - case None => - fail(typer, expandee) - } - } - - private def handleMacroExpansionException(typer: Typer, expandee: Tree, ex: Throwable): MacroExpansionResult = { - val realex = ReflectionUtils.unwrapThrowable(ex) - realex match { - case realex: reflect.macros.runtime.AbortMacroException => - macroLogVerbose("macro expansion has failed: %s".format(realex.msg)) - fail(typer, expandee) // error has been reported by abort - case err: TypeError => - macroLogLite("macro expansion has failed: %s at %s".format(err.msg, err.pos)) - throw err // error should be propagated, don't report - case _ => - val message = { - try { - // [Eugene] is there a better way? - // [Paul] See Exceptional.scala and Origins.scala. - val relevancyThreshold = realex.getStackTrace().indexWhere(este => este.getMethodName == "macroExpand1") - if (relevancyThreshold == -1) None - else { - var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1) - def isMacroInvoker(este: StackTraceElement) = este.isNativeMethod || (este.getClassName != null && (este.getClassName contains "fastTrack")) - var threshold = relevantElements.reverse.indexWhere(isMacroInvoker) + 1 - while (threshold != relevantElements.length && isMacroInvoker(relevantElements(relevantElements.length - threshold - 1))) threshold += 1 - relevantElements = relevantElements dropRight threshold - - realex.setStackTrace(relevantElements) - val message = new java.io.StringWriter() - realex.printStackTrace(new java.io.PrintWriter(message)) - Some(EOL + message) - } - } catch { - // if the magic above goes boom, just fall back to uninformative, but better than nothing, getMessage - case ex: Throwable => - None - } - } getOrElse { - val msg = realex.getMessage - if (msg != null) msg else realex.getClass.getName - } - fail(typer, expandee, msg = "exception during macro expansion: " + message) - } + Fallback(mkFallbackTree(expandee)) } /** Without any restrictions on macro expansion, macro applications will expand at will, @@ -1236,7 +866,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { /** Performs macro expansion on all subtrees of a given tree. * Innermost macros are expanded first, outermost macros are expanded last. - * See the documentation for ``macroExpand'' for more information. + * See the documentation for `macroExpand` for more information. */ def macroExpandAll(typer: Typer, expandee: Tree): Tree = new Transformer { diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index f7ba189e0f..4f597f97c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -242,7 +242,7 @@ trait MethodSynthesis { abort("No synthetics for " + meth + ": synthetics contains " + context.unit.synthetics.keys.mkString(", ")) } case _ => - List(stat) + stat :: Nil } def standardAccessors(vd: ValDef): List[DerivedFromValDef] = ( @@ -491,7 +491,7 @@ trait MethodSynthesis { // Derives a tree without attempting to use the original tree's symbol. override def derivedTree = { atPos(tree.pos.focus) { - DefDef(derivedMods, name, Nil, List(Nil), tree.tpt.duplicate, + DefDef(derivedMods, name, Nil, ListOfNil, tree.tpt.duplicate, if (isDeferred) EmptyTree else Select(This(owner), tree.name) ) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 92d0d4e228..adced9d8c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -192,6 +192,10 @@ trait Namers extends MethodSynthesis { if (!allowsOverload(sym)) { val prev = scope.lookupEntry(sym.name) if ((prev ne null) && prev.owner == scope && conflict(sym, prev.sym)) { + if (sym.isSynthetic || prev.sym.isSynthetic) { + handleSyntheticNameConflict(sym, prev.sym) + handleSyntheticNameConflict(prev.sym, sym) + } DoubleDefError(sym, prev.sym) sym setInfo ErrorType scope unlink prev.sym // let them co-exist... @@ -202,6 +206,14 @@ trait Namers extends MethodSynthesis { scope enter sym } + /** Logic to handle name conflicts of synthetically generated symbols + * We handle right now: t6227 + */ + def handleSyntheticNameConflict(sym1: Symbol, sym2: Symbol) = { + if (sym1.isImplicit && sym1.isMethod && sym2.isModule && sym2.companionClass.isCaseClass) + validate(sym2.companionClass) + } + def enterSym(tree: Tree): Context = { def dispatch() = { var returnContext = this.context @@ -973,7 +985,7 @@ trait Namers extends MethodSynthesis { // Add a () parameter section if this overrides some method with () parameters. if (clazz.isClass && vparamss.isEmpty && overriddenSymbol.alternatives.exists( _.info.isInstanceOf[MethodType])) { - vparamSymss = List(List()) + vparamSymss = ListOfNil } mforeach(vparamss) { vparam => if (vparam.tpt.isEmpty) { @@ -1032,7 +1044,7 @@ trait Namers extends MethodSynthesis { var baseParamss = (vparamss, overridden.tpe.paramss) match { // match empty and missing parameter list case (Nil, List(Nil)) => Nil - case (List(Nil), Nil) => List(Nil) + case (List(Nil), Nil) => ListOfNil case (_, paramss) => paramss } assert( @@ -1390,6 +1402,7 @@ trait Namers extends MethodSynthesis { fail(ImplicitAtToplevel) } if (sym.isClass) { + checkNoConflict(IMPLICIT, CASE) if (sym.isAnyOverride && !sym.hasFlag(TRAIT)) fail(OverrideClass) } else { diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 6eeba2b4bf..c60118a8b4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -277,7 +277,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL if(phase.id >= currentRun.uncurryPhase.id) debugwarn("running translateMatch at "+ phase +" on "+ selector +" match "+ cases) patmatDebug("translating "+ cases.mkString("{", "\n", "}")) - val start = Statistics.startTimer(patmatNanos) + val start = if (Statistics.canEnable) Statistics.startTimer(patmatNanos) else null val selectorTp = repeatedToSeq(elimAnonymousClass(selector.tpe.widen.withoutAnnotations)) @@ -305,7 +305,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental val combined = combineCases(selector, selectorSym, cases map translateCase(selectorSym, pt), pt, matchOwner, matchFailGenOverride) - Statistics.stopTimer(patmatNanos, start) + if (Statistics.canEnable) Statistics.stopTimer(patmatNanos, start) combined } @@ -1954,7 +1954,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // TODO: for V1 representing x1 and V2 standing for x1.head, encode that // V1 = Nil implies -(V2 = Ci) for all Ci in V2's domain (i.e., it is unassignable) def removeVarEq(props: List[Prop], modelNull: Boolean = false): (Prop, List[Prop]) = { - val start = Statistics.startTimer(patmatAnaVarEq) + val start = if (Statistics.canEnable) Statistics.startTimer(patmatAnaVarEq) else null val vars = new collection.mutable.HashSet[Var] @@ -2009,7 +2009,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL patmatDebug("eqAxioms:\n"+ cnfString(eqFreePropToSolvable(eqAxioms))) patmatDebug("pure:"+ pure.map(p => cnfString(eqFreePropToSolvable(p))).mkString("\n")) - Statistics.stopTimer(patmatAnaVarEq, start) + if (Statistics.canEnable) Statistics.stopTimer(patmatAnaVarEq, start) (eqAxioms, pure) } @@ -2040,6 +2040,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL trait CNF extends Logic { // CNF: a formula is a conjunction of clauses type Formula = Array[Clause] + /** Override Array creation for efficiency (to not go through reflection). */ + private implicit val formulaTag: scala.reflect.ClassTag[Formula] = new scala.reflect.ClassTag[Formula] { + def runtimeClass: java.lang.Class[Formula] = classOf[Formula] + final override def newArray(len: Int): Array[Formula] = new Array[Formula](len) + } def formula(c: Clause*): Formula = c.toArray def andFormula(a: Formula, b: Formula): Formula = a ++ b @@ -2116,13 +2121,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - val start = Statistics.startTimer(patmatCNF) + val start = if (Statistics.canEnable) Statistics.startTimer(patmatCNF) else null val res = conjunctiveNormalForm(negationNormalForm(p)) - Statistics.stopTimer(patmatCNF, start) + if (Statistics.canEnable) Statistics.stopTimer(patmatCNF, start) // - if (Statistics.enabled) patmatCNFSizes(res.size).value += 1 + if (Statistics.canEnable) patmatCNFSizes(res.size).value += 1 // patmatDebug("cnf for\n"+ p +"\nis:\n"+cnfString(res)) res @@ -2199,7 +2204,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL patmatDebug("DPLL\n"+ cnfString(f)) - val start = Statistics.startTimer(patmatAnaDPLL) + val start = if (Statistics.canEnable) Statistics.startTimer(patmatAnaDPLL) else null val satisfiableWithModel: Model = if (f isEmpty) EmptyModel @@ -2237,7 +2242,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - Statistics.stopTimer(patmatAnaDPLL, start) + if (Statistics.canEnable) Statistics.stopTimer(patmatAnaDPLL, start) satisfiableWithModel } @@ -2598,7 +2603,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // thus, the case is unreachable if there is no model for -(-P /\ C), // or, equivalently, P \/ -C, or C => P def unreachableCase(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Int] = { - val start = Statistics.startTimer(patmatAnaReach) + val start = if (Statistics.canEnable) Statistics.startTimer(patmatAnaReach) else null // use the same approximator so we share variables, // but need different conditions depending on whether we're conservatively looking for failure or success @@ -2652,7 +2657,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - Statistics.stopTimer(patmatAnaReach, start) + if (Statistics.canEnable) Statistics.stopTimer(patmatAnaReach, start) if (reachable) None else Some(caseIndex) } catch { @@ -2745,7 +2750,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // - back off (to avoid crying exhaustive too often) when: // - there are guards --> // - there are extractor calls (that we can't secretly/soundly) rewrite - val start = Statistics.startTimer(patmatAnaExhaust) + val start = if (Statistics.canEnable) Statistics.startTimer(patmatAnaExhaust) else null var backoff = false val approx = new TreeMakersToConds(prevBinder) @@ -2797,7 +2802,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val pruned = CounterExample.prune(counterExamples).map(_.toString).sorted - Statistics.stopTimer(patmatAnaExhaust, start) + if (Statistics.canEnable) Statistics.stopTimer(patmatAnaExhaust, start) pruned } catch { case ex : AnalysisBudget.Exception => diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 41387b7507..166bb2d18c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -124,7 +124,11 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R defaultMethodNames.toList.distinct foreach { name => val methods = clazz.info.findMember(name, 0L, METHOD, false).alternatives - val haveDefaults = methods filter (sym => sym.hasParamWhich(_.hasDefault) && !nme.isProtectedAccessorName(sym.name)) + def hasDefaultParam(tpe: Type): Boolean = tpe match { + case MethodType(params, restpe) => (params exists (_.hasDefault)) || hasDefaultParam(restpe) + case _ => false + } + val haveDefaults = methods filter (sym => hasDefaultParam(sym.info) && !nme.isProtectedAccessorName(sym.name)) if (haveDefaults.lengthCompare(1) > 0) { val owners = haveDefaults map (_.owner) @@ -937,9 +941,9 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case TypeBounds(lo, hi) => validateVariance(lo, -variance) validateVariance(hi, variance) - case MethodType(formals, result) => + case mt @ MethodType(formals, result) => if (inRefinement) - validateVariances(formals map (_.tpe), -variance) + validateVariances(mt.paramTypes, -variance) validateVariance(result, variance) case NullaryMethodType(result) => validateVariance(result, variance) @@ -1298,13 +1302,12 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case t if treeInfo.isSelfConstrCall(t) => assert(index == 0, index) - val t = transform(tree) - if (currentLevel.maxindex > 0) { + try transform(tree) :: Nil + finally if (currentLevel.maxindex > 0) { // An implementation restriction to avoid VerifyErrors and lazyvals mishaps; see SI-4717 debuglog("refsym = " + currentLevel.refsym) unit.error(currentLevel.refpos, "forward reference not allowed from self constructor invocation") } - List(t) case ModuleDef(_, _, _) => eliminateModuleDefs(tree) case ValDef(_, _, _, _) => val tree1 @ ValDef(_, _, _, rhs) = transform(tree) // important to do before forward reference check @@ -1316,11 +1319,11 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R debuglog("refsym = " + currentLevel.refsym) unit.error(currentLevel.refpos, "forward reference extends over definition of " + lazySym) } - List(tree1) + tree1 :: Nil } case Import(_, _) => Nil case DefDef(mods, _, _, _, _, _) if (mods hasFlag MACRO) || (tree.symbol hasFlag MACRO) => Nil - case _ => List(transform(tree)) + case _ => transform(tree) :: Nil } /* Check whether argument types conform to bounds of type parameters */ @@ -1615,6 +1618,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R if ((clazz isSubClass AnyValClass) && !isPrimitiveValueClass(clazz)) { if (clazz.isTrait) unit.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal") + else if ((clazz != AnyValClass) && clazz.hasFlag(ABSTRACT)) + unit.error(clazz.pos, "`abstract' modifier cannot be used with value classes") } } diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index d2a89eb9ff..63050bc032 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -70,7 +70,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT storeAccessorDefinition(clazz, DefDef(acc, EmptyTree)) acc } - + atPos(sel.pos)(Select(gen.mkAttributedThis(clazz), superAcc) setType sel.tpe) } @@ -170,6 +170,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT treeCopy.CaseDef(tree, pat, transform(guard), transform(body)) case ClassDef(_, _, _, _) => + def transformClassDef = { checkCompanionNameClashes(sym) val decls = sym.info.decls for (s <- decls) { @@ -195,12 +196,15 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } } super.transform(tree) + } + transformClassDef case ModuleDef(_, _, _) => checkCompanionNameClashes(sym) super.transform(tree) case Template(_, _, body) => + def transformTemplate = { val ownAccDefs = new ListBuffer[Tree] accDefs(currentOwner) = ownAccDefs @@ -213,6 +217,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT accDefs -= currentOwner ownAccDefs ++= body1 deriveTemplate(tree)(_ => ownAccDefs.toList) + } + transformTemplate case TypeApply(sel @ Select(This(_), name), args) => mayNeedProtectedAccessor(sel, args, false) @@ -227,6 +233,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT typeDef case sel @ Select(qual, name) => + def transformSelect = { /** return closest enclosing method, unless shadowed by an enclosing class; * no use of closures here in the interest of speed. */ @@ -290,7 +297,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT log("Ensuring accessor for call to protected " + sym.fullLocationString + " from " + currentClass) ensureAccessor(sel) } else - mayNeedProtectedAccessor(sel, List(EmptyTree), false) + mayNeedProtectedAccessor(sel, EmptyTree.asList, false) } case Super(_, mix) => @@ -303,8 +310,10 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT transformSuperSelect(sel) case _ => - mayNeedProtectedAccessor(sel, List(EmptyTree), true) + mayNeedProtectedAccessor(sel, EmptyTree.asList, true) + } } + transformSelect case DefDef(mods, name, tparams, vparamss, tpt, rhs) if tree.symbol.isMethodWithExtension => treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, withInvalidOwner(transform(rhs))) @@ -313,6 +322,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT mayNeedProtectedAccessor(sel, args, true) case Assign(lhs @ Select(qual, name), rhs) => + def transformAssign = { if (lhs.symbol.isVariable && lhs.symbol.isJavaDefined && needsProtectedAccessor(lhs.symbol, tree.pos)) { @@ -322,6 +332,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT transform(localTyper.typed(Apply(setter, List(qual, rhs)))) } else super.transform(tree) + } + transformAssign case Apply(fn, args) => assert(fn.tpe != null, tree) @@ -345,9 +357,22 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } } - override def atOwner[A](owner: Symbol)(trans: => A): A = { + /** a typer for each enclosing class */ + private var typers = immutable.Map[Symbol, analyzer.Typer]() + + /** Specialized here for performance; the previous blanked + * introduction of typers in TypingTransformer caused a >5% + * performance hit for the compiler as a whole. + */ + override def atOwner[A](tree: Tree, owner: Symbol)(trans: => A): A = { if (owner.isClass) validCurrentOwner = true - super.atOwner(owner)(trans) + val savedLocalTyper = localTyper + localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner) + typers = typers updated (owner, localTyper) + val result = super.atOwner(tree, owner)(trans) + localTyper = savedLocalTyper + typers -= owner + result } private def withInvalidOwner[A](trans: => A): A = { @@ -500,7 +525,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT ) true } - isCandidate && !host.isPackageClass && !isSelfType + isCandidate && !host.isPackageClass && !isSelfType } /** Return the innermost enclosing class C of referencingClass for which either diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9b07eed5d7..f6baf02c3e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -104,7 +104,8 @@ trait Typers extends Modes with Adaptations with Tags { tp.isError || pt.isError || context0.implicitsEnabled && // this condition prevents chains of views inferView(EmptyTree, tp, pt, false) != EmptyTree - }} + } + } /** Find implicit arguments and pass them to given tree. */ @@ -194,16 +195,14 @@ trait Typers extends Modes with Adaptations with Tags { case PolyType(_, _) => EmptyTree case _ => def wrapImplicit(from: Type): Tree = { - val result = inferImplicit(tree, functionType(List(from), to), reportAmbiguous, true, context, saveErrors) + val result = inferImplicit(tree, functionType(from :: Nil, to), reportAmbiguous, true, context, saveErrors) if (result.subst != EmptyTreeTypeSubstituter) { result.subst traverse tree notifyUndetparamsInferred(result.subst.from, result.subst.to) } result.tree } - val result = wrapImplicit(from) - if (result != EmptyTree) result - else wrapImplicit(byNameType(from)) + wrapImplicit(from) orElse wrapImplicit(byNameType(from)) } } @@ -354,7 +353,7 @@ trait Typers extends Modes with Adaptations with Tags { if (formals exists (isRepeatedParamType(_))) error(pos, "methods with `*`-parameters cannot be converted to function values"); */ - if (restpe.isDependent) + if (tpe.isDependentMethodType) DependentMethodTpeConversionToFunctionError(tree, tpe) checkParamsConvertible(tree, restpe) case _ => @@ -608,21 +607,23 @@ trait Typers extends Modes with Adaptations with Tags { /** Is `sym` defined in package object of package `pkg`? */ - private def isInPackageObject(sym: Symbol, pkg: Symbol) = - pkg.isPackageClass && { - sym.alternatives forall { sym => - !sym.owner.isPackage && { - sym.owner.isPackageObjectClass && + private def isInPackageObject(sym: Symbol, pkg: Symbol) = { + def isInPkgObj(sym: Symbol) = + !sym.owner.isPackage && { + sym.owner.isPackageObjectClass && sym.owner.owner == pkg || pkg.isInitialized && { // need to be careful here to not get a cyclic reference during bootstrap val pkgobj = pkg.info.member(nme.PACKAGEkw) pkgobj.isInitialized && - (pkgobj.info.member(sym.name).alternatives contains sym) + (pkgobj.info.member(sym.name).alternatives contains sym) } - } } + pkg.isPackageClass && { + if (sym.isOverloaded) sym.alternatives forall isInPkgObj + else isInPkgObj(sym) } + } /** Post-process an identifier or selection node, performing the following: * 1. Check that non-function pattern expressions are stable @@ -705,15 +706,15 @@ trait Typers extends Modes with Adaptations with Tags { def silent[T](op: Typer => T, reportAmbiguousErrors: Boolean = context.ambiguousErrors, newtree: Tree = context.tree): SilentResult[T] = { - val rawTypeStart = Statistics.startCounter(rawTypeFailed) - val findMemberStart = Statistics.startCounter(findMemberFailed) - val subtypeStart = Statistics.startCounter(subtypeFailed) - val failedSilentStart = Statistics.startTimer(failedSilentNanos) + val rawTypeStart = if (Statistics.canEnable) Statistics.startCounter(rawTypeFailed) else null + val findMemberStart = if (Statistics.canEnable) Statistics.startCounter(findMemberFailed) else null + val subtypeStart = if (Statistics.canEnable) Statistics.startCounter(subtypeFailed) else null + val failedSilentStart = if (Statistics.canEnable) Statistics.startTimer(failedSilentNanos) else null def stopStats() = { - Statistics.stopCounter(rawTypeFailed, rawTypeStart) - Statistics.stopCounter(findMemberFailed, findMemberStart) - Statistics.stopCounter(subtypeFailed, subtypeStart) - Statistics.stopTimer(failedSilentNanos, failedSilentStart) + if (Statistics.canEnable) Statistics.stopCounter(rawTypeFailed, rawTypeStart) + if (Statistics.canEnable) Statistics.stopCounter(findMemberFailed, findMemberStart) + if (Statistics.canEnable) Statistics.stopCounter(subtypeFailed, subtypeStart) + if (Statistics.canEnable) Statistics.stopTimer(failedSilentNanos, failedSilentStart) } try { if (context.reportErrors || @@ -1133,109 +1134,112 @@ trait Typers extends Modes with Adaptations with Tags { } else if (tree.tpe <:< pt) { tree } else { - if (inPatternMode(mode)) { - if ((tree.symbol ne null) && tree.symbol.isModule) - inferModulePattern(tree, pt) - if (isPopulated(tree.tpe, approximateAbstracts(pt))) - return tree - } - val tree1 = constfold(tree, pt) // (10) (11) - if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original) - else { - if (inExprModeButNot(mode, FUNmode)) { - pt.normalize match { - case TypeRef(_, sym, _) => - // note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially - // infinite expansion if pt is constant type () - if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) { // (12) - if (settings.warnValueDiscard.value) - context.unit.warning(tree.pos, "discarded non-Unit value") - return typed(atPos(tree.pos)(Block(List(tree), Literal(Constant()))), mode, pt) - } else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) { - if (settings.warnNumericWiden.value) - context.unit.warning(tree.pos, "implicit numeric widening") - return typed(atPos(tree.pos)(Select(tree, "to" + sym.name)), mode, pt) - } - case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (13) - return typed(adaptAnnotations(tree, mode, pt), mode, pt) - case _ => - } - if (!context.undetparams.isEmpty) { - return instantiate(tree, mode, pt) - } - if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) { - // (14); the condition prevents chains of views - debuglog("inferring view from " + tree.tpe + " to " + pt) - val coercion = inferView(tree, tree.tpe, pt, true) - // convert forward views of delegate types into closures wrapped around - // the delegate's apply method (the "Invoke" method, which was translated into apply) - if (forMSIL && coercion != null && isCorrespondingDelegate(tree.tpe, pt)) { - val meth: Symbol = tree.tpe.member(nme.apply) - debuglog("replacing forward delegate view with: " + meth + ":" + meth.tpe) - return typed(Select(tree, meth), mode, pt) + def fallBack: Tree = { + if (inPatternMode(mode)) { + if ((tree.symbol ne null) && tree.symbol.isModule) + inferModulePattern(tree, pt) + if (isPopulated(tree.tpe, approximateAbstracts(pt))) + return tree + } + val tree1 = constfold(tree, pt) // (10) (11) + if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original) + else { + if (inExprModeButNot(mode, FUNmode)) { + pt.normalize match { + case TypeRef(_, sym, _) => + // note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially + // infinite expansion if pt is constant type () + if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) { // (12) + if (settings.warnValueDiscard.value) + context.unit.warning(tree.pos, "discarded non-Unit value") + return typed(atPos(tree.pos)(Block(List(tree), Literal(Constant()))), mode, pt) + } else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) { + if (settings.warnNumericWiden.value) + context.unit.warning(tree.pos, "implicit numeric widening") + return typed(atPos(tree.pos)(Select(tree, "to" + sym.name)), mode, pt) + } + case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (13) + return typed(adaptAnnotations(tree, mode, pt), mode, pt) + case _ => } - if (coercion != EmptyTree) { - def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe - if (settings.logImplicitConv.value) - unit.echo(tree.pos, msg) - - debuglog(msg) - val silentContext = context.makeImplicit(context.ambiguousErrors) - val res = newTyper(silentContext).typed( - new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) - if (silentContext.hasErrors) context.issue(silentContext.errBuffer.head) else return res + if (!context.undetparams.isEmpty) { + return instantiate(tree, mode, pt) + } + if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) { + // (14); the condition prevents chains of views + debuglog("inferring view from " + tree.tpe + " to " + pt) + val coercion = inferView(tree, tree.tpe, pt, true) + // convert forward views of delegate types into closures wrapped around + // the delegate's apply method (the "Invoke" method, which was translated into apply) + if (forMSIL && coercion != null && isCorrespondingDelegate(tree.tpe, pt)) { + val meth: Symbol = tree.tpe.member(nme.apply) + debuglog("replacing forward delegate view with: " + meth + ":" + meth.tpe) + return typed(Select(tree, meth), mode, pt) + } + if (coercion != EmptyTree) { + def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe + if (settings.logImplicitConv.value) + unit.echo(tree.pos, msg) + + debuglog(msg) + val silentContext = context.makeImplicit(context.ambiguousErrors) + val res = newTyper(silentContext).typed( + new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) + if (silentContext.hasErrors) context.issue(silentContext.errBuffer.head) else return res + } } } - } - if (settings.debug.value) { - log("error tree = " + tree) - if (settings.explaintypes.value) explainTypes(tree.tpe, pt) - } + if (settings.debug.value) { + log("error tree = " + tree) + if (settings.explaintypes.value) explainTypes(tree.tpe, pt) + } - val found = tree.tpe - if (!found.isErroneous && !pt.isErroneous) { - if (!context.reportErrors && isPastTyper) { - val (bound, req) = pt match { - case ExistentialType(qs, tpe) => (qs, tpe) - case _ => (Nil, pt) - } - val boundOrSkolems = bound ++ pt.skolemsExceptMethodTypeParams - if (boundOrSkolems.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 - // - // val x: T = expr - // - // where T is the type of expr, but T contains existential skolems ts. - // In that case, this value definition does not typecheck. - // The value definition - // - // val x: T forSome { ts } = expr - // - // would typecheck. Or one can simply leave out the type of the `val`: - // - // val x = expr - // - // SI-6029 shows another case where we also fail (in uncurry), but this time the expected - // type is an existential type. - // - // The reason for both failures have to do with the way we (don't) transform - // skolem types along with the trees that contain them. We'd need a - // radically different approach to do it. But before investing a lot of time to - // to do this (I have already sunk 3 full days with in the end futile attempts - // to consistently transform skolems and fix 6029), I'd like to - // investigate ways to avoid skolems completely. - // - log("recovering from existential or skolem type error in tree \n" + tree + "\nwith type " + tree.tpe + "\n expected type = " + pt + "\n context = " + context.tree) - return adapt(tree, mode, deriveTypeWithWildcards(boundOrSkolems)(pt)) + val found = tree.tpe + if (!found.isErroneous && !pt.isErroneous) { + if (!context.reportErrors && isPastTyper) { + val (bound, req) = pt match { + case ExistentialType(qs, tpe) => (qs, tpe) + case _ => (Nil, pt) + } + val boundOrSkolems = bound ++ pt.skolemsExceptMethodTypeParams + if (boundOrSkolems.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 + // + // val x: T = expr + // + // where T is the type of expr, but T contains existential skolems ts. + // In that case, this value definition does not typecheck. + // The value definition + // + // val x: T forSome { ts } = expr + // + // would typecheck. Or one can simply leave out the type of the `val`: + // + // val x = expr + // + // SI-6029 shows another case where we also fail (in uncurry), but this time the expected + // type is an existential type. + // + // The reason for both failures have to do with the way we (don't) transform + // skolem types along with the trees that contain them. We'd need a + // radically different approach to do it. But before investing a lot of time to + // to do this (I have already sunk 3 full days with in the end futile attempts + // to consistently transform skolems and fix 6029), I'd like to + // investigate ways to avoid skolems completely. + // + log("recovering from existential or skolem type error in tree \n" + tree + "\nwith type " + tree.tpe + "\n expected type = " + pt + "\n context = " + context.tree) + return adapt(tree, mode, deriveTypeWithWildcards(boundOrSkolems)(pt)) + } } + // create an actual error + AdaptTypeError(tree, found, pt) } - // create an actual error - AdaptTypeError(tree, found, pt) + setError(tree) } - setError(tree) } + fallBack } } } @@ -2504,7 +2508,7 @@ trait Typers extends Modes with Adaptations with Tags { def translated = if (members.head eq EmptyTree) setError(tree) - else typed(atPos(tree.pos)(Block(List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, tree.pos.focus)), atPos(tree.pos.focus)(New(anonClass.tpe)))), mode, pt) + else typed(atPos(tree.pos)(Block(List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus)), atPos(tree.pos.focus)(New(anonClass.tpe)))), mode, pt) } // Function(params, Match(sel, cases)) ==> new <Partial>Function { def apply<OrElse>(params) = `translateMatch('sel match { cases }')` } @@ -2850,73 +2854,79 @@ trait Typers extends Modes with Adaptations with Tags { def duplErrTree = setError(treeCopy.Apply(tree, fun0, args)) def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree } - var fun = fun0 - if (fun.hasSymbol && fun.symbol.isOverloaded) { - // remove alternatives with wrong number of parameters without looking at types. - // less expensive than including them in inferMethodAlternatvie (see below). - def shapeType(arg: Tree): Type = arg match { - case Function(vparams, body) => - functionType(vparams map (vparam => AnyClass.tpe), shapeType(body)) - case AssignOrNamedArg(Ident(name), rhs) => - NamedType(name, shapeType(rhs)) - case _ => - NothingClass.tpe - } - val argtypes = args map shapeType - val pre = fun.symbol.tpe.prefix - - var sym = fun.symbol filter { alt => - // must use pt as expected type, not WildcardType (a tempting quick fix to #2665) - // now fixed by using isWeaklyCompatible in exprTypeArgs - // TODO: understand why exactly -- some types were not inferred anymore (`ant clean quick.bin` failed) - // (I had expected inferMethodAlternative to pick up the slack introduced by using WildcardType here) - // - // @PP responds: I changed it to pass WildcardType instead of pt and only one line in - // trunk (excluding scalacheck, which had another) failed to compile. It was this line in - // Types: "refs = Array(Map(), Map())". I determined that inference fails if there are at - // least two invariant type parameters. See the test case I checked in to help backstop: - // pos/isApplicableSafe.scala. - isApplicableSafe(context.undetparams, followApply(pre.memberType(alt)), argtypes, pt) - } - if (sym.isOverloaded) { - val sym1 = sym filter (alt => { - // eliminate functions that would result from tupling transforms - // keeps alternatives with repeated params - hasExactlyNumParams(followApply(alt.tpe), argtypes.length) || - // also keep alts which define at least one default - alt.tpe.paramss.exists(_.exists(_.hasDefault)) - }) - if (sym1 != NoSymbol) sym = sym1 - } - if (sym != NoSymbol) - fun = adapt(fun setSymbol sym setType pre.memberType(sym), forFunMode(mode), WildcardType) + def preSelectOverloaded(fun: Tree): Tree = { + if (fun.hasSymbol && fun.symbol.isOverloaded) { + // remove alternatives with wrong number of parameters without looking at types. + // less expensive than including them in inferMethodAlternatvie (see below). + def shapeType(arg: Tree): Type = arg match { + case Function(vparams, body) => + functionType(vparams map (vparam => AnyClass.tpe), shapeType(body)) + case AssignOrNamedArg(Ident(name), rhs) => + NamedType(name, shapeType(rhs)) + case _ => + NothingClass.tpe + } + val argtypes = args map shapeType + val pre = fun.symbol.tpe.prefix + + var sym = fun.symbol filter { alt => + // must use pt as expected type, not WildcardType (a tempting quick fix to #2665) + // now fixed by using isWeaklyCompatible in exprTypeArgs + // TODO: understand why exactly -- some types were not inferred anymore (`ant clean quick.bin` failed) + // (I had expected inferMethodAlternative to pick up the slack introduced by using WildcardType here) + // + // @PP responds: I changed it to pass WildcardType instead of pt and only one line in + // trunk (excluding scalacheck, which had another) failed to compile. It was this line in + // Types: "refs = Array(Map(), Map())". I determined that inference fails if there are at + // least two invariant type parameters. See the test case I checked in to help backstop: + // pos/isApplicableSafe.scala. + isApplicableSafe(context.undetparams, followApply(pre.memberType(alt)), argtypes, pt) + } + if (sym.isOverloaded) { + val sym1 = sym filter (alt => { + // eliminate functions that would result from tupling transforms + // keeps alternatives with repeated params + hasExactlyNumParams(followApply(alt.tpe), argtypes.length) || + // also keep alts which define at least one default + alt.tpe.paramss.exists(_.exists(_.hasDefault)) + }) + if (sym1 != NoSymbol) sym = sym1 + } + if (sym == NoSymbol) fun + else adapt(fun setSymbol sym setType pre.memberType(sym), forFunMode(mode), WildcardType) + } else fun } + val fun = preSelectOverloaded(fun0) + fun.tpe match { case OverloadedType(pre, alts) => - val undetparams = context.extractUndetparams() - - val argtpes = new ListBuffer[Type] - val amode = forArgMode(fun, mode) - val args1 = args map { - case arg @ AssignOrNamedArg(Ident(name), rhs) => - // named args: only type the righthand sides ("unknown identifier" errors otherwise) - val rhs1 = typedArg(rhs, amode, BYVALmode, WildcardType) - argtpes += NamedType(name, rhs1.tpe.deconst) - // the assign is untyped; that's ok because we call doTypedApply - atPos(arg.pos) { new AssignOrNamedArg(arg.lhs , rhs1) } - case arg => - val arg1 = typedArg(arg, amode, BYVALmode, WildcardType) - argtpes += arg1.tpe.deconst - arg1 - } - context.undetparams = undetparams - if (context.hasErrors) - setError(tree) - else { - inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) - doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + def handleOverloaded = { + val undetparams = context.extractUndetparams() + + val argtpes = new ListBuffer[Type] + val amode = forArgMode(fun, mode) + val args1 = args map { + case arg @ AssignOrNamedArg(Ident(name), rhs) => + // named args: only type the righthand sides ("unknown identifier" errors otherwise) + val rhs1 = typedArg(rhs, amode, BYVALmode, WildcardType) + argtpes += NamedType(name, rhs1.tpe.deconst) + // the assign is untyped; that's ok because we call doTypedApply + atPos(arg.pos) { new AssignOrNamedArg(arg.lhs, rhs1) } + case arg => + val arg1 = typedArg(arg, amode, BYVALmode, WildcardType) + argtpes += arg1.tpe.deconst + arg1 + } + context.undetparams = undetparams + if (context.hasErrors) + setError(tree) + else { + inferMethodAlternative(fun, undetparams, argtpes.toList, pt, varArgsOnly = treeInfo.isWildcardStarArgList(args)) + doTypedApply(tree, adapt(fun, forFunMode(mode), WildcardType), args1, mode, pt) + } } + handleOverloaded case mt @ MethodType(params, _) => val paramTypes = mt.paramTypes @@ -3037,101 +3047,107 @@ trait Typers extends Modes with Adaptations with Tags { } else { val tparams = context.extractUndetparams() if (tparams.isEmpty) { // all type params are defined - // In order for checkDead not to be misled by the unfortunate special - // case of AnyRef#synchronized (which is implemented with signature T => T - // but behaves as if it were (=> T) => T) we need to know what is the actual - // target of a call. Since this information is no longer available from - // typedArg, it is recorded here. - checkDead.updateExpr(fun) - - val args1 = - // no expected type when jumping to a match label -- anything goes (this is ok since we're typing the translation of well-typed code) - // ... except during erasure: we must take the expected type into account as it drives the insertion of casts! - // I've exhausted all other semi-clean approaches I could think of in balancing GADT magic, SI-6145, CPS type-driven transforms and other existential trickiness - // (the right thing to do -- packing existential types -- runs into limitations in subtyping existential types, - // casting breaks SI-6145, - // not casting breaks GADT typing as it requires sneaking ill-typed trees past typer) - if (!phase.erasedTypes && fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol)) - typedArgs(args, forArgMode(fun, mode)) - else - typedArgs(args, forArgMode(fun, mode), paramTypes, formals) - - // instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case: - // val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo) - // precise(foo) : foo.type => foo.type - val restpe = mt.resultType(args1 map (arg => gen.stableTypeFor(arg) getOrElse arg.tpe)) - def ifPatternSkipFormals(tp: Type) = tp match { - case MethodType(_, rtp) if (inPatternMode(mode)) => rtp - case _ => tp - } + def handleMonomorphicCall: Tree = { + // In order for checkDead not to be misled by the unfortunate special + // case of AnyRef#synchronized (which is implemented with signature T => T + // but behaves as if it were (=> T) => T) we need to know what is the actual + // target of a call. Since this information is no longer available from + // typedArg, it is recorded here. + checkDead.updateExpr(fun) + + val args1 = + // no expected type when jumping to a match label -- anything goes (this is ok since we're typing the translation of well-typed code) + // ... except during erasure: we must take the expected type into account as it drives the insertion of casts! + // I've exhausted all other semi-clean approaches I could think of in balancing GADT magic, SI-6145, CPS type-driven transforms and other existential trickiness + // (the right thing to do -- packing existential types -- runs into limitations in subtyping existential types, + // casting breaks SI-6145, + // not casting breaks GADT typing as it requires sneaking ill-typed trees past typer) + if (!phase.erasedTypes && fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol)) + typedArgs(args, forArgMode(fun, mode)) + else + typedArgs(args, forArgMode(fun, mode), paramTypes, formals) + + // instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case: + // val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo) + // precise(foo) : foo.type => foo.type + val restpe = mt.resultType(args1 map (arg => gen.stableTypeFor(arg) getOrElse arg.tpe)) + def ifPatternSkipFormals(tp: Type) = tp match { + case MethodType(_, rtp) if (inPatternMode(mode)) => rtp + case _ => tp + } - // Replace the Delegate-Chainer methods += and -= with corresponding - // + and - calls, which are translated in the code generator into - // Combine and Remove - if (forMSIL) { - fun match { - case Select(qual, name) => - if (isSubType(qual.tpe, DelegateClass.tpe) - && (name == encode("+=") || name == encode("-="))) - { - val n = if (name == encode("+=")) nme.PLUS else nme.MINUS - val f = Select(qual, n) - // the compiler thinks, the PLUS method takes only one argument, - // but he thinks it's an instance method -> still two ref's on the stack - // -> translated by backend - val rhs = treeCopy.Apply(tree, f, args) - return typed(Assign(qual, rhs)) - } - case _ => () + // Replace the Delegate-Chainer methods += and -= with corresponding + // + and - calls, which are translated in the code generator into + // Combine and Remove + if (forMSIL) { + fun match { + case Select(qual, name) => + if (isSubType(qual.tpe, DelegateClass.tpe) + && (name == encode("+=") || name == encode("-="))) { + val n = if (name == encode("+=")) nme.PLUS else nme.MINUS + val f = Select(qual, n) + // the compiler thinks, the PLUS method takes only one argument, + // but he thinks it's an instance method -> still two ref's on the stack + // -> translated by backend + val rhs = treeCopy.Apply(tree, f, args) + return typed(Assign(qual, rhs)) + } + case _ => () + } } - } - /** This is translating uses of List() into Nil. This is less - * than ideal from a consistency standpoint, but it shouldn't be - * altered without due caution. - * ... this also causes bootstrapping cycles if List_apply is - * forced during kind-arity checking, so it is guarded by additional - * tests to ensure we're sufficiently far along. - */ - if (args.isEmpty && !forInteractive && fun.symbol.isInitialized && ListModule.hasCompleteInfo && (fun.symbol == List_apply)) - atPos(tree.pos)(gen.mkNil setType restpe) - else - constfold(treeCopy.Apply(tree, fun, args1) setType ifPatternSkipFormals(restpe)) + /** + * This is translating uses of List() into Nil. This is less + * than ideal from a consistency standpoint, but it shouldn't be + * altered without due caution. + * ... this also causes bootstrapping cycles if List_apply is + * forced during kind-arity checking, so it is guarded by additional + * tests to ensure we're sufficiently far along. + */ + if (args.isEmpty && !forInteractive && fun.symbol.isInitialized && ListModule.hasCompleteInfo && (fun.symbol == List_apply)) + atPos(tree.pos)(gen.mkNil setType restpe) + else + constfold(treeCopy.Apply(tree, fun, args1) setType ifPatternSkipFormals(restpe)) + } + handleMonomorphicCall } else if (needsInstantiation(tparams, formals, args)) { //println("needs inst "+fun+" "+tparams+"/"+(tparams map (_.info))) inferExprInstance(fun, tparams) doTypedApply(tree, fun, args, mode, pt) } else { - assert(!inPatternMode(mode), modeString(mode)) // this case cannot arise for patterns - val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt) - val strictTargs = map2(lenientTargs, tparams)((targ, tparam) => - if (targ == WildcardType) tparam.tpeHK else targ) - var remainingParams = paramTypes - def typedArgToPoly(arg: Tree, formal: Type): Tree = { //TR TODO: cleanup - val lenientPt = formal.instantiateTypeParams(tparams, lenientTargs) - val newmode = - if (isByNameParamType(remainingParams.head)) POLYmode - else POLYmode | BYVALmode - if (remainingParams.tail.nonEmpty) remainingParams = remainingParams.tail - val arg1 = typedArg(arg, forArgMode(fun, mode), newmode, lenientPt) - val argtparams = context.extractUndetparams() - if (!argtparams.isEmpty) { - val strictPt = formal.instantiateTypeParams(tparams, strictTargs) - inferArgumentInstance(arg1, argtparams, strictPt, lenientPt) - arg1 - } else arg1 - } - val args1 = map2(args, formals)(typedArgToPoly) - if (args1 exists {_.isErrorTyped}) duplErrTree - else { - debuglog("infer method inst "+fun+", tparams = "+tparams+", args = "+args1.map(_.tpe)+", pt = "+pt+", lobounds = "+tparams.map(_.tpe.bounds.lo)+", parambounds = "+tparams.map(_.info)) //debug - // define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun" - // returns those undetparams which have not been instantiated. - val undetparams = inferMethodInstance(fun, tparams, args1, pt) - val result = doTypedApply(tree, fun, args1, mode, pt) - context.undetparams = undetparams - result + def handlePolymorphicCall = { + assert(!inPatternMode(mode), modeString(mode)) // this case cannot arise for patterns + val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt) + val strictTargs = map2(lenientTargs, tparams)((targ, tparam) => + if (targ == WildcardType) tparam.tpeHK else targ) + var remainingParams = paramTypes + def typedArgToPoly(arg: Tree, formal: Type): Tree = { //TR TODO: cleanup + val lenientPt = formal.instantiateTypeParams(tparams, lenientTargs) + val newmode = + if (isByNameParamType(remainingParams.head)) POLYmode + else POLYmode | BYVALmode + if (remainingParams.tail.nonEmpty) remainingParams = remainingParams.tail + val arg1 = typedArg(arg, forArgMode(fun, mode), newmode, lenientPt) + val argtparams = context.extractUndetparams() + if (!argtparams.isEmpty) { + val strictPt = formal.instantiateTypeParams(tparams, strictTargs) + inferArgumentInstance(arg1, argtparams, strictPt, lenientPt) + arg1 + } else arg1 + } + val args1 = map2(args, formals)(typedArgToPoly) + if (args1 exists { _.isErrorTyped }) duplErrTree + else { + debuglog("infer method inst " + fun + ", tparams = " + tparams + ", args = " + args1.map(_.tpe) + ", pt = " + pt + ", lobounds = " + tparams.map(_.tpe.bounds.lo) + ", parambounds = " + tparams.map(_.info)) //debug + // define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun" + // returns those undetparams which have not been instantiated. + val undetparams = inferMethodInstance(fun, tparams, args1, pt) + val result = doTypedApply(tree, fun, args1, mode, pt) + context.undetparams = undetparams + result + } } + handlePolymorphicCall } } @@ -3572,9 +3588,9 @@ trait Typers extends Modes with Adaptations with Tags { def isCapturedExistential(sym: Symbol) = (sym hasAllFlags (EXISTENTIAL | CAPTURED)) && { - val start = Statistics.startTimer(isReferencedNanos) + val start = if (Statistics.canEnable) Statistics.startTimer(isReferencedNanos) else null try !isReferencedFrom(context, sym) - finally Statistics.stopTimer(isReferencedNanos, start) + finally if (Statistics.canEnable) Statistics.stopTimer(isReferencedNanos, start) } def packCaptured(tpe: Type): Type = { @@ -3854,7 +3870,9 @@ trait Typers extends Modes with Adaptations with Tags { case _ => NoType } - def typedAnnotated(ann: Tree, arg1: Tree): Tree = { + def typedAnnotated(atd: Annotated): Tree = { + val ann = atd.annot + val arg1 = typed(atd.arg, mode, pt) /** mode for typing the annotation itself */ val annotMode = mode & ~TYPEmode | EXPRmode @@ -3933,7 +3951,9 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedBind(name: Name, body: Tree) = + def typedBind(tree: Bind) = { + val name = tree.name + val body = tree.body name match { case name: TypeName => assert(body == EmptyTree, context.unit + " typedBind: " + name.debugString + " " + body + " " + body.getClass) val sym = @@ -3977,11 +3997,11 @@ trait Typers extends Modes with Adaptations with Tags { tree setSymbol sym treeCopy.Bind(tree, name, body1) setSymbol sym setType body1.tpe } + } - - def typedArrayValue(elemtpt: Tree, elems: List[Tree]) = { - val elemtpt1 = typedType(elemtpt, mode) - val elems1 = elems mapConserve (elem => typed(elem, mode, elemtpt1.tpe)) + def typedArrayValue(tree: ArrayValue) = { + val elemtpt1 = typedType(tree.elemtpt, mode) + val elems1 = tree.elems mapConserve (elem => typed(elem, mode, elemtpt1.tpe)) treeCopy.ArrayValue(tree, elemtpt1, elems1) .setType( (if (isFullyDefined(pt) && !phase.erasedTypes) pt @@ -4024,17 +4044,19 @@ trait Typers extends Modes with Adaptations with Tags { else fail() } - def typedIf(cond: Tree, thenp: Tree, elsep: Tree) = { - val cond1 = checkDead(typed(cond, EXPRmode | BYVALmode, BooleanClass.tpe)) + def typedIf(tree: If) = { + val cond1 = checkDead(typed(tree.cond, EXPRmode | BYVALmode, BooleanClass.tpe)) + val thenp = tree.thenp + val elsep = tree.elsep if (elsep.isEmpty) { // in the future, should be unnecessary val thenp1 = typed(thenp, UnitClass.tpe) treeCopy.If(tree, cond1, thenp1, elsep) setType thenp1.tpe } else { var thenp1 = typed(thenp, pt) var elsep1 = typed(elsep, pt) + def thenTp = packedType(thenp1, context.owner) + def elseTp = packedType(elsep1, context.owner) - lazy val thenTp = packedType(thenp1, context.owner) - lazy val elseTp = packedType(elsep1, context.owner) // println("typedIf: "+(thenp1.tpe, elsep1.tpe, ptOrLub(List(thenp1.tpe, elsep1.tpe)),"\n", thenTp, elseTp, thenTp =:= elseTp)) val (owntype, needAdapt) = // in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway @@ -4046,7 +4068,7 @@ trait Typers extends Modes with Adaptations with Tags { && thenTp =:= elseTp ) (thenp1.tpe, false) // use unpacked type // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala - else ptOrLub(List(thenp1.tpe, elsep1.tpe), pt) + else ptOrLub(thenp1.tpe :: elsep1.tpe :: Nil, pt) if (needAdapt) { //isNumericValueType(owntype)) { thenp1 = adapt(thenp1, mode, owntype) @@ -4059,7 +4081,9 @@ trait Typers extends Modes with Adaptations with Tags { // under -Xexperimental (and not -Xoldpatmat), and when there's a suitable __match in scope, virtualize the pattern match // otherwise, type the Match and leave it until phase `patmat` (immediately after typer) // empty-selector matches are transformed into synthetic PartialFunction implementations when the expected type demands it - def typedVirtualizedMatch(tree: Tree, selector: Tree, cases: List[CaseDef]): Tree = + def typedVirtualizedMatch(tree: Match): Tree = { + val selector = tree.selector + val cases = tree.cases if (selector == EmptyTree) { if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass)) (new MatchFunTyper(tree, cases, mode, pt)).translated else { @@ -4076,8 +4100,10 @@ trait Typers extends Modes with Adaptations with Tags { } } else virtualizedMatch(typedMatch(selector, cases, mode, pt, tree), mode, pt) + } - def typedReturn(expr: Tree) = { + def typedReturn(tree: Return) = { + val expr = tree.expr val enclMethod = context.enclMethod if (enclMethod == NoContext || enclMethod.owner.isConstructor || @@ -4104,7 +4130,8 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedNew(tpt: Tree) = { + def typedNew(tree: New) = { + val tpt = tree.tpt val tpt1 = { val tpt0 = typedTypeConstructor(tpt) if (checkStablePrefixClassType(tpt0)) @@ -4212,10 +4239,10 @@ trait Typers extends Modes with Adaptations with Tags { * insert an implicit conversion. */ def tryTypedApply(fun: Tree, args: List[Tree]): Tree = { - val start = Statistics.startTimer(failedApplyNanos) + val start = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null def onError(typeError: AbsTypeError): Tree = { - Statistics.stopTimer(failedApplyNanos, start) + if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, start) // If the problem is with raw types, copnvert to existentials and try again. // See #4712 for a case where this situation arises, @@ -4271,15 +4298,15 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedApply(fun: Tree, args: List[Tree]) = { + def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = { val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable if (stableApplication && isPatternMode) { // treat stable function applications f() as expressions. typed1(tree, mode & ~PATTERNmode | EXPRmode, pt) } else { val funpt = if (isPatternMode) pt else WildcardType - val appStart = Statistics.startTimer(failedApplyNanos) - val opeqStart = Statistics.startTimer(failedOpEqNanos) + val appStart = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null + val opeqStart = if (Statistics.canEnable) Statistics.startTimer(failedOpEqNanos) else null def onError(reportError: => Tree): Tree = { fun match { @@ -4287,14 +4314,14 @@ trait Typers extends Modes with Adaptations with Tags { if !isPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) => val qual1 = typedQualifier(qual) if (treeInfo.isVariableOrGetter(qual1)) { - Statistics.stopTimer(failedOpEqNanos, opeqStart) + if (Statistics.canEnable) Statistics.stopTimer(failedOpEqNanos, opeqStart) convertToAssignment(fun, qual1, name, args) } else { - Statistics.stopTimer(failedApplyNanos, appStart) + if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) reportError } case _ => - Statistics.stopTimer(failedApplyNanos, appStart) + if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) reportError } } @@ -4303,7 +4330,7 @@ trait Typers extends Modes with Adaptations with Tags { if ((mode & EXPRmode) != 0) tree else context.tree) match { case SilentResultValue(fun1) => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 - Statistics.incCounter(typedApplyCount) + if (Statistics.canEnable) Statistics.incCounter(typedApplyCount) def isImplicitMethod(tpe: Type) = tpe match { case mt: MethodType => mt.isImplicit case _ => false @@ -4342,6 +4369,38 @@ trait Typers extends Modes with Adaptations with Tags { } } + def typedApply(tree: Apply) = { + val fun = tree.fun + val args = tree.args + fun match { + case Block(stats, expr) => + typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) + case _ => + normalTypedApply(tree, fun, args) match { + case Apply(Select(New(tpt), name), args) + if (tpt.tpe != null && + tpt.tpe.typeSymbol == ArrayClass && + args.length == 1 && + erasure.GenericArray.unapply(tpt.tpe).isDefined) => // !!! todo simplify by using extractor + // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len) + // convert new Array^N[T](len) for N > 1 to evidence[ClassTag[Array[...Array[T]...]]].newArray(len), where Array HK gets applied (N-1) times + // [Eugene] no more MaxArrayDims. ClassTags are flexible enough to allow creation of arrays of arbitrary dimensionality (w.r.t JVM restrictions) + val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe) + val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.toTypeConstructor, List(tpe))).last + val newArrayApp = atPos(tree.pos) { + val tag = resolveClassTag(tree.pos, tagType) + if (tag.isEmpty) MissingClassTagError(tree, tagType) + else new ApplyToImplicitArgs(Select(tag, nme.newArray), args) + } + typed(newArrayApp, mode, pt) + case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => //SI-5696 + TooManyArgumentListsForConstructor(tree) + case tree1 => + tree1 + } + } + } + def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree]): Tree = { val prefix = name.toTermName stripSuffix nme.EQL def mkAssign(vble: Tree): Tree = @@ -4387,8 +4446,9 @@ trait Typers extends Modes with Adaptations with Tags { typed1(tree1, mode, pt) } - def typedSuper(qual: Tree, mix: TypeName) = { - val qual1 = typed(qual) + def typedSuper(tree: Super) = { + val mix = tree.mix + val qual1 = typed(tree.qual) val clazz = qual1 match { case This(_) => qual1.symbol @@ -4431,12 +4491,13 @@ trait Typers extends Modes with Adaptations with Tags { treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype) } - def typedThis(qual: Name) = tree.symbol orElse qualifyingClass(tree, qual, packageOK = false) match { - case NoSymbol => tree - case clazz => - tree setSymbol clazz setType clazz.thisType.underlying - if (isStableContext(tree, mode, pt)) tree setType clazz.thisType else tree - } + def typedThis(tree: This) = + tree.symbol orElse qualifyingClass(tree, tree.qual, packageOK = false) match { + case NoSymbol => tree + case clazz => + tree setSymbol clazz setType clazz.thisType.underlying + if (isStableContext(tree, mode, pt)) tree setType clazz.thisType else tree + } /** Attribute a selection where <code>tree</code> is <code>qual.name</code>. * <code>qual</code> is already attributed. @@ -4445,7 +4506,7 @@ trait Typers extends Modes with Adaptations with Tags { * @param name ... * @return ... */ - def typedSelect(qual: Tree, name: Name): Tree = { + def typedSelect(tree: Tree, qual: Tree, name: Name): Tree = { def asDynamicCall = dyna.mkInvoke(context.tree, tree, qual, name) map (typed1(_, mode, pt)) val sym = tree.symbol orElse member(qual, name) orElse { @@ -4463,41 +4524,43 @@ trait Typers extends Modes with Adaptations with Tags { qual.tpe = tree.symbol.owner.tpe if (!reallyExists(sym)) { - if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { - val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } - if (tree1 != EmptyTree) return typed1(tree1, mode, pt) - } + def handleMissing: Tree = { + if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { + val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } + if (tree1 != EmptyTree) return typed1(tree1, mode, pt) + } - // try to expand according to Dynamic rules. - asDynamicCall foreach (x => return x) + // try to expand according to Dynamic rules. + asDynamicCall foreach (x => return x) - debuglog( - "qual = "+qual+":"+qual.tpe+ - "\nSymbol="+qual.tpe.termSymbol+"\nsymbol-info = "+qual.tpe.termSymbol.info+ - "\nscope-id = "+qual.tpe.termSymbol.info.decls.hashCode()+"\nmembers = "+qual.tpe.members+ - "\nname = "+name+"\nfound = "+sym+"\nowner = "+context.enclClass.owner - ) + debuglog( + "qual = " + qual + ":" + qual.tpe + + "\nSymbol=" + qual.tpe.termSymbol + "\nsymbol-info = " + qual.tpe.termSymbol.info + + "\nscope-id = " + qual.tpe.termSymbol.info.decls.hashCode() + "\nmembers = " + qual.tpe.members + + "\nname = " + name + "\nfound = " + sym + "\nowner = " + context.enclClass.owner) - def makeInteractiveErrorTree = { - val tree1 = tree match { - case Select(_, _) => treeCopy.Select(tree, qual, name) - case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) + def makeInteractiveErrorTree = { + val tree1 = tree match { + case Select(_, _) => treeCopy.Select(tree, qual, name) + case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name) + } + setError(tree1) } - setError(tree1) - } - if (name == nme.ERROR && forInteractive) - return makeInteractiveErrorTree + if (name == nme.ERROR && forInteractive) + return makeInteractiveErrorTree - if (!qual.tpe.widen.isErroneous) { - if ((mode & QUALmode) != 0) { - val lastTry = rootMirror.missingHook(qual.tpe.typeSymbol, name) - if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) + if (!qual.tpe.widen.isErroneous) { + if ((mode & QUALmode) != 0) { + val lastTry = rootMirror.missingHook(qual.tpe.typeSymbol, name) + if (lastTry != NoSymbol) return typed1(tree setSymbol lastTry, mode, pt) + } + NotAMemberError(tree, qual, name) } - NotAMemberError(tree, qual, name) - } - if (forInteractive) makeInteractiveErrorTree else setError(tree) + if (forInteractive) makeInteractiveErrorTree else setError(tree) + } + handleMissing } else { val tree1 = tree match { case Select(_, _) => treeCopy.Select(tree, qual, name) @@ -4553,6 +4616,49 @@ trait Typers extends Modes with Adaptations with Tags { } } + def typedSelectOrSuperCall(tree: Select) = { + val qual = tree.qualifier + val name = tree.name + qual match { + case _: Super if name == nme.CONSTRUCTOR => + val qual1 = + typed(qual, EXPRmode | QUALmode | POLYmode | SUPERCONSTRmode, WildcardType) + // the qualifier type of a supercall constructor is its first parent class + typedSelect(tree, qual1, nme.CONSTRUCTOR) + case _ => + if (Statistics.canEnable) Statistics.incCounter(typedSelectCount) + var qual1 = checkDead(typedQualifier(qual, mode)) + if (name.isTypeName) qual1 = checkStable(qual1) + + val tree1 = // temporarily use `filter` and an alternative for `withFilter` + if (name == nme.withFilter) + silent(_ => typedSelect(tree, qual1, name)) match { + case SilentResultValue(result) => + result + case _ => + silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { + case SilentResultValue(result2) => + unit.deprecationWarning( + tree.pos, "`withFilter' method does not yet exist on " + qual1.tpe.widen + + ", using `filter' method instead") + result2 + case SilentTypeError(err) => + WithFilterError(tree, err) + } + } + else + typedSelect(tree, qual1, name) + + if (tree.isInstanceOf[PostfixSelect]) + checkFeature(tree.pos, PostfixOpsFeature, name.decode) + if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember) + checkFeature(tree1.pos, ReflectiveCallsFeature, tree1.symbol.toString) + + if (qual1.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name) + else tree1 + } + } + /** Attribute an identifier consisting of a simple name or an outer reference. * * @param tree The tree representing the identifier. @@ -4560,7 +4666,7 @@ trait Typers extends Modes with Adaptations with Tags { * Transformations: (1) Prefix class members with this. * (2) Change imported symbols to selections */ - def typedIdent(name: Name): Tree = { + def typedIdent(tree: Tree, name: Name): Tree = { var errorContainer: AbsTypeError = null @inline def ambiguousError(msg: String) = { @@ -4573,8 +4679,6 @@ trait Typers extends Modes with Adaptations with Tags { errorContainer = tree } - val fingerPrint: Long = name.fingerPrint - var defSym: Symbol = tree.symbol // the directly found symbol var pre: Type = NoPrefix // the prefix type of defSym, if a class member var qual: Tree = EmptyTree // the qualifier tree if transformed tree is a select @@ -4612,10 +4716,7 @@ trait Typers extends Modes with Adaptations with Tags { var cx = startingIdentContext while (defSym == NoSymbol && cx != NoContext && (cx.scope ne null)) { // cx.scope eq null arises during FixInvalidSyms in Duplicators pre = cx.enclClass.prefix - defEntry = { - val scope = cx.scope - if ((fingerPrint & scope.fingerPrints) != 0) scope.lookupEntry(name) else null - } + defEntry = cx.scope.lookupEntry(name) if ((defEntry ne null) && qualifies(defEntry.sym)) { // Right here is where SI-1987, overloading in package objects, can be // seen to go wrong. There is an overloaded symbol, but when referring @@ -4629,8 +4730,13 @@ trait Typers extends Modes with Adaptations with Tags { if (isInPackageObject(defEntry.sym, pre.typeSymbol)) { defSym = pre.member(defEntry.sym.name) if (defSym ne defEntry.sym) { - log("!!! Overloaded package object member resolved incorrectly.\n Discarded: " + - defEntry.sym.defString + "\n Using: " + defSym.defString) + qual = gen.mkAttributedQualifier(pre) + log(s""" + | !!! Overloaded package object member resolved incorrectly. + | prefix: $pre + | Discarded: ${defEntry.sym.defString} + | Using: ${defSym.defString} + """.stripMargin) } } else @@ -4803,7 +4909,18 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedCompoundTypeTree(templ: Template) = { + def typedIdentOrWildcard(tree: Ident) = { + val name = tree.name + if (Statistics.canEnable) Statistics.incCounter(typedIdentCount) + if ((name == nme.WILDCARD && (mode & (PATTERNmode | FUNmode)) == PATTERNmode) || + (name == tpnme.WILDCARD && (mode & TYPEmode) != 0)) + tree setType makeFullyDefined(pt) + else + typedIdent(tree, name) + } + + def typedCompoundTypeTree(tree: CompoundTypeTree) = { + val templ = tree.templ val parents1 = templ.parents mapConserve (typedType(_, mode)) if (parents1 exists (_.isErrorTyped)) tree setType ErrorType else { @@ -4816,7 +4933,9 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedAppliedTypeTree(tpt: Tree, args: List[Tree]) = { + def typedAppliedTypeTree(tree: AppliedTypeTree) = { + val tpt = tree.tpt + val args = tree.args val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) if (tpt1.isErrorTyped) { tpt1 @@ -4867,367 +4986,395 @@ trait Typers extends Modes with Adaptations with Tags { } } - // begin typed1 val sym: Symbol = tree.symbol if ((sym ne null) && (sym ne NoSymbol)) sym.initialize - //if (settings.debug.value && tree.isDef) log("typing definition of "+sym);//DEBUG - tree match { - case PackageDef(pid, stats) => - val pid1 = typedQualifier(pid).asInstanceOf[RefTree] - assert(sym.moduleClass ne NoSymbol, sym) - // complete lazy annotations - val annots = sym.annotations - val stats1 = newTyper(context.make(tree, sym.moduleClass, sym.info.decls)) - .typedStats(stats, NoSymbol) - treeCopy.PackageDef(tree, pid1, stats1) setType NoType - - case tree @ ClassDef(_, _, _, _) => - newTyper(context.makeNewScope(tree, sym)).typedClassDef(tree) - case tree @ ModuleDef(_, _, _) => - newTyper(context.makeNewScope(tree, sym.moduleClass)).typedModuleDef(tree) + def typedPackageDef(pdef: PackageDef) = { + val pid1 = typedQualifier(pdef.pid).asInstanceOf[RefTree] + assert(sym.moduleClass ne NoSymbol, sym) + // complete lazy annotations + val annots = sym.annotations + val stats1 = newTyper(context.make(tree, sym.moduleClass, sym.info.decls)) + .typedStats(pdef.stats, NoSymbol) + treeCopy.PackageDef(tree, pid1, stats1) setType NoType + } - case vdef @ ValDef(_, _, _, _) => - typedValDef(vdef) + def typedDocDef(docdef: DocDef) = { + val comment = docdef.comment + if (forScaladoc && (sym ne null) && (sym ne NoSymbol)) { + docComments(sym) = comment + comment.defineVariables(sym) + val typer1 = newTyper(context.makeNewScope(tree, context.owner)) + for (useCase <- comment.useCases) { + typer1.silent(_.typedUseCase(useCase)) match { + case SilentTypeError(err) => + unit.warning(useCase.pos, err.errMsg) + case _ => + } + for (useCaseSym <- useCase.defined) { + if (sym.name != useCaseSym.name) + unit.warning(useCase.pos, "@usecase " + useCaseSym.name.decode + " does not match commented symbol: " + sym.name.decode) + } + } + } + typed(docdef.definition, mode, pt) + } - case ddef @ DefDef(_, _, _, _, _, _) => - // flag default getters for constructors. An actual flag would be nice. See SI-5543. - //val flag = ddef.mods.hasDefaultFlag && ddef.mods.hasFlag(PRESUPER) - val flag = ddef.mods.hasDefaultFlag && sym.owner.isModuleClass && - nme.defaultGetterToMethod(sym.name) == nme.CONSTRUCTOR - newTyper(context.makeNewScope(tree, sym)).constrTyperIf(flag).typedDefDef(ddef) + def defDefTyper(ddef: DefDef) = { + val flag = ddef.mods.hasDefaultFlag && sym.owner.isModuleClass && + nme.defaultGetterToMethod(sym.name) == nme.CONSTRUCTOR + newTyper(context.makeNewScope(ddef, sym)).constrTyperIf(flag) + } - case tdef @ TypeDef(_, _, _, _) => - typedTypeDef(tdef) + def typedAlternative(alt: Alternative) = { + val alts1 = alt.trees mapConserve (alt => typed(alt, mode | ALTmode, pt)) + treeCopy.Alternative(tree, alts1) setType pt + } - case ldef @ LabelDef(_, _, _) => - labelTyper(ldef).typedLabelDef(ldef) - - case ddef @ DocDef(comment, defn) => - if (forScaladoc && (sym ne null) && (sym ne NoSymbol)) { - docComments(sym) = comment - comment.defineVariables(sym) - val typer1 = newTyper(context.makeNewScope(tree, context.owner)) - for (useCase <- comment.useCases) { - typer1.silent(_.typedUseCase(useCase)) match { - case SilentTypeError(err) => - unit.warning(useCase.pos, err.errMsg) - case _ => - } - for (useCaseSym <- useCase.defined) { - if (sym.name != useCaseSym.name) - unit.warning(useCase.pos, "@usecase " + useCaseSym.name.decode + " does not match commented symbol: " + sym.name.decode) + def typedStar(tree: Star) = { + if ((mode & STARmode) == 0 && !isPastTyper) + StarPatternWithVarargParametersError(tree) + treeCopy.Star(tree, typed(tree.elem, mode, pt)) setType makeFullyDefined(pt) + } + + def typedUnApply(tree: UnApply) = { + val fun1 = typed(tree.fun) + val tpes = formalTypes(unapplyTypeList(tree.fun.symbol, fun1.tpe, tree.args.length), tree.args.length) + val args1 = map2(tree.args, tpes)(typedPattern) + treeCopy.UnApply(tree, fun1, args1) setType pt + } + + def typedTry(tree: Try) = { + var block1 = typed(tree.block, pt) + var catches1 = typedCases(tree.catches, ThrowableClass.tpe, pt) + + for (cdef <- catches1 if cdef.guard.isEmpty) { + def warn(name: Name) = context.warning(cdef.pat.pos, s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning.") + def unbound(t: Tree) = t.symbol == null || t.symbol == NoSymbol + cdef.pat match { + case Bind(name, i @ Ident(_)) if unbound(i) => warn(name) + case i @ Ident(name) if unbound(i) => warn(name) + case _ => + } + } + + val finalizer1 = + if (tree.finalizer.isEmpty) tree.finalizer + else typed(tree.finalizer, UnitClass.tpe) + val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)), pt) + if (needAdapt) { + block1 = adapt(block1, mode, owntype) + catches1 = catches1 map (adaptCase(_, mode, owntype)) + } + + treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype + } + + def typedThrow(tree: Throw) = { + val expr1 = typed(tree.expr, EXPRmode | BYVALmode, ThrowableClass.tpe) + treeCopy.Throw(tree, expr1) setType NothingClass.tpe + } + + def typedTyped(tree: Typed) = { + val expr = tree.expr + val tpt = tree.tpt + tpt match { + case Function(List(), EmptyTree) => + // find out whether the programmer is trying to eta-expand a macro def + // to do that we need to typecheck the tree first (we need a symbol of the eta-expandee) + // that typecheck must not trigger macro expansions, so we explicitly prohibit them + // Q: "but, " - you may ask - ", `typed1` doesn't call adapt, which does macro expansion, so why explicit check?" + // A: solely for robustness reasons. this mechanism might change in the future, which might break unprotected code + val exprTyped = context.withMacrosDisabled(typed1(expr, mode, pt)) + exprTyped match { + case macroDef if macroDef.symbol != null && macroDef.symbol.isTermMacro && !macroDef.symbol.isErroneous => + MacroEtaError(exprTyped) + case _ => + typedEta(checkDead(exprTyped)) + } + + case Ident(tpnme.WILDCARD_STAR) => + val exprTyped = typed(expr, onlyStickyModes(mode), WildcardType) + def subArrayType(pt: Type) = + if (isPrimitiveValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt) + else { + val tparam = context.owner freshExistential "" setInfo TypeBounds.upper(pt) + newExistentialType(List(tparam), arrayType(tparam.tpe)) } + + val (exprAdapted, baseClass) = exprTyped.tpe.typeSymbol match { + case ArrayClass => (adapt(exprTyped, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) + case _ => (adapt(exprTyped, onlyStickyModes(mode), seqType(pt)), SeqClass) } - } - typed(defn, mode, pt) + exprAdapted.tpe.baseType(baseClass) match { + case TypeRef(_, _, List(elemtp)) => + treeCopy.Typed(tree, exprAdapted, tpt setType elemtp) setType elemtp + case _ => + setError(tree) + } + + case _ => + val tptTyped = typedType(tpt, mode) + val exprTyped = typed(expr, onlyStickyModes(mode), tptTyped.tpe.deconst) + val treeTyped = treeCopy.Typed(tree, exprTyped, tptTyped) - case Annotated(constr, arg) => - typedAnnotated(constr, typed(arg, mode, pt)) + if (isPatternMode) { + val uncheckedTypeExtractor = extractorForUncheckedType(tpt.pos, tptTyped.tpe) - case tree @ Block(_, _) => + // make fully defined to avoid bounded wildcard types that may be in pt from calling dropExistential (SI-2038) + val ptDefined = if (isFullyDefined(pt)) pt else makeFullyDefined(pt) + val ownType = inferTypedPattern(tptTyped, tptTyped.tpe, ptDefined, canRemedy = uncheckedTypeExtractor.nonEmpty) + treeTyped setType ownType + + uncheckedTypeExtractor match { + case None => treeTyped + case Some(extractor) => wrapClassTagUnapply(treeTyped, extractor, tptTyped.tpe) + } + } else + treeTyped setType tptTyped.tpe + } + } + + def typedTypeApply(tree: TypeApply) = { + val fun = tree.fun + val args = tree.args + // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) + //@M! we must type fun in order to type the args, as that requires the kinds of fun's type parameters. + // However, args should apparently be done first, to save context.undetparams. Unfortunately, the args + // *really* have to be typed *after* fun. We escape from this classic Catch-22 by simply saving&restoring undetparams. + + // @M TODO: the compiler still bootstraps&all tests pass when this is commented out.. + //val undets = context.undetparams + + // @M: fun is typed in TAPPmode because it is being applied to its actual type parameters + val fun1 = typed(fun, forFunMode(mode) | TAPPmode, WildcardType) + val tparams = fun1.symbol.typeParams + + //@M TODO: val undets_fun = context.undetparams ? + // "do args first" (by restoring the context.undetparams) in order to maintain context.undetparams on the function side. + + // @M TODO: the compiler still bootstraps when this is commented out.. TODO: run tests + //context.undetparams = undets + + // @M maybe the well-kindedness check should be done when checking the type arguments conform to the type parameters' bounds? + val args1 = if (sameLength(args, tparams)) map2Conserve(args, tparams) { + //@M! the polytype denotes the expected kind + (arg, tparam) => typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyClass.tpe)) + } + else { + //@M this branch is correctly hit for an overloaded polymorphic type. It also has to handle erroneous cases. + // Until the right alternative for an overloaded method is known, be very liberal, + // typedTypeApply will find the right alternative and then do the same check as + // in the then-branch above. (see pos/tcpoly_overloaded.scala) + // this assert is too strict: be tolerant for errors like trait A { def foo[m[x], g]=error(""); def x[g] = foo[g/*ERR: missing argument type*/] } + //assert(fun1.symbol.info.isInstanceOf[OverloadedType] || fun1.symbol.isError) //, (fun1.symbol,fun1.symbol.info,fun1.symbol.info.getClass,args,tparams)) + args mapConserve (typedHigherKindedType(_, mode)) + } + + //@M TODO: context.undetparams = undets_fun ? + Typer.this.typedTypeApply(tree, mode, fun1, args1) + } + + def typedApplyDynamic(tree: ApplyDynamic) = { + assert(phase.erasedTypes) + val reflectiveCalls = !(settings.refinementMethodDispatch.value == "invoke-dynamic") + val qual1 = typed(tree.qual, AnyRefClass.tpe) + val args1 = tree.args mapConserve (arg => if (reflectiveCalls) typed(arg, AnyRefClass.tpe) else typed(arg)) + treeCopy.ApplyDynamic(tree, qual1, args1) setType (if (reflectiveCalls) AnyRefClass.tpe else tree.symbol.info.resultType) + } + + def typedReferenceToBoxed(tree: ReferenceToBoxed) = { + val id = tree.ident + val id1 = typed1(id, mode, pt) match { case id: Ident => id } + // [Eugene] am I doing it right? + val erasedTypes = phaseId(currentPeriod) >= currentRun.erasurePhase.id + val tpe = capturedVariableType(id.symbol, erasedTypes = erasedTypes) + treeCopy.ReferenceToBoxed(tree, id1) setType tpe + } + + def typedLiteral(tree: Literal) = { + val value = tree.value + tree setType ( + if (value.tag == UnitTag) UnitClass.tpe + else ConstantType(value)) + } + + def typedSingletonTypeTree(tree: SingletonTypeTree) = { + val ref1 = checkStable( + typed(tree.ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe)) + tree setType ref1.tpe.resultType + } + + def typedSelectFromTypeTree(tree: SelectFromTypeTree) = { + val qual1 = typedType(tree.qualifier, mode) + if (qual1.tpe.isVolatile) TypeSelectionFromVolatileTypeError(tree, qual1) + else typedSelect(tree, qual1, tree.name) + } + + def typedTypeBoundsTree(tree: TypeBoundsTree) = { + val lo1 = typedType(tree.lo, mode) + val hi1 = typedType(tree.hi, mode) + treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe) + } + + def typedExistentialTypeTree(tree: ExistentialTypeTree) = { + val tree1 = typerWithLocalContext(context.makeNewScope(tree, context.owner)){ + _.typedExistentialTypeTree(tree, mode) + } + checkExistentialsFeature(tree1.pos, tree1.tpe, "the existential type") + tree1 + } + + def typedTypeTree(tree: TypeTree) = { + if (tree.original != null) + tree setType typedType(tree.original, mode).tpe + else + // we should get here only when something before failed + // and we try again (@see tryTypedApply). In that case we can assign + // whatever type to tree; we just have to survive until a real error message is issued. + tree setType AnyClass.tpe + } + + // begin typed1 + //if (settings.debug.value && tree.isDef) log("typing definition of "+sym);//DEBUG + + tree match { + case tree: Ident => + typedIdentOrWildcard(tree) + + case tree: Select => + typedSelectOrSuperCall(tree) + + case tree: Apply => + typedApply(tree) + + case tree: TypeTree => + typedTypeTree(tree) + + case tree: Literal => + typedLiteral(tree) + + case tree: This => + typedThis(tree) + + case tree: ValDef => + typedValDef(tree) + + case tree: DefDef => + // flag default getters for constructors. An actual flag would be nice. See SI-5543. + //val flag = ddef.mods.hasDefaultFlag && ddef.mods.hasFlag(PRESUPER) + defDefTyper(tree).typedDefDef(tree) + + case tree: Block => typerWithLocalContext(context.makeNewScope(tree, context.owner)){ _.typedBlock(tree, mode, pt) } - case Alternative(alts) => - val alts1 = alts mapConserve (alt => typed(alt, mode | ALTmode, pt)) - treeCopy.Alternative(tree, alts1) setType pt - - case Star(elem) => - if ((mode & STARmode) == 0 && !isPastTyper) - StarPatternWithVarargParametersError(tree) - treeCopy.Star(tree, typed(elem, mode, pt)) setType makeFullyDefined(pt) + case tree: If => + typedIf(tree) - case Bind(name, body) => - typedBind(name, body) + case tree: TypeApply => + typedTypeApply(tree) - case UnApply(fun, args) => - val fun1 = typed(fun) - val tpes = formalTypes(unapplyTypeList(fun.symbol, fun1.tpe, args.length), args.length) - val args1 = map2(args, tpes)(typedPattern) - treeCopy.UnApply(tree, fun1, args1) setType pt + case tree: AppliedTypeTree => + typedAppliedTypeTree(tree) - case ArrayValue(elemtpt, elems) => - typedArrayValue(elemtpt, elems) + case tree: Bind => + typedBind(tree) - case tree @ Function(_, _) => + case tree: Function => if (tree.symbol == NoSymbol) tree.symbol = context.owner.newAnonymousFunctionValue(tree.pos) typerWithLocalContext(context.makeNewScope(tree, tree.symbol))(_.typedFunction(tree, mode, pt)) + case tree: Match => + typedVirtualizedMatch(tree) + + case tree: New => + typedNew(tree) + case Assign(lhs, rhs) => typedAssign(lhs, rhs) case AssignOrNamedArg(lhs, rhs) => // called by NamesDefaults in silent typecheck typedAssign(lhs, rhs) - case If(cond, thenp, elsep) => - typedIf(cond, thenp, elsep) + case tree: Super => + typedSuper(tree) - case tree @ Match(selector, cases) => - typedVirtualizedMatch(tree, selector, cases) + case tree: TypeBoundsTree => + typedTypeBoundsTree(tree) - case Return(expr) => - typedReturn(expr) + case tree: Typed => + typedTyped(tree) - case Try(block, catches, finalizer) => - var block1 = typed(block, pt) - var catches1 = typedCases(catches, ThrowableClass.tpe, pt) + case tree: ClassDef => + newTyper(context.makeNewScope(tree, sym)).typedClassDef(tree) - for (cdef <- catches1 if cdef.guard.isEmpty) { - def warn(name: Name) = context.warning(cdef.pat.pos, s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning.") - def unbound(t: Tree) = t.symbol == null || t.symbol == NoSymbol - cdef.pat match { - case Bind(name, i@Ident(_)) if unbound(i) => warn(name) - case i@Ident(name) if unbound(i) => warn(name) - case _ => - } - } + case tree: ModuleDef => + newTyper(context.makeNewScope(tree, sym.moduleClass)).typedModuleDef(tree) - val finalizer1 = if (finalizer.isEmpty) finalizer - else typed(finalizer, UnitClass.tpe) - val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)), pt) - if (needAdapt) { - block1 = adapt(block1, mode, owntype) - catches1 = catches1 map (adaptCase(_, mode, owntype)) - } + case tree: TypeDef => + typedTypeDef(tree) - treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype - - case Throw(expr) => - val expr1 = typed(expr, EXPRmode | BYVALmode, ThrowableClass.tpe) - treeCopy.Throw(tree, expr1) setType NothingClass.tpe - - case New(tpt: Tree) => - typedNew(tpt) - - case Typed(expr, Function(List(), EmptyTree)) => - // find out whether the programmer is trying to eta-expand a macro def - // to do that we need to typecheck the tree first (we need a symbol of the eta-expandee) - // that typecheck must not trigger macro expansions, so we explicitly prohibit them - // Q: "but, " - you may ask - ", `typed1` doesn't call adapt, which does macro expansion, so why explicit check?" - // A: solely for robustness reasons. this mechanism might change in the future, which might break unprotected code - val expr1 = context.withMacrosDisabled(typed1(expr, mode, pt)) - expr1 match { - case macroDef if macroDef.symbol != null && macroDef.symbol.isTermMacro && !macroDef.symbol.isErroneous => - MacroEtaError(expr1) - case _ => - typedEta(checkDead(expr1)) - } + case tree: LabelDef => + labelTyper(tree).typedLabelDef(tree) - case Typed(expr0, tpt @ Ident(tpnme.WILDCARD_STAR)) => - val expr = typed(expr0, onlyStickyModes(mode), WildcardType) - def subArrayType(pt: Type) = - if (isPrimitiveValueClass(pt.typeSymbol) || !isFullyDefined(pt)) arrayType(pt) - else { - val tparam = context.owner freshExistential "" setInfo TypeBounds.upper(pt) - newExistentialType(List(tparam), arrayType(tparam.tpe)) - } + case tree: PackageDef => + typedPackageDef(tree) - val (expr1, baseClass) = expr.tpe.typeSymbol match { - case ArrayClass => (adapt(expr, onlyStickyModes(mode), subArrayType(pt)), ArrayClass) - case _ => (adapt(expr, onlyStickyModes(mode), seqType(pt)), SeqClass) - } - expr1.tpe.baseType(baseClass) match { - case TypeRef(_, _, List(elemtp)) => - treeCopy.Typed(tree, expr1, tpt setType elemtp) setType elemtp - case _ => - setError(tree) - } + case tree: DocDef => + typedDocDef(tree) - case Typed(expr, tpt) => - val tptTyped = typedType(tpt, mode) - val exprTyped = typed(expr, onlyStickyModes(mode), tptTyped.tpe.deconst) - val treeTyped = treeCopy.Typed(tree, exprTyped, tptTyped) + case tree: Annotated => + typedAnnotated(tree) - if (isPatternMode) { - val uncheckedTypeExtractor = extractorForUncheckedType(tpt.pos, tptTyped.tpe) + case tree: SingletonTypeTree => + typedSingletonTypeTree(tree) - // make fully defined to avoid bounded wildcard types that may be in pt from calling dropExistential (SI-2038) - val ptDefined = if (isFullyDefined(pt)) pt else makeFullyDefined(pt) - val ownType = inferTypedPattern(tptTyped, tptTyped.tpe, ptDefined, canRemedy = uncheckedTypeExtractor.nonEmpty) - treeTyped setType ownType + case tree: SelectFromTypeTree => + typedSelectFromTypeTree(tree) - uncheckedTypeExtractor match { - case None => treeTyped - case Some(extractor) => wrapClassTagUnapply(treeTyped, extractor, tptTyped.tpe) - } - } else - treeTyped setType tptTyped.tpe - - case TypeApply(fun, args) => - // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) - //@M! we must type fun in order to type the args, as that requires the kinds of fun's type parameters. - // However, args should apparently be done first, to save context.undetparams. Unfortunately, the args - // *really* have to be typed *after* fun. We escape from this classic Catch-22 by simply saving&restoring undetparams. - - // @M TODO: the compiler still bootstraps&all tests pass when this is commented out.. - //val undets = context.undetparams - - // @M: fun is typed in TAPPmode because it is being applied to its actual type parameters - val fun1 = typed(fun, forFunMode(mode) | TAPPmode, WildcardType) - val tparams = fun1.symbol.typeParams - - //@M TODO: val undets_fun = context.undetparams ? - // "do args first" (by restoring the context.undetparams) in order to maintain context.undetparams on the function side. - - // @M TODO: the compiler still bootstraps when this is commented out.. TODO: run tests - //context.undetparams = undets - - // @M maybe the well-kindedness check should be done when checking the type arguments conform to the type parameters' bounds? - val args1 = if (sameLength(args, tparams)) map2Conserve(args, tparams) { - //@M! the polytype denotes the expected kind - (arg, tparam) => typedHigherKindedType(arg, mode, GenPolyType(tparam.typeParams, AnyClass.tpe)) - } else { - //@M this branch is correctly hit for an overloaded polymorphic type. It also has to handle erroneous cases. - // Until the right alternative for an overloaded method is known, be very liberal, - // typedTypeApply will find the right alternative and then do the same check as - // in the then-branch above. (see pos/tcpoly_overloaded.scala) - // this assert is too strict: be tolerant for errors like trait A { def foo[m[x], g]=error(""); def x[g] = foo[g/*ERR: missing argument type*/] } - //assert(fun1.symbol.info.isInstanceOf[OverloadedType] || fun1.symbol.isError) //, (fun1.symbol,fun1.symbol.info,fun1.symbol.info.getClass,args,tparams)) - args mapConserve (typedHigherKindedType(_, mode)) - } + case tree: CompoundTypeTree => + typedCompoundTypeTree(tree) - //@M TODO: context.undetparams = undets_fun ? - typedTypeApply(tree, mode, fun1, args1) + case tree: ExistentialTypeTree => + typedExistentialTypeTree(tree) - case Apply(Block(stats, expr), args) => - typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) + case tree: Return => + typedReturn(tree) - case Apply(fun, args) => - typedApply(fun, args) match { - case Apply(Select(New(tpt), name), args) - if (tpt.tpe != null && - tpt.tpe.typeSymbol == ArrayClass && - args.length == 1 && - erasure.GenericArray.unapply(tpt.tpe).isDefined) => // !!! todo simplify by using extractor - // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len) - // convert new Array^N[T](len) for N > 1 to evidence[ClassTag[Array[...Array[T]...]]].newArray(len), where Array HK gets applied (N-1) times - // no more MaxArrayDims. ClassTags are flexible enough to allow creation of arrays of arbitrary dimensionality (w.r.t JVM restrictions) - val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe) - val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.toTypeConstructor, List(tpe))).last - val newArrayApp = atPos(tree.pos) { - val tag = resolveClassTag(tree.pos, tagType) - if (tag.isEmpty) MissingClassTagError(tree, tagType) - else new ApplyToImplicitArgs(Select(tag, nme.newArray), args) - } - typed(newArrayApp, mode, pt) - case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => //SI-5696 - TooManyArgumentListsForConstructor(tree) - case tree1 => - tree1 - } + case tree: Try => + typedTry(tree) - case ApplyDynamic(qual, args) => - assert(phase.erasedTypes) - val reflectiveCalls = !(settings.refinementMethodDispatch.value == "invoke-dynamic") - val qual1 = typed(qual, AnyRefClass.tpe) - val args1 = args mapConserve (arg => if (reflectiveCalls) typed(arg, AnyRefClass.tpe) else typed(arg)) - treeCopy.ApplyDynamic(tree, qual1, args1) setType (if (reflectiveCalls) AnyRefClass.tpe else tree.symbol.info.resultType) - - case Super(qual, mix) => - typedSuper(qual, mix) - - case This(qual) => - typedThis(qual) - - case Select(qual @ Super(_, _), nme.CONSTRUCTOR) => - val qual1 = - typed(qual, EXPRmode | QUALmode | POLYmode | SUPERCONSTRmode, WildcardType) - // the qualifier type of a supercall constructor is its first parent class - typedSelect(qual1, nme.CONSTRUCTOR) - - case Select(qual, name) => - Statistics.incCounter(typedSelectCount) - var qual1 = checkDead(typedQualifier(qual, mode)) - if (name.isTypeName) qual1 = checkStable(qual1) - - val tree1 = // temporarily use `filter` and an alternative for `withFilter` - if (name == nme.withFilter) - silent(_ => typedSelect(qual1, name)) match { - case SilentResultValue(result) => - result - case _ => - silent(_ => typed1(Select(qual1, nme.filter) setPos tree.pos, mode, pt)) match { - case SilentResultValue(result2) => - unit.deprecationWarning( - tree.pos, "`withFilter' method does not yet exist on "+qual1.tpe.widen+ - ", using `filter' method instead") - result2 - case SilentTypeError(err) => - WithFilterError(tree, err) - } - } - else - typedSelect(qual1, name) + case tree: Throw => + typedThrow(tree) - if (tree.isInstanceOf[PostfixSelect]) - checkFeature(tree.pos, PostfixOpsFeature, name.decode) - if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember) - checkFeature(tree1.pos, ReflectiveCallsFeature, tree1.symbol.toString) + case tree: Alternative => + typedAlternative(tree) - if (qual1.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name) - else tree1 + case tree: Star => + typedStar(tree) - case Ident(name) => - Statistics.incCounter(typedIdentCount) - if ((name == nme.WILDCARD && (mode & (PATTERNmode | FUNmode)) == PATTERNmode) || - (name == tpnme.WILDCARD && (mode & TYPEmode) != 0)) - tree setType makeFullyDefined(pt) - else - typedIdent(name) - - case ReferenceToBoxed(idt @ Ident(_)) => - val id1 = typed1(idt, mode, pt) match { case id: Ident => id } - val tpe = capturedVariableType(idt.symbol, erasedTypes = phase.erasedTypes) - treeCopy.ReferenceToBoxed(tree, id1) setType tpe - - case Literal(value) => - tree setType ( - if (value.tag == UnitTag) UnitClass.tpe - else ConstantType(value)) - - case SingletonTypeTree(ref) => - val ref1 = checkStable( - typed(ref, EXPRmode | QUALmode | (mode & TYPEPATmode), AnyRefClass.tpe)) - tree setType ref1.tpe.resultType - - case SelectFromTypeTree(qual, selector) => - val qual1 = typedType(qual, mode) - if (qual1.tpe.isVolatile) TypeSelectionFromVolatileTypeError(tree, qual1) - else typedSelect(qual1, selector) - - case CompoundTypeTree(templ) => - typedCompoundTypeTree(templ) - - case AppliedTypeTree(tpt, args) => - typedAppliedTypeTree(tpt, args) - - case TypeBoundsTree(lo, hi) => - val lo1 = typedType(lo, mode) - val hi1 = typedType(hi, mode) - treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe) - - case etpt @ ExistentialTypeTree(_, _) => - val tree1 = typerWithLocalContext(context.makeNewScope(tree, context.owner)){ - _.typedExistentialTypeTree(etpt, mode) - } - checkExistentialsFeature(tree1.pos, tree1.tpe, "the existential type") - tree1 + case tree: UnApply => + typedUnApply(tree) - case dc@TypeTreeWithDeferredRefCheck() => dc // TODO: should we re-type the wrapped tree? then we need to change TypeTreeWithDeferredRefCheck's representation to include the wrapped tree explicitly (instead of in its closure) - case tpt @ TypeTree() => - if (tpt.original != null) - tree setType typedType(tpt.original, mode).tpe - else - // we should get here only when something before failed - // and we try again (@see tryTypedApply). In that case we can assign - // whatever type to tree; we just have to survive until a real error message is issued. - tree setType AnyClass.tpe - case Import(expr, selectors) => + case tree: ArrayValue => + typedArrayValue(tree) + + case tree: ApplyDynamic => + typedApplyDynamic(tree) + + case tree: ReferenceToBoxed => + typedReferenceToBoxed(tree) + + case tree: TypeTreeWithDeferredRefCheck => + tree // TODO: should we re-type the wrapped tree? then we need to change TypeTreeWithDeferredRefCheck's representation to include the wrapped tree explicitly (instead of in its closure) + + case tree: Import => assert(forInteractive, "!forInteractive") // should not happen in normal circumstances. tree setType tree.symbol.tpe + case _ => abort("unexpected tree: " + tree.getClass + "\n" + tree)//debug } @@ -5244,8 +5391,8 @@ trait Typers extends Modes with Adaptations with Tags { indentTyping() var alreadyTyped = false - val startByType = Statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) - Statistics.incCounter(visitsByType, tree.getClass) + val startByType = if (Statistics.canEnable) Statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null + if (Statistics.canEnable) Statistics.incCounter(visitsByType, tree.getClass) try { if (context.retyping && (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< pt))) { @@ -5300,7 +5447,7 @@ trait Typers extends Modes with Adaptations with Tags { } finally { deindentTyping() - Statistics.popTimer(byTypeStack, startByType) + if (Statistics.canEnable) Statistics.popTimer(byTypeStack, startByType) } } @@ -5454,7 +5601,7 @@ trait Typers extends Modes with Adaptations with Tags { val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous)) val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty - if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImpl(ddef, tree.symbol, tree1.symbol) else AnyClass.tpe + if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImpl(ddef, tree1.symbol) else AnyClass.tpe } def transformedOr(tree: Tree, op: => Tree): Tree = transformed.get(tree) match { diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 4871ef199c..5db1863f67 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -115,7 +115,7 @@ trait Unapplies extends ast.TreeDSL Modifiers(OVERRIDE | FINAL), nme.toString_, Nil, - List(Nil), + ListOfNil, TypeTree(), Literal(Constant(cdef.name.decode))) @@ -126,7 +126,7 @@ trait Unapplies extends ast.TreeDSL ModuleDef( Modifiers(cdef.mods.flags & AccessFlags | SYNTHETIC, cdef.mods.privateWithin), cdef.name.toTermName, - Template(parents, emptyValDef, NoMods, Nil, List(Nil), body, cdef.impl.pos.focus)) + Template(parents, emptyValDef, NoMods, Nil, ListOfNil, body, cdef.impl.pos.focus)) } private val caseMods = Modifiers(SYNTHETIC | CASE) diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala index f84877cccb..07c972899e 100644 --- a/src/compiler/scala/tools/reflect/FastTrack.scala +++ b/src/compiler/scala/tools/reflect/FastTrack.scala @@ -17,16 +17,11 @@ trait FastTrack { private implicit def context2taggers(c0: MacroContext): Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } = new { val c: c0.type = c0 } with MacroImplementations - implicit def fastTrackEntry2MacroRuntime(entry: FastTrackEntry): MacroRuntime = args => entry.run(args) + implicit def fastTrackEntry2MacroRuntime(entry: FastTrackEntry): MacroRuntime = args => entry.run(args.c) type FastTrackExpander = PartialFunction[(MacroContext, Tree), Tree] case class FastTrackEntry(sym: Symbol, expander: FastTrackExpander) { - def validate(argss: List[List[Any]]): Boolean = { - val c = argss.flatten.apply(0).asInstanceOf[MacroContext] - val isValid = expander isDefinedAt (c, c.expandee) - isValid - } - def run(args: List[Any]): Any = { - val c = args(0).asInstanceOf[MacroContext] + def validate(c: MacroContext): Boolean = expander.isDefinedAt((c, c.expandee)) + def run(c: MacroContext): Any = { val result = expander((c, c.expandee)) c.Expr[Nothing](result)(c.AbsTypeTag.Nothing) } diff --git a/src/library/scala/Function.scala b/src/library/scala/Function.scala index 270581a3aa..d470f4c966 100644 --- a/src/library/scala/Function.scala +++ b/src/library/scala/Function.scala @@ -28,11 +28,11 @@ object Function { /** Turns a function `A => Option[B]` into a `PartialFunction[A, B]`. * - * TODO: check if the paragraph below is still correct * '''Important note''': this transformation implies the original function - * will be called 2 or more times on each logical invocation, because the + * may be called 2 or more times on each logical invocation, because the * only way to supply an implementation of `isDefinedAt` is to call the * function and examine the return value. + * See also [[scala.PartialFunction]], method `applyOrElse`. * * @param f a function `T => Option[R]` * @return a partial function defined for those inputs where diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala index f651461fe6..b7420f4447 100644 --- a/src/library/scala/Option.scala +++ b/src/library/scala/Option.scala @@ -266,7 +266,7 @@ sealed abstract class Option[+A] extends Product with Serializable { * if it is nonempty, or the empty list if the $option is empty. */ def toList: List[A] = - if (isEmpty) List() else List(this.get) + if (isEmpty) List() else new ::(this.get, Nil) /** Returns a [[scala.util.Left]] containing the given * argument `left` if this $option is empty, or diff --git a/src/library/scala/PartialFunction.scala b/src/library/scala/PartialFunction.scala index 7154b8da34..d0a339bdd5 100644 --- a/src/library/scala/PartialFunction.scala +++ b/src/library/scala/PartialFunction.scala @@ -67,7 +67,7 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * of this partial function and `that`. The resulting partial function * takes `x` to `this(x)` where `this` is defined, and to `that(x)` where it is not. */ - def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) : PartialFunction[A1, B1] = + def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = new OrElse[A1, B1] (this, that) //TODO: why not overload it with orElse(that: F1): F1? @@ -78,10 +78,8 @@ trait PartialFunction[-A, +B] extends (A => B) { self => * @return a partial function with the same domain as this partial function, which maps * arguments `x` to `k(this(x))`. */ - override def andThen[C](k: B => C) : PartialFunction[A, C] = new PartialFunction[A, C] { - def isDefinedAt(x: A): Boolean = self isDefinedAt x - def apply(x: A): C = k(self(x)) - } + override def andThen[C](k: B => C): PartialFunction[A, C] = + new AndThen[A, B, C] (this, k) /** Turns this partial function into an plain function returning an `Option` result. * @see Function.unlift @@ -90,28 +88,54 @@ trait PartialFunction[-A, +B] extends (A => B) { self => */ def lift: A => Option[B] = new Lifted(this) - /** - * TODO: comment + /** Applies this partial function to the given argument when it is contained in the function domain. + * Applies fallback function where this partial function is not defined. + * + * Note that expression `pf.applyOrElse(x, default)` is equivalent to + * {{{ if(pf isDefinedAt x) pf(x) else default(x) }}} + * except that `applyOrElse` method can be implemented more efficiently. + * For all partial function literals compiler generates `applyOrElse` implementation which + * avoids double evaluation of pattern matchers and guards. + * This makes `applyOrElse` the basis for the efficient implementation for many operations and scenarios, such as: + * + * - combining partial functions into `orElse`/`andThen` chains does not lead to + * excessive `apply`/`isDefinedAt` evaluation + * - `lift` and `unlift` do not evaluate source functions twice on each invocation + * - `runWith` allows efficient imperative-style combining of partial functions + * with conditionally applied actions + * + * For non-literal partial function classes with nontrivial `isDefinedAt` method + * it is recommended to override `applyOrElse` with custom implementation that avoids + * double `isDefinedAt` evaluation. This may result in better performance + * and more predictable behavior w.r.t. side effects. + * + * @param x the function argument + * @param default the fallback function + * @return the result of this function or fallback function application. * @since 2.10 */ def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = if (isDefinedAt(x)) apply(x) else default(x) - /** - * TODO: comment - * @since 2.10 - */ - def run[U](x: A)(action: B => U): Boolean = - applyOrElse(x, fallbackToken) match { - case FallbackToken => false - case z => action(z); true - } - - /** - * TODO: comment + /** Composes this partial function with an action function which + * gets applied to results of this partial function. + * The action function is invoked only for its side effects; its result is ignored. + * + * Note that expression `pf.runWith(action)(x)` is equivalent to + * {{{ if(pf isDefinedAt x) { action(pf(x)); true } else false }}} + * except that `runWith` is implemented via `applyOrElse` and thus potentially more efficient. + * Using `runWith` avoids double evaluation of pattern matchers and guards for partial function literals. + * @see `applyOrElse`. + * + * @param action the action function + * @return a function which maps arguments `x` to `isDefinedAt(x)`. The resulting function + * runs `action(this(x))` where `this` is defined. * @since 2.10 */ - def runWith[U](action: B => U): A => Boolean = { x => run(x)(action) } + def runWith[U](action: B => U): A => Boolean = { x => + val z = applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) { action(z); true } else false + } } /** A few handy operations which leverage the extra bit of information @@ -137,11 +161,10 @@ object PartialFunction { def apply(x: A): B = f1.applyOrElse(x, f2) - override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = - f1.applyOrElse(x, fallbackToken) match { - case FallbackToken => f2.applyOrElse(x, default) - case z => z - } + override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = { + val z = f1.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) z else f2.applyOrElse(x, default) + } override def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]) = new OrElse[A1, B1] (f1, f2 orElse that) @@ -150,23 +173,61 @@ object PartialFunction { new OrElse[A, C] (f1 andThen k, f2 andThen k) } - private[scala] lazy val FallbackToken: PartialFunction[Any, PartialFunction[Any, Nothing]] = { case _ => FallbackToken.asInstanceOf[PartialFunction[Any, Nothing]] } - private[scala] final def fallbackToken[B] = FallbackToken.asInstanceOf[PartialFunction[Any, B]] - //TODO: check generated code for PF literal here + /** Composite function produced by `PartialFunction#andThen` method + */ + private final class AndThen[-A, B, +C] (pf: PartialFunction[A, B], k: B => C) extends PartialFunction[A, C] { + def isDefinedAt(x: A) = pf.isDefinedAt(x) + + def apply(x: A): C = k(pf(x)) + + override def applyOrElse[A1 <: A, C1 >: C](x: A1, default: A1 => C1): C1 = { + val z = pf.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) k(z) else default(x) + } + } + + /** To implement patterns like {{{ if(pf isDefinedAt x) f1(pf(x)) else f2(x) }}} efficiently + * the following trick is used: + * + * To avoid double evaluation of pattern matchers & guards `applyOrElse` method is used here + * instead of `isDefinedAt`/`apply` pair. + * + * After call to `applyOrElse` we need both the function result it returned and + * the fact if the function's argument was contained in its domain. The only degree of freedom we have here + * to achieve this goal is tweaking with the continuation argument (`default`) of `applyOrElse` method. + * The obvious way is to throw an exception from `default` function and to catch it after + * calling `applyOrElse` but I consider this somewhat inefficient. + * + * I know only one way how you can do this task efficiently: `default` function should return unique marker object + * which never may be returned by any other (regular/partial) function. This way after calling `applyOrElse` you need + * just one reference comparison to distinguish if `pf isDefined x` or not. + * + * This correctly interacts with specialization as return type of `applyOrElse` + * (which is parameterized upper bound) can never be specialized. + * + * Here `fallback_pf` is used as both unique marker object and special fallback function that returns it. + */ + private[this] final val fallback_pf: PartialFunction[Any, Any] = { case _ => fallback_pf } + @inline private final def checkFallback[B] = fallback_pf.asInstanceOf[PartialFunction[Any, B]] + @inline private final def fallbackOccurred[B](x: B) = (fallback_pf eq x.asInstanceOf[AnyRef]) - private[scala] final class Lifted[-A, +B] (val pf: PartialFunction[A, B]) + private final class Lifted[-A, +B] (val pf: PartialFunction[A, B]) extends runtime.AbstractFunction1[A, Option[B]] { - def apply(x: A): Option[B] = pf.applyOrElse(x, fallbackToken) match { - case FallbackToken => None - case z => Some(z) + def apply(x: A): Option[B] = { + val z = pf.applyOrElse(x, checkFallback[B]) + if (!fallbackOccurred(z)) Some(z) else None } } private final class Unlifted[A, B] (f: A => Option[B]) extends runtime.AbstractPartialFunction[A, B] { def isDefinedAt(x: A): Boolean = f(x).isDefined - override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = - f(x) getOrElse default(x) //TODO: check generated code and inline getOrElse if needed + + override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = { + val z = f(x) + if (!z.isEmpty) z.get else default(x) + } + override def lift = f } @@ -178,7 +239,6 @@ object PartialFunction { /** Converts ordinary function to partial one * @since 2.10 */ - //TODO: check generated code for PF literal here def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) } private[this] final val constFalse: Any => Boolean = { _ => false} @@ -189,12 +249,11 @@ object PartialFunction { override def orElse[A1, B1](that: PartialFunction[A1, B1]) = that override def andThen[C](k: Nothing => C) = this override val lift = (x: Any) => None - override def run[U](x: Any)(action: Nothing => U) = false override def runWith[U](action: Nothing => U) = constFalse } - /** - * TODO: comment + /** The partial function with empty domain. + * Any attempt to invoke empty partial function leads to throwing [[scala.MatchError]] exception. * @since 2.10 */ def empty[A, B] : PartialFunction[A, B] = empty_pf diff --git a/src/library/scala/collection/GenTraversableLike.scala b/src/library/scala/collection/GenTraversableLike.scala index 903de4a247..9b04256c8d 100644 --- a/src/library/scala/collection/GenTraversableLike.scala +++ b/src/library/scala/collection/GenTraversableLike.scala @@ -65,14 +65,14 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with * @throws `NoSuchElementException` if the $coll is empty. */ def head: A - + /** Optionally selects the first element. * $orderDependent * @return the first element of this $coll if it is nonempty, * `None` if it is empty. */ def headOption: Option[A] - + /** Tests whether this $coll can be repeatedly traversed. * @return `true` */ @@ -92,14 +92,14 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with * @throws NoSuchElementException If the $coll is empty. */ def last: A - + /** Optionally selects the last element. * $orderDependent * @return the last element of this $coll$ if it is nonempty, * `None` if it is empty. */ def lastOption: Option[A] - + /** Selects all elements except the last. * $orderDependent * @return a $coll consisting of all elements of this $coll @@ -107,7 +107,7 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with * @throws `UnsupportedOperationException` if the $coll is empty. */ def init: Repr - + /** Computes a prefix scan of the elements of the collection. * * Note: The neutral element `z` may be applied more than once. @@ -210,7 +210,7 @@ trait GenTraversableLike[+A, +Repr] extends Any with GenTraversableOnce[A] with def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That /** Builds a new collection by applying a function to all elements of this $coll - * and using the elements of the resulting collections. + * and using the elements of the resulting collections. * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. diff --git a/src/library/scala/collection/JavaConversions.scala b/src/library/scala/collection/JavaConversions.scala index a978a9a783..8e4fdf537d 100644 --- a/src/library/scala/collection/JavaConversions.scala +++ b/src/library/scala/collection/JavaConversions.scala @@ -22,7 +22,8 @@ import convert._ * scala.collection.mutable.Buffer <=> java.util.List * scala.collection.mutable.Set <=> java.util.Set * scala.collection.mutable.Map <=> java.util.{ Map, Dictionary } - * scala.collection.mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap + * scala.collection.mutable.ConcurrentMap (deprecated since 2.10) <=> java.util.concurrent.ConcurrentMap + * scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap *}}} * In all cases, converting from a source type to a target type and back * again will return the original source object, eg. diff --git a/src/library/scala/collection/LinearSeqOptimized.scala b/src/library/scala/collection/LinearSeqOptimized.scala index 5e0bd010a6..188e0e8afd 100755 --- a/src/library/scala/collection/LinearSeqOptimized.scala +++ b/src/library/scala/collection/LinearSeqOptimized.scala @@ -82,17 +82,16 @@ trait LinearSeqOptimized[+A, +Repr <: LinearSeqOptimized[A, Repr]] extends Linea false } - override /*TraversableLike*/ - def count(p: A => Boolean): Int = { + override /*SeqLike*/ + def contains(elem: Any): Boolean = { var these = this - var cnt = 0 while (!these.isEmpty) { - if (p(these.head)) cnt += 1 + if (these.head == elem) return true these = these.tail } - cnt + false } - + override /*IterableLike*/ def find(p: A => Boolean): Option[A] = { var these = this @@ -113,7 +112,7 @@ trait LinearSeqOptimized[+A, +Repr <: LinearSeqOptimized[A, Repr]] extends Linea } acc } - + override /*IterableLike*/ def foldRight[B](z: B)(f: (A, B) => B): B = if (this.isEmpty) z diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 0345f05355..b2051bf209 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -235,14 +235,19 @@ trait TraversableLike[+A, +Repr] extends Any (that ++ seq)(breakOut) def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { - val b = bf(repr) - b.sizeHint(this) + def builder = { // extracted to keep method size under 35 bytes, so that it can be JIT-inlined + val b = bf(repr) + b.sizeHint(this) + b + } + val b = builder for (x <- this) b += f(x) b.result } def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { - val b = bf(repr) + def builder = bf(repr) // extracted to keep method size under 35 bytes, so that it can be JIT-inlined + val b = builder for (x <- this) b ++= f(x).seq b.result } @@ -266,7 +271,12 @@ trait TraversableLike[+A, +Repr] extends Any * @return a new $coll consisting of all elements of this $coll that do not satisfy the given * predicate `p`. The order of the elements is preserved. */ - def filterNot(p: A => Boolean): Repr = filter(!p(_)) + def filterNot(p: A => Boolean): Repr = { + val b = newBuilder + for (x <- this) + if (!p(x)) b += x + b.result + } def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) diff --git a/src/library/scala/collection/convert/WrapAsScala.scala b/src/library/scala/collection/convert/WrapAsScala.scala index 49f4d7cd99..c2994a0986 100644 --- a/src/library/scala/collection/convert/WrapAsScala.scala +++ b/src/library/scala/collection/convert/WrapAsScala.scala @@ -13,7 +13,27 @@ import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } import Wrappers._ import language.implicitConversions -trait WrapAsScala { +trait LowPriorityWrapAsScala { + this: WrapAsScala => + /** + * Implicitly converts a Java ConcurrentMap to a Scala mutable ConcurrentMap. + * The returned Scala ConcurrentMap is backed by the provided Java + * ConcurrentMap and any side-effects of using it via the Scala interface will + * be visible via the Java interface and vice versa. + * + * If the Java ConcurrentMap was previously obtained from an implicit or + * explicit call of `asConcurrentMap(scala.collection.mutable.ConcurrentMap)` + * then the original Scala ConcurrentMap will be returned. + * + * @param m The ConcurrentMap to be converted. + * @return A Scala mutable ConcurrentMap view of the argument. + */ + @deprecated("Use `mapAsScalaConcurrentMap` instead, and use `concurrent.Map` instead of `ConcurrentMap`.", "2.10.0") + implicit def mapAsScalaDeprecatedConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): mutable.ConcurrentMap[A, B] = + asScalaConcurrentMap(m) +} + +trait WrapAsScala extends LowPriorityWrapAsScala { /** * Implicitly converts a Java `Iterator` to a Scala `Iterator`. * diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala index 0b297aeb45..b41327ed95 100644 --- a/src/library/scala/collection/immutable/HashMap.scala +++ b/src/library/scala/collection/immutable/HashMap.scala @@ -146,6 +146,29 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { private object EmptyHashMap extends HashMap[Any, Nothing] { } + // utility method to create a HashTrieMap from two leaf HashMaps (HashMap1 or HashMapCollision1) with non-colliding hash code) + private def makeHashTrieMap[A, B](hash0:Int, elem0:HashMap[A, B], hash1:Int, elem1:HashMap[A, B], level:Int, size:Int) : HashTrieMap[A, B] = { + val index0 = (hash0 >>> level) & 0x1f + val index1 = (hash1 >>> level) & 0x1f + if(index0 != index1) { + val bitmap = (1 << index0) | (1 << index1) + val elems = new Array[HashMap[A,B]](2) + if(index0 < index1) { + elems(0) = elem0 + elems(1) = elem1 + } else { + elems(0) = elem1 + elems(1) = elem0 + } + new HashTrieMap[A, B](bitmap, elems, size) + } else { + val elems = new Array[HashMap[A,B]](1) + val bitmap = (1 << index0) + elems(0) = makeHashTrieMap(hash0, elem0, hash1, elem1, level + 5, size) + new HashTrieMap[A, B](bitmap, elems, size) + } + } + // TODO: add HashMap2, HashMap3, ... class HashMap1[A,+B](private[collection] val key: A, private[collection] val hash: Int, private[collection] val value: (B @uV), private[collection] var kv: (A,B @uV)) extends HashMap[A,B] { @@ -183,30 +206,10 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { new HashMap1(nkv._1, hash, nkv._2, nkv) } } else { - var thatindex = (hash >>> level) & 0x1f - var thisindex = (this.hash >>> level) & 0x1f if (hash != this.hash) { // they have different hashes, but may collide at this level - find a level at which they don't - var lvl = level - var top: HashTrieMap[A, B1] = null - var prev: HashTrieMap[A, B1] = null - while (thisindex == thatindex) { - val newlevel = new HashTrieMap[A, B1](1 << thisindex, new Array[HashMap[A, B1]](1), 2) - if (prev ne null) prev.elems(0) = newlevel else top = newlevel - prev = newlevel - lvl += 5 - thatindex = (hash >>> lvl) & 0x1f - thisindex = (this.hash >>> lvl) & 0x1f - } - val bottelems = new Array[HashMap[A,B1]](2) - val ind = if (thisindex < thatindex) 1 else 0 - bottelems(1 - ind) = this - bottelems(ind) = new HashMap1[A, B1](key, hash, value, kv) - val bottom = new HashTrieMap[A,B1]((1 << thisindex) | (1 << thatindex), bottelems, 2) - if (prev ne null) { - prev.elems(0) = bottom - top - } else bottom + val that = new HashMap1[A, B1](key, hash, value, kv) + makeHashTrieMap[A,B1](this.hash, this, hash, that, level, 2) } else { // 32-bit hash collision (rare, but not impossible) new HashMapCollision1(hash, ListMap.empty.updated(this.key,this.value).updated(key,value)) @@ -221,12 +224,13 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { // this method may be called multiple times in a multithreaded environment, but that's ok private[HashMap] def ensurePair: (A,B) = if (kv ne null) kv else { kv = (key, value); kv } protected override def merge0[B1 >: B](that: HashMap[A, B1], level: Int, merger: Merger[A, B1]): HashMap[A, B1] = { - that.updated0(key, hash, level, value, kv, if (merger ne null) merger.invert else null) + that.updated0(key, hash, level, value, kv, merger.invert) } } private[collection] class HashMapCollision1[A, +B](private[collection] val hash: Int, val kvs: ListMap[A, B @uV]) extends HashMap[A, B @uV] { + // assert(kvs.size > 1) override def size = kvs.size @@ -238,20 +242,20 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { if ((merger eq null) || !kvs.contains(key)) new HashMapCollision1(hash, kvs.updated(key, value)) else new HashMapCollision1(hash, kvs + merger((key, kvs(key)), kv)) } else { - var m: HashMap[A,B1] = new HashTrieMap[A,B1](0,new Array[HashMap[A,B1]](0),0) - // might be able to save some ops here, but it doesn't seem to be worth it - for ((k,v) <- kvs) - m = m.updated0(k, this.hash, level, v, null, merger) - m.updated0(key, hash, level, value, kv, merger) + val that = new HashMap1(key, hash, value, kv) + makeHashTrieMap(this.hash, this, hash, that, level, size + 1) } override def removed0(key: A, hash: Int, level: Int): HashMap[A, B] = if (hash == this.hash) { val kvs1 = kvs - key - if (!kvs1.isEmpty) - new HashMapCollision1(hash, kvs1) - else + if (kvs1.isEmpty) HashMap.empty[A,B] + else if(kvs1.tail.isEmpty) { + val kv = kvs1.head + new HashMap1[A,B](kv._1,hash,kv._2,kv) + } else + new HashMapCollision1(hash, kvs1) } else this override def iterator: Iterator[(A,B)] = kvs.iterator @@ -275,6 +279,9 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { private[collection] val size0: Int ) extends HashMap[A, B @uV] { + // assert(Integer.bitCount(bitmap) == elems.length) + // assert(elems.length > 1 || (elems.length == 1 && elems(0).isInstanceOf[HashTrieMap[_,_]])) + /* def this (level: Int, m1: HashMap1[A,B], m2: HashMap1[A,B]) = { this(((m1.hash >>> level) & 0x1f) | ((m2.hash >>> level) & 0x1f), { @@ -347,9 +354,14 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { Array.copy(elems, 0, elemsNew, 0, offset) Array.copy(elems, offset + 1, elemsNew, offset, elems.length - offset - 1) val sizeNew = size - sub.size - new HashTrieMap(bitmapNew, elemsNew, sizeNew) + if (elemsNew.length == 1 && !elemsNew(0).isInstanceOf[HashTrieMap[_,_]]) + elemsNew(0) + else + new HashTrieMap(bitmapNew, elemsNew, sizeNew) } else HashMap.empty[A,B] + } else if(elems.length == 1 && !subNew.isInstanceOf[HashTrieMap[_,_]]) { + subNew } else { val elemsNew = new Array[HashMap[A,B]](elems.length) Array.copy(elems, 0, elemsNew, 0, elems.length) @@ -480,7 +492,7 @@ time { mNew.iterator.foreach( p => ()) } } new HashTrieMap[A, B1](this.bitmap | that.bitmap, merged, totalelems) - case hm: HashMapCollision1[_, _] => that.merge0(this, level, if (merger ne null) merger.invert else null) + case hm: HashMapCollision1[_, _] => that.merge0(this, level, merger.invert) case hm: HashMap[_, _] => this case _ => sys.error("section supposed to be unreachable.") } diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 74dc385f99..87b58005cf 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -302,6 +302,15 @@ sealed abstract class List[+A] extends AbstractSeq[A] if (isEmpty) Stream.Empty else new Stream.Cons(head, tail.toStream) + @inline override final + def foreach[B](f: A => B) { + var these = this + while (!these.isEmpty) { + f(these.head) + these = these.tail + } + } + @deprecated("use `distinct` instead", "2.8.0") def removeDuplicates: List[A] = distinct } @@ -378,7 +387,6 @@ final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extend while (!xs.isEmpty) { out.writeObject(xs.head); xs = xs.tail } out.writeObject(ListSerializeEnd) } - } /** $factoryInfo diff --git a/src/library/scala/collection/immutable/ListMap.scala b/src/library/scala/collection/immutable/ListMap.scala index 091443f909..c21032603f 100644 --- a/src/library/scala/collection/immutable/ListMap.scala +++ b/src/library/scala/collection/immutable/ListMap.scala @@ -121,12 +121,12 @@ extends AbstractMap[A, B] def hasNext = !self.isEmpty def next(): (A,B) = if (!hasNext) throw new NoSuchElementException("next on empty iterator") - else { val res = (self.key, self.value); self = self.next; res } + else { val res = (self.key, self.value); self = self.tail; res } }.toList.reverseIterator protected def key: A = throw new NoSuchElementException("empty map") protected def value: B = throw new NoSuchElementException("empty map") - protected def next: ListMap[A, B] = throw new NoSuchElementException("empty map") + override def tail: ListMap[A, B] = throw new NoSuchElementException("empty map") /** This class represents an entry in the `ListMap`. */ @@ -140,7 +140,7 @@ extends AbstractMap[A, B] override def size: Int = size0(this, 0) // to allow tail recursion and prevent stack overflows - @tailrec private def size0(cur: ListMap[A, B1], acc: Int): Int = if (cur.isEmpty) acc else size0(cur.next, acc + 1) + @tailrec private def size0(cur: ListMap[A, B1], acc: Int): Int = if (cur.isEmpty) acc else size0(cur.tail, acc + 1) /** Is this an empty map? * @@ -157,7 +157,7 @@ extends AbstractMap[A, B] */ override def apply(k: A): B1 = apply0(this, k) - @tailrec private def apply0(cur: ListMap[A, B1], k: A): B1 = if (k == cur.key) cur.value else apply0(cur.next, k) + @tailrec private def apply0(cur: ListMap[A, B1], k: A): B1 = if (k == cur.key) cur.value else apply0(cur.tail, k) /** Checks if this map maps `key` to a value and return the * value if it exists. @@ -169,7 +169,7 @@ extends AbstractMap[A, B] @tailrec private def get0(cur: ListMap[A, B1], k: A): Option[B1] = if (k == cur.key) Some(cur.value) - else if (cur.next.nonEmpty) get0(cur.next, k) else None + else if (cur.tail.nonEmpty) get0(cur.tail, k) else None /** This method allows one to create a new map with an additional mapping * from `key` to `value`. If the map contains already a mapping for `key`, @@ -198,7 +198,7 @@ extends AbstractMap[A, B] var lst: List[(A, B1)] = Nil while (cur.nonEmpty) { if (k != cur.key) lst ::= ((cur.key, cur.value)) - cur = cur.next + cur = cur.tail } var acc = ListMap[A, B1]() while (lst != Nil) { @@ -211,6 +211,6 @@ extends AbstractMap[A, B] } - override protected def next: ListMap[A, B1] = ListMap.this + override def tail: ListMap[A, B1] = ListMap.this } } diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index 1bf1e20694..97707d4f7c 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -479,22 +479,40 @@ self => final class StreamWithFilter(p: A => Boolean) extends WithFilter(p) { override def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Stream[A], B, That]): That = { - def tailMap = asStream[B](tail withFilter p map f) - if (isStreamBuilder(bf)) asThat( - if (isEmpty) Stream.Empty - else if (p(head)) cons(f(head), tailMap) - else tailMap - ) + def tailMap(coll: Stream[A]): Stream[B] = { + var head: A = null.asInstanceOf[A] + var tail: Stream[A] = coll + while (true) { + if (tail.isEmpty) + return Stream.Empty + head = tail.head + tail = tail.tail + if (p(head)) + return cons(f(head), tailMap(tail)) + } + throw new RuntimeException() + } + + if (isStreamBuilder(bf)) asThat(tailMap(Stream.this)) else super.map(f)(bf) } override def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Stream[A], B, That]): That = { - def tailFlatMap = asStream[B](tail withFilter p flatMap f) - if (isStreamBuilder(bf)) asThat( - if (isEmpty) Stream.Empty - else if (p(head)) f(head).toStream append tailFlatMap - else tailFlatMap - ) + def tailFlatMap(coll: Stream[A]): Stream[B] = { + var head: A = null.asInstanceOf[A] + var tail: Stream[A] = coll + while (true) { + if (tail.isEmpty) + return Stream.Empty + head = tail.head + tail = tail.tail + if (p(head)) + return f(head).toStream append tailFlatMap(tail) + } + throw new RuntimeException() + } + + if (isStreamBuilder(bf)) asThat(tailFlatMap(Stream.this)) else super.flatMap(f)(bf) } diff --git a/src/library/scala/collection/mutable/Builder.scala b/src/library/scala/collection/mutable/Builder.scala index bbf4f5889d..b6887df61e 100644 --- a/src/library/scala/collection/mutable/Builder.scala +++ b/src/library/scala/collection/mutable/Builder.scala @@ -62,9 +62,27 @@ trait Builder[-Elem, +To] extends Growable[Elem] { * wrong, i.e. a different number of elements is added. * * @param coll the collection which serves as a hint for the result's size. + */ + def sizeHint(coll: TraversableLike[_, _]) { + if (coll.isInstanceOf[collection.IndexedSeqLike[_,_]]) { + sizeHint(coll.size) + } + } + + /** Gives a hint that one expects the `result` of this builder + * to have the same size as the given collection, plus some delta. This will + * provide a hint only if the collection is known to have a cheap + * `size` method. Currently this is assumed to be the case if and only if + * the collection is of type `IndexedSeqLike`. + * Some builder classes + * will optimize their representation based on the hint. However, + * builder implementations are still required to work correctly even if the hint is + * wrong, i.e. a different number of elements is added. + * + * @param coll the collection which serves as a hint for the result's size. * @param delta a correction to add to the `coll.size` to produce the size hint. */ - def sizeHint(coll: TraversableLike[_, _], delta: Int = 0) { + def sizeHint(coll: TraversableLike[_, _], delta: Int) { if (coll.isInstanceOf[collection.IndexedSeqLike[_,_]]) { sizeHint(coll.size + delta) } diff --git a/src/library/scala/collection/mutable/DoubleLinkedList.scala b/src/library/scala/collection/mutable/DoubleLinkedList.scala index cba4e9725e..b7c5f07502 100644 --- a/src/library/scala/collection/mutable/DoubleLinkedList.scala +++ b/src/library/scala/collection/mutable/DoubleLinkedList.scala @@ -63,6 +63,13 @@ class DoubleLinkedList[A]() extends AbstractSeq[A] } override def companion: GenericCompanion[DoubleLinkedList] = DoubleLinkedList + + // Accurately clone this collection. See SI-6296 + override def clone(): DoubleLinkedList[A] = { + val builder = newBuilder + builder ++= this + builder.result + } } /** $factoryInfo diff --git a/src/library/scala/collection/mutable/Queue.scala b/src/library/scala/collection/mutable/Queue.scala index 2aa19d6cb0..21c3a84699 100644 --- a/src/library/scala/collection/mutable/Queue.scala +++ b/src/library/scala/collection/mutable/Queue.scala @@ -32,6 +32,7 @@ import generic._ */ class Queue[A] extends MutableList[A] + with LinearSeqOptimized[A, Queue[A]] with GenericTraversableTemplate[A, Queue] with Cloneable[Queue[A]] with Serializable @@ -165,6 +166,17 @@ extends MutableList[A] * @return the first element. */ def front: A = head + + + // TODO - Don't override this just for new to create appropriate type.... + override def tail: Queue[A] = { + require(nonEmpty, "tail of empty list") + val tl = new Queue[A] + tl.first0 = first0.tail + tl.last0 = last0 + tl.len = len - 1 + tl + } } diff --git a/src/library/scala/collection/parallel/immutable/ParHashMap.scala b/src/library/scala/collection/parallel/immutable/ParHashMap.scala index ad882390c8..c9876c4d74 100644 --- a/src/library/scala/collection/parallel/immutable/ParHashMap.scala +++ b/src/library/scala/collection/parallel/immutable/ParHashMap.scala @@ -202,7 +202,7 @@ extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], (K, V), Has def groupByKey[Repr](cbf: () => Combiner[V, Repr]): ParHashMap[K, Repr] = { val bucks = buckets.filter(_ != null).map(_.headPtr) val root = new Array[HashMap[K, AnyRef]](bucks.length) - + combinerTaskSupport.executeAndWaitResult(new CreateGroupedTrie(cbf, bucks, root, 0, bucks.length)) var bitmap = 0 @@ -306,8 +306,7 @@ extends collection.parallel.BucketCombiner[(K, V), ParHashMap[K, V], (K, V), Has unrolled = unrolled.next } - evaluateCombiners(trie) - trie.asInstanceOf[HashMap[K, Repr]] + evaluateCombiners(trie).asInstanceOf[HashMap[K, Repr]] } private def evaluateCombiners(trie: HashMap[K, Combiner[V, Repr]]): HashMap[K, Repr] = trie match { case hm1: HashMap.HashMap1[_, _] => diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala index 7b6df6e31c..5255c44f10 100644 --- a/src/library/scala/reflect/ClassTag.scala +++ b/src/library/scala/reflect/ClassTag.scala @@ -64,6 +64,7 @@ trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serial } object ClassTag { + private val NothingTYPE = classOf[scala.runtime.Nothing$] private val NullTYPE = classOf[scala.runtime.Null$] private val ObjectTYPE = classOf[java.lang.Object] @@ -80,13 +81,7 @@ object ClassTag { val Object : ClassTag[java.lang.Object] = new ClassTag[java.lang.Object]{ def runtimeClass = ObjectTYPE; private def readResolve() = ClassTag.Object } val AnyVal : ClassTag[scala.AnyVal] = ClassTag.Object.asInstanceOf[ClassTag[scala.AnyVal]] val AnyRef : ClassTag[scala.AnyRef] = ClassTag.Object.asInstanceOf[ClassTag[scala.AnyRef]] - val Nothing : ClassTag[scala.Nothing] = new ClassTag[scala.Nothing]{ - def runtimeClass = throw new Exception("Nothing is a bottom type, therefore its erasure does not return a value") - private def readResolve() = ClassTag.Nothing - override def equals(x: Any) = x.isInstanceOf[ClassTag[_]] && (x.asInstanceOf[AnyRef] eq ClassTag.Nothing) - override def hashCode = System.identityHashCode(this) - override def toString = "ClassTag[Nothing]" - } + val Nothing : ClassTag[scala.Nothing] = new ClassTag[scala.Nothing]{ def runtimeClass = NothingTYPE; private def readResolve() = ClassTag.Nothing } val Null : ClassTag[scala.Null] = new ClassTag[scala.Null]{ def runtimeClass = NullTYPE; private def readResolve() = ClassTag.Null } def apply[T](runtimeClass1: jClass[_]): ClassTag[T] = diff --git a/src/library/scala/reflect/base/Base.scala b/src/library/scala/reflect/base/Base.scala index 798e257d1e..28ebdf4377 100644 --- a/src/library/scala/reflect/base/Base.scala +++ b/src/library/scala/reflect/base/Base.scala @@ -487,6 +487,7 @@ class Base extends Universe { self => } case object EmptyTree extends TermTree { + val asList = List(this) override def isEmpty = true } diff --git a/src/library/scala/reflect/base/Symbols.scala b/src/library/scala/reflect/base/Symbols.scala index 294fa19d62..3830264425 100644 --- a/src/library/scala/reflect/base/Symbols.scala +++ b/src/library/scala/reflect/base/Symbols.scala @@ -107,8 +107,8 @@ trait Symbols { self: Universe => /** Does this symbol represent the definition of a type? * Note that every symbol is either a term or a type. - * So for every symbol `sym`, either `sym.isTerm` is true - * or `sym.isType` is true. + * So for every symbol `sym` (except for `NoSymbol`), + * either `sym.isTerm` is true or `sym.isType` is true. */ def isType: Boolean = false @@ -118,9 +118,9 @@ trait Symbols { self: Universe => def asType: TypeSymbol = throw new ScalaReflectionException(s"$this is not a type") /** Does this symbol represent the definition of a term? - * Note that every symbol is either a term or a term. - * So for every symbol `sym`, either `sym.isTerm` is true - * or `sym.isTerm` is true. + * Note that every symbol is either a term or a type. + * So for every symbol `sym` (except for `NoSymbol`), + * either `sym.isTerm` is true or `sym.isTerm` is true. */ def isTerm: Boolean = false @@ -234,10 +234,10 @@ trait Symbols { self: Universe => * `PolyType(ClassInfoType(...))` that describes type parameters, value * parameters, parent types, and members of `C`. */ - def toType: Type + def toType: Type - override def isType = true - override def asType = this + final override def isType = true + final override def asType = this } /** The base API that all term symbols support */ diff --git a/src/library/scala/reflect/base/TypeTags.scala b/src/library/scala/reflect/base/TypeTags.scala index c9d1ccf5bc..b7e0c37a4b 100644 --- a/src/library/scala/reflect/base/TypeTags.scala +++ b/src/library/scala/reflect/base/TypeTags.scala @@ -250,9 +250,11 @@ trait TypeTags { self: Universe => } // incantations + def absTypeTag[T](implicit attag: AbsTypeTag[T]) = attag def typeTag[T](implicit ttag: TypeTag[T]) = ttag // big thanks to Viktor Klang for this brilliant idea! + def absTypeOf[T](implicit attag: AbsTypeTag[T]): Type = attag.tpe def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe } diff --git a/src/library/scala/runtime/AbstractPartialFunction.scala b/src/library/scala/runtime/AbstractPartialFunction.scala index f499350ce9..c1f245590b 100644 --- a/src/library/scala/runtime/AbstractPartialFunction.scala +++ b/src/library/scala/runtime/AbstractPartialFunction.scala @@ -8,7 +8,8 @@ package scala.runtime -/** `AbstractPartialFunction` reformulates all operations of its supertrait `PartialFunction` in terms of `isDefinedAt` and `applyOrElse`. +/** `AbstractPartialFunction` reformulates all operations of its supertrait `PartialFunction` + * in terms of `isDefinedAt` and `applyOrElse`. * * This allows more efficient implementations in many cases: * - optimized `orElse` method supports chained `orElse` in linear time, @@ -16,12 +17,7 @@ package scala.runtime * - optimized `lift` method helps to avoid double evaluation of pattern matchers & guards * of partial function literals. * - * This trait is used as a basis for implementation of all partial function literals - * with non-exhaustive matchers. - * - * Use of `AbstractPartialFunction` instead of `PartialFunction` as a base trait for - * user-defined partial functions may result in better performance - * and more predictable behavior w.r.t. side effects. + * This trait is used as a basis for implementation of all partial function literals. * * @author Pavel Pavlov * @since 2.10 @@ -35,34 +31,4 @@ abstract class AbstractPartialFunction[@specialized(scala.Int, scala.Long, scala // probably okay to make final since classes compiled before have overridden against the old version of AbstractPartialFunction // let's not make it final so as not to confuse anyone /*final*/ def apply(x: T1): R = applyOrElse(x, PartialFunction.empty) - - @annotation.unspecialized override final def andThen[C](k: R => C) : PartialFunction[T1, C] = - new AbstractPartialFunction[T1, C] { - def isDefinedAt(x: T1): Boolean = self.isDefinedAt(x) - override def applyOrElse[A1 <: T1, C1 >: C](x: A1, default: A1 => C1): C1 = - self.applyOrElse(x, PartialFunction.fallbackToken) match { - case PartialFunction.FallbackToken => default(x) - case z => k(z) - } - } - - // TODO: remove - protected def missingCase(x: T1): R = throw new MatchError(x) -} - - -/** `AbstractTotalFunction` is a partial function whose `isDefinedAt` method always returns `true`. - * - * This class is used as base class for partial function literals with - * certainly exhaustive pattern matchers. - * - * @author Pavel Pavlov - * @since 2.10 - */ -abstract class AbstractTotalFunction[@specialized(scala.Int, scala.Long, scala.Float, scala.Double, scala.AnyRef) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double, scala.AnyRef) +R] extends Function1[T1, R] with PartialFunction[T1, R] { - final def isDefinedAt(x: T1): Boolean = true - @annotation.unspecialized override final def applyOrElse[A1 <: T1, B1 >: R](x: A1, default: A1 => B1): B1 = apply(x) - @annotation.unspecialized override final def orElse[A1 <: T1, B1 >: R](that: PartialFunction[A1, B1]): PartialFunction[A1, B1] = this - //TODO: check generated code for PF literal here - @annotation.unspecialized override final def andThen[C](k: R => C): PartialFunction[T1, C] = { case x => k(apply(x)) } } diff --git a/src/library/scala/util/control/Exception.scala b/src/library/scala/util/control/Exception.scala index 2ee053c92b..1567e06c22 100644 --- a/src/library/scala/util/control/Exception.scala +++ b/src/library/scala/util/control/Exception.scala @@ -6,7 +6,8 @@ ** |/ ** \* */ -package scala.util.control +package scala.util +package control import collection.immutable.List import reflect.{ ClassTag, classTag } @@ -25,6 +26,10 @@ import language.implicitConversions * val x2 = catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s) * }}} * + * This class differs from `scala.util.Try` in that it focuses on composing exception handlers rather than + * composing behavior. All behavior should be composed first and fed to a `Catch` object using one of the + * `opt` or `either` methods. + * * @author Paul Phillips */ @@ -118,6 +123,11 @@ object Exception { */ def either[U >: T](body: => U): Either[Throwable, U] = toEither(Right(body)) + /** Apply this catch logic to the supplied body, mapping the result + * into Try[T] - Failure if an exception was caught, Success(T) otherwise. + */ + def withTry[U >: T](body: => U): scala.util.Try[U] = toTry(Success(body)) + /** Create a `Catch` object with the same `isDefinedAt` logic as this one, * but with the supplied `apply` method replacing the current one. */ def withApply[U](f: Throwable => U): Catch[U] = { @@ -131,35 +141,11 @@ object Exception { /** Convenience methods. */ def toOption: Catch[Option[T]] = withApply(_ => None) def toEither: Catch[Either[Throwable, T]] = withApply(Left(_)) - } - - /** A container class for Try logic */ - class Try[+T] private[Exception](body: => T, val catcher: Catch[T]) { - /** Execute "body" using catch/finally logic "catcher" */ - def apply(): T = catcher(body) - def apply[U >: T](other: => U): U = catcher(other) - - /** As apply, but map caught exceptions to `None` and success to `Some(T)`. */ - def opt(): Option[T] = catcher opt body - def opt[U >: T](other: => U): Option[U] = catcher opt other - - /** As apply, but map caught exceptions to `Left(ex)` and success to Right(x) */ - def either(): Either[Throwable, T] = catcher either body - def either[U >: T](other: => U): Either[Throwable, U] = catcher either other - - /** Create a `Try` object with the supplied body replacing the current body. */ - def tryInstead[U >: T](other: => U) = new Try(other, catcher) - - /** Create a `Try` object with the supplied logic appended to the existing Catch logic. */ - def or[U >: T](pf: Catcher[U]) = new Try(body, catcher or pf) - - /** Create a `Try`object with the supplied code appended to the existing `Finally`. */ - def andFinally(fin: => Unit) = new Try(body, catcher andFinally fin) - - override def toString() = List("Try(<body>)", catcher.toString) mkString " " + def toTry: Catch[scala.util.Try[T]] = withApply(x => Failure(x)) } final val nothingCatcher: Catcher[Nothing] = mkThrowableCatcher(_ => false, throw _) + final def nonFatalCatcher[T]: Catcher[T] = mkThrowableCatcher({ case NonFatal(_) => true; case _ => false }, throw _) final def allCatcher[T]: Catcher[T] = mkThrowableCatcher(_ => true, throw _) /** The empty `Catch` object. */ @@ -168,6 +154,9 @@ object Exception { /** A `Catch` object which catches everything. */ final def allCatch[T]: Catch[T] = new Catch(allCatcher[T]) withDesc "<everything>" + /** A `Catch` object witch catches non-fatal exceptions. */ + final def nonFatalCatch[T]: Catch[T] = new Catch(nonFatalCatcher[T]) withDesc "<non-fatal>" + /** Creates a `Catch` object which will catch any of the supplied exceptions. * Since the returned `Catch` object has no specific logic defined and will simply * rethrow the exceptions it catches, you will typically want to call `opt` or diff --git a/src/library/scala/util/hashing/MurmurHash3.scala b/src/library/scala/util/hashing/MurmurHash3.scala index 3efd5b5e72..1cfb8276fe 100644 --- a/src/library/scala/util/hashing/MurmurHash3.scala +++ b/src/library/scala/util/hashing/MurmurHash3.scala @@ -157,6 +157,20 @@ private[hashing] class MurmurHash3 { // Finalization finalizeHash(h, data.length) } + + final def listHash(xs: collection.immutable.List[_], seed: Int): Int = { + var n = 0 + var h = seed + var elems = xs + while (!elems.isEmpty) { + val head = elems.head + val tail = elems.tail + h = mix(h, head.##) + n += 1 + elems = tail + } + finalizeHash(h, n) + } } /** @@ -199,7 +213,11 @@ object MurmurHash3 extends MurmurHash3 { /** To offer some potential for optimization. */ - def seqHash(xs: collection.Seq[_]): Int = orderedHash(xs, seqSeed) + def seqHash(xs: collection.Seq[_]): Int = xs match { + case xs: List[_] => listHash(xs, seqSeed) + case xs => orderedHash(xs, seqSeed) + } + def mapHash(xs: collection.Map[_, _]): Int = unorderedHash(xs, mapSeed) def setHash(xs: collection.Set[_]): Int = unorderedHash(xs, setSeed) diff --git a/src/library/scala/util/parsing/combinator/Parsers.scala b/src/library/scala/util/parsing/combinator/Parsers.scala index 66e0a496d8..eaaea583ce 100644 --- a/src/library/scala/util/parsing/combinator/Parsers.scala +++ b/src/library/scala/util/parsing/combinator/Parsers.scala @@ -155,14 +155,20 @@ trait Parsers { val successful = true } - private lazy val lastNoSuccess = new DynamicVariable[Option[NoSuccess]](None) + private lazy val lastNoSuccessVar = new DynamicVariable[Option[NoSuccess]](None) + + @deprecated("lastNoSuccess was not thread-safe and will be removed in 2.11.0", "2.10.0") + def lastNoSuccess: NoSuccess = lastNoSuccessVar.value.orNull + + @deprecated("lastNoSuccess was not thread-safe and will be removed in 2.11.0", "2.10.0") + def lastNoSuccess_=(x: NoSuccess): Unit = lastNoSuccessVar.value = Option(x) /** A common super-class for unsuccessful parse results. */ sealed abstract class NoSuccess(val msg: String, override val next: Input) extends ParseResult[Nothing] { // when we don't care about the difference between Failure and Error val successful = false - if (lastNoSuccess.value map { v => !(next.pos < v.next.pos) } getOrElse true) - lastNoSuccess.value = Some(this) + if (lastNoSuccessVar.value forall (v => !(next.pos < v.next.pos))) + lastNoSuccessVar.value = Some(this) def map[U](f: Nothing => U) = this def mapPartial[U](f: PartialFunction[Nothing, U], error: Nothing => String): ParseResult[U] = this @@ -881,14 +887,14 @@ trait Parsers { * if `p` consumed all the input. */ def phrase[T](p: Parser[T]) = new Parser[T] { - def apply(in: Input) = lastNoSuccess.withValue(None) { + def apply(in: Input) = lastNoSuccessVar.withValue(None) { p(in) match { case s @ Success(out, in1) => if (in1.atEnd) s else - lastNoSuccess.value filterNot { _.next.pos < in1.pos } getOrElse Failure("end of input expected", in1) - case ns => lastNoSuccess.value.getOrElse(ns) + lastNoSuccessVar.value filterNot { _.next.pos < in1.pos } getOrElse Failure("end of input expected", in1) + case ns => lastNoSuccessVar.value.getOrElse(ns) } } } diff --git a/src/reflect/scala/reflect/api/FlagSets.scala b/src/reflect/scala/reflect/api/FlagSets.scala index 36836e84a9..fdd43f1883 100644 --- a/src/reflect/scala/reflect/api/FlagSets.scala +++ b/src/reflect/scala/reflect/api/FlagSets.scala @@ -3,7 +3,7 @@ package api import scala.language.implicitConversions -trait FlagSets { self: Universe => +trait FlagSets extends base.FlagSets { self: Universe => type FlagSet diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala index 7d185d9879..8c4c423221 100644 --- a/src/reflect/scala/reflect/api/Mirrors.scala +++ b/src/reflect/scala/reflect/api/Mirrors.scala @@ -1,7 +1,7 @@ package scala.reflect package api -trait Mirrors { self: Universe => +trait Mirrors extends base.Mirrors { self: Universe => type RuntimeClass >: Null diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 9e7b3c9712..b17377795b 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -59,6 +59,9 @@ trait Symbols extends base.Symbols { self: Universe => */ def typeSignature: Type + /** Returns all symbols overriden by this symbol. */ + def allOverriddenSymbols: List[Symbol] + /******************* tests *******************/ /** Does this symbol represent a synthetic (i.e. a compiler-generated) entity? @@ -197,9 +200,6 @@ trait Symbols extends base.Symbols { self: Universe => /** ... */ def suchThat(cond: Symbol => Boolean): Symbol - - /** The string discriminator of this symbol; useful for debugging */ - def kind: String } /** The API of term symbols */ diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala index ebaedd7ac3..f22f8d3e75 100644 --- a/src/reflect/scala/reflect/api/Types.scala +++ b/src/reflect/scala/reflect/api/Types.scala @@ -66,6 +66,10 @@ trait Types extends base.Types { self: Universe => /** Does this type conform to given type argument `that`? */ def <:< (that: Type): Boolean + /** Is this type a weak subtype of that type? True also for numeric types, i.e. Int weak_<:< Long. + */ + def weak_<:<(that: Type): Boolean + /** Is this type equivalent to given type argument `that`? */ def =:= (that: Type): Boolean @@ -155,9 +159,6 @@ trait Types extends base.Types { self: Universe => /** Does this type contain a reference to given symbol? */ def contains(sym: Symbol): Boolean - - /** The string discriminator of this type; useful for debugging */ - def kind: String } /** .. */ diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 229570dafd..a444c786f7 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -8,6 +8,7 @@ package internal import util._ import pickling.ByteCodecs +import scala.annotation.tailrec /** AnnotationInfo and its helpers */ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => @@ -31,11 +32,27 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => case AnnotationInfo(tp, Literal(Constant(tpe: Type)) :: Nil, _) if tp.typeSymbol == ThrowsClass => tpe.typeSymbol } - /** Test for, get, or remove an annotation */ - def hasAnnotation(cls: Symbol) = annotations exists (_ matches cls) - def getAnnotation(cls: Symbol) = annotations find (_ matches cls) + /** Tests for, get, or remove an annotation */ + def hasAnnotation(cls: Symbol): Boolean = + //OPT inlined from exists to save on #closures; was: annotations exists (_ matches cls) + dropOtherAnnotations(annotations, cls).nonEmpty + + def getAnnotation(cls: Symbol): Option[AnnotationInfo] = + //OPT inlined from exists to save on #closures; was: annotations find (_ matches cls) + dropOtherAnnotations(annotations, cls) match { + case ann :: _ => Some(ann) + case _ => None + } + def removeAnnotation(cls: Symbol): Self = filterAnnotations(ann => !(ann matches cls)) + final def withAnnotation(annot: AnnotationInfo): Self = withAnnotations(List(annot)) + + @tailrec private + def dropOtherAnnotations(anns: List[AnnotationInfo], cls: Symbol): List[AnnotationInfo] = anns match { + case ann :: rest => if (ann matches cls) anns else dropOtherAnnotations(rest, cls) + case Nil => Nil + } } /** Arguments to classfile annotations (which are written to diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index fbee906b7b..554b3bfca6 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -39,8 +39,8 @@ trait BaseTypeSeqs { */ class BaseTypeSeq protected[BaseTypeSeqs] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { self => - Statistics.incCounter(baseTypeSeqCount) - Statistics.incCounter(baseTypeSeqLenTotal, elems.length) + if (Statistics.canEnable) Statistics.incCounter(baseTypeSeqCount) + if (Statistics.canEnable) Statistics.incCounter(baseTypeSeqLenTotal, elems.length) /** The number of types in the sequence */ def length: Int = elems.length diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index fcbe7d0ed9..98d42b724c 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -137,9 +137,8 @@ trait Definitions extends api.StandardDefinitions { lazy val BooleanTpe = BooleanClass.toTypeConstructor lazy val ScalaNumericValueClasses = ScalaValueClasses filterNot Set[Symbol](UnitClass, BooleanClass) - - def ScalaValueClassesNoUnit = ScalaValueClasses filterNot (_ eq UnitClass) - def ScalaValueClasses: List[ClassSymbol] = List( + lazy val ScalaValueClassesNoUnit = ScalaValueClasses filterNot (_ eq UnitClass) + lazy val ScalaValueClasses: List[ClassSymbol] = List( UnitClass, BooleanClass, ByteClass, @@ -381,6 +380,7 @@ trait Definitions extends api.StandardDefinitions { def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass def isRepeatedParamType(tp: Type) = isScalaRepeatedParamType(tp) || isJavaRepeatedParamType(tp) + def isRepeated(param: Symbol) = isRepeatedParamType(param.tpe) def isCastSymbol(sym: Symbol) = sym == Any_asInstanceOf || sym == Object_asInstanceOf def isJavaVarArgsMethod(m: Symbol) = m.isMethod && isJavaVarArgs(m.info.params) @@ -462,7 +462,7 @@ trait Definitions extends api.StandardDefinitions { def ReflectRuntimeUniverse = if (ReflectRuntimePackage != NoSymbol) getMemberValue(ReflectRuntimePackage, nme.universe) else NoSymbol def ReflectRuntimeCurrentMirror = if (ReflectRuntimePackage != NoSymbol) getMemberMethod(ReflectRuntimePackage, nme.currentMirror) else NoSymbol - lazy val PartialManifestClass = getMemberType(ReflectPackage, tpnme.ClassManifest) + lazy val PartialManifestClass = getTypeMember(ReflectPackage, tpnme.ClassManifest) lazy val PartialManifestModule = requiredModule[scala.reflect.ClassManifestFactory.type] lazy val FullManifestClass = requiredClass[scala.reflect.Manifest[_]] lazy val FullManifestModule = requiredModule[scala.reflect.ManifestFactory.type] @@ -494,7 +494,7 @@ trait Definitions extends api.StandardDefinitions { lazy val MacroContextClass = getClassIfDefined("scala.reflect.macros.Context") // defined in scala-reflect.jar, so we need to be careful def MacroContextPrefix = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.prefix) else NoSymbol - def MacroContextPrefixType = if (MacroContextClass != NoSymbol) getMemberType(MacroContextClass, tpnme.PrefixType) else NoSymbol + def MacroContextPrefixType = if (MacroContextClass != NoSymbol) getTypeMember(MacroContextClass, tpnme.PrefixType) else NoSymbol def MacroContextUniverse = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.universe) else NoSymbol def MacroContextMirror = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.mirror) else NoSymbol lazy val MacroImplAnnotation = requiredClass[scala.reflect.macros.internal.macroImpl] @@ -1012,10 +1012,10 @@ trait Definitions extends api.StandardDefinitions { case _ => fatalMissingSymbol(owner, name, "member object") } } - def getMemberType(owner: Symbol, name: Name): TypeSymbol = { + def getTypeMember(owner: Symbol, name: Name): TypeSymbol = { getMember(owner, name.toTypeName) match { case x: TypeSymbol => x - case _ => fatalMissingSymbol(owner, name, "member type") + case _ => fatalMissingSymbol(owner, name, "type member") } } def getMemberClass(owner: Symbol, name: Name): ClassSymbol = { diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala index 25441f9812..4b3eb0cdc4 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -3,7 +3,7 @@ package internal import scala.collection.mutable.WeakHashMap // SI-6241: move importers to a mirror -trait Importers { self: SymbolTable => +trait Importers extends api.Importers { self: SymbolTable => def mkImporter(from0: api.Universe): Importer { val from: from0.type } = ( if (self eq from0) { @@ -238,7 +238,7 @@ trait Importers { self: SymbolTable => case from.AntiPolyType(pre, targs) => AntiPolyType(importType(pre), targs map importType) case x: from.TypeVar => - TypeVar(importType(x.origin), importTypeConstraint(x.constr0), x.typeArgs map importType, x.params map importSymbol) + TypeVar(importType(x.origin), importTypeConstraint(x.constr), x.typeArgs map importType, x.params map importSymbol) case from.NotNullType(tpe) => NotNullType(importType(tpe)) case from.AnnotatedType(annots, tpe, selfsym) => @@ -448,4 +448,4 @@ trait Importers { self: SymbolTable => case _ => constant.value }) } -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index 835a46f05d..2fdf27d847 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -415,9 +415,6 @@ trait Names extends api.Names with LowPriorityNames { } else toString } - - @inline - final def fingerPrint: Long = (1L << start) /** TODO - find some efficiency. */ def append(ch: Char) = newName("" + this + ch) diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index ed75b5d855..385e45997b 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -43,13 +43,8 @@ trait Scopes extends api.Scopes { self: SymbolTable => */ class Scope protected[Scopes] (initElems: ScopeEntry = null, initFingerPrints: Long = 0L) extends ScopeBase with MemberScopeBase { - /** A bitset containing the last 6 bits of the start value of every name - * stored in this scope. - */ - var fingerPrints: Long = initFingerPrints - protected[Scopes] def this(base: Scope) = { - this(base.elems, base.fingerPrints) + this(base.elems) nestinglevel = base.nestinglevel + 1 } @@ -119,7 +114,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => * @param sym ... */ def enter[T <: Symbol](sym: T): T = { - fingerPrints |= sym.name.fingerPrint enterEntry(newScopeEntry(sym, this)) sym } @@ -156,7 +150,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => } def rehash(sym: Symbol, newname: Name) { - fingerPrints |= newname.fingerPrint if (hashtable ne null) { val index = sym.name.start & HASHMASK var e1 = hashtable(index) diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 761dcc0534..4100e97cdd 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -72,11 +72,13 @@ abstract class SymbolTable extends macros.Universe Console.err.println(msg + ": " + result) result } - private[scala] def logResult[T](msg: String)(result: T): T = { + @inline + final private[scala] def logResult[T](msg: => String)(result: T): T = { log(msg + ": " + result) result } - private[scala] def logResultIf[T](msg: String, cond: T => Boolean)(result: T): T = { + @inline + final private[scala] def logResultIf[T](msg: => String, cond: T => Boolean)(result: T): T = { if (cond(result)) log(msg + ": " + result) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d52afed28a..37f41e2868 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -11,6 +11,7 @@ import scala.collection.mutable.ListBuffer import util.Statistics import Flags._ import base.Attachments +import scala.annotation.tailrec trait Symbols extends api.Symbols { self: SymbolTable => import definitions._ @@ -62,7 +63,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => abstract class SymbolContextApiImpl extends SymbolContextApi { this: Symbol => - def kind: String = kindString def isExistential: Boolean = this.isExistentiallyBound def isParamWithDefault: Boolean = this.hasDefault def isByNameParam: Boolean = this.isValueParameter && (this hasFlag BYNAMEPARAM) @@ -294,14 +294,31 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def newExistential(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): TypeSymbol = newAbstractType(name, pos, EXISTENTIAL | newFlags) - /** Synthetic value parameters when parameter symbols are not available - */ - final def newSyntheticValueParamss(argtypess: List[List[Type]]): List[List[TermSymbol]] = { + private def freshNamer: () => TermName = { var cnt = 0 - def freshName() = { cnt += 1; nme.syntheticParamName(cnt) } - mmap(argtypess)(tp => newValueParameter(freshName(), owner.pos.focus, SYNTHETIC) setInfo tp) + () => { cnt += 1; nme.syntheticParamName(cnt) } } + /** Synthetic value parameters when parameter symbols are not available + */ + final def newSyntheticValueParamss(argtypess: List[List[Type]]): List[List[TermSymbol]] = + argtypess map (xs => newSyntheticValueParams(xs, freshNamer)) + + /** Synthetic value parameters when parameter symbols are not available. + * Calling this method multiple times will re-use the same parameter names. + */ + final def newSyntheticValueParams(argtypes: List[Type]): List[TermSymbol] = + newSyntheticValueParams(argtypes, freshNamer) + + final def newSyntheticValueParams(argtypes: List[Type], freshName: () => TermName): List[TermSymbol] = + argtypes map (tp => newSyntheticValueParam(tp, freshName())) + + /** Synthetic value parameter when parameter symbol is not available. + * Calling this method multiple times will re-use the same parameter name. + */ + final def newSyntheticValueParam(argtype: Type, name: TermName = nme.syntheticParamName(1)): TermSymbol = + newValueParameter(name, owner.pos.focus, SYNTHETIC) setInfo argtype + def newSyntheticTypeParam(): TypeSymbol = newSyntheticTypeParam("T0", 0L) def newSyntheticTypeParam(name: String, newFlags: Long): TypeSymbol = newTypeParameter(newTypeName(name), NoPosition, newFlags) setInfo TypeBounds.empty def newSyntheticTypeParams(num: Int): List[TypeSymbol] = (0 until num).toList map (n => newSyntheticTypeParam("T" + n, 0L)) @@ -328,18 +345,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def freshExistential(suffix: String): TypeSymbol = newExistential(freshExistentialName(suffix), pos) - /** Synthetic value parameters when parameter symbols are not available. - * Calling this method multiple times will re-use the same parameter names. - */ - final def newSyntheticValueParams(argtypes: List[Type]): List[TermSymbol] = - newSyntheticValueParamss(List(argtypes)).head - - /** Synthetic value parameter when parameter symbol is not available. - * Calling this method multiple times will re-use the same parameter name. - */ - final def newSyntheticValueParam(argtype: Type): Symbol = - newSyntheticValueParams(List(argtype)).head - /** Type skolems are type parameters ''seen from the inside'' * Assuming a polymorphic method m[T], its type is a PolyType which has a TypeParameter * with name `T` in its typeParams list. While type checking the parameters, result type and @@ -1373,7 +1378,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The value parameter sections of this symbol. */ def paramss: List[List[Symbol]] = info.paramss - def hasParamWhich(cond: Symbol => Boolean) = mexists(paramss)(cond) /** The least proper supertype of a class; includes all parent types * and refinement where needed. You need to compute that in a situation like this: @@ -1522,7 +1526,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def alternatives: List[Symbol] = if (isOverloaded) info.asInstanceOf[OverloadedType].alternatives - else List(this) + else this :: Nil def filter(cond: Symbol => Boolean): Symbol = if (isOverloaded) { @@ -1626,7 +1630,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => (info.decls filter (_.isCaseAccessorMethod)).toList final def constrParamAccessors: List[Symbol] = - info.decls.toList filter (sym => !sym.isMethod && sym.isParamAccessor) + info.decls.filter(sym => !sym.isMethod && sym.isParamAccessor).toList /** The symbol accessed by this accessor (getter or setter) function. */ final def accessed: Symbol = accessed(owner.info) @@ -1892,9 +1896,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => * @param ofclazz The class containing the symbol's definition * @param site The base type from which member types are computed */ - final def matchingSymbol(ofclazz: Symbol, site: Type): Symbol = - ofclazz.info.nonPrivateDecl(name).filter(sym => - !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) + final def matchingSymbol(ofclazz: Symbol, site: Type): Symbol = { + //OPT cut down on #closures by special casing non-overloaded case + // was: ofclazz.info.nonPrivateDecl(name) filter (sym => + // !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) + val result = ofclazz.info.nonPrivateDecl(name) + def qualifies(sym: Symbol) = !sym.isTerm || (site.memberType(this) matches site.memberType(sym)) + if ((result eq NoSymbol) || !result.isOverloaded && qualifies(result)) result + else result filter qualifies + } /** The non-private member of `site` whose type and name match the type of this symbol. */ final def matchingSymbol(site: Type, admit: Long = 0L): Symbol = @@ -2873,7 +2883,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } override def name: TypeName = { - Statistics.incCounter(nameCount) + if (Statistics.canEnable) Statistics.incCounter(nameCount) if (needsFlatClasses) { if (flatname eq null) flatname = nme.flattenedName(rawowner.name, rawname).toTypeName @@ -3202,7 +3212,22 @@ trait Symbols extends api.Symbols { self: SymbolTable => def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) } +// ----- Hoisted closures and convenience methods, for compile time reductions ------- + + private[scala] final val symbolIsPossibleInRefinement = (sym: Symbol) => sym.isPossibleInRefinement + private[scala] final val symbolIsNonVariant = (sym: Symbol) => sym.variance == 0 + + @tailrec private[scala] final + def allSymbolsHaveOwner(syms: List[Symbol], owner: Symbol): Boolean = syms match { + case sym :: rest => sym.owner == owner && allSymbolsHaveOwner(rest, owner) + case _ => true + } + + +// -------------- Statistics -------------------------------------------------------- + Statistics.newView("#symbols")(ids) + } object SymbolsStats { diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index d160695e67..f953e9b757 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -194,7 +194,7 @@ abstract class TreeGen extends macros.TreeBuilder { mkTypeApply(mkAttributedSelect(target, method), targs map TypeTree) private def mkSingleTypeApply(value: Tree, tpe: Type, what: Symbol, wrapInApply: Boolean) = { - val tapp = mkAttributedTypeApply(value, what, List(tpe.normalize)) + val tapp = mkAttributedTypeApply(value, what, tpe.normalize :: Nil) if (wrapInApply) Apply(tapp, Nil) else tapp } private def typeTestSymbol(any: Boolean) = if (any) Any_isInstanceOf else Object_isInstanceOf diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 19f264f60e..3a930a195b 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/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, TupleClass, MacroContextClass, MacroContextPrefixType } /* Does not seem to be used. Not sure what it does anyway. def isOwnerDefinition(tree: Tree): Boolean = tree match { @@ -521,20 +521,18 @@ abstract class TreeInfo { */ def noPredefImportForUnit(body: Tree) = { // Top-level definition whose leading imports include Predef. - def containsLeadingPredefImport(defs: List[Tree]): Boolean = defs match { - case PackageDef(_, defs1) :: _ => containsLeadingPredefImport(defs1) - case Import(expr, _) :: rest => isReferenceToPredef(expr) || containsLeadingPredefImport(rest) - case _ => false + def isLeadingPredefImport(defn: Tree): Boolean = defn match { + case PackageDef(_, defs1) => defs1 exists isLeadingPredefImport + case Import(expr, _) => isReferenceToPredef(expr) + case _ => false } - // Compilation unit is class or object 'name' in package 'scala' def isUnitInScala(tree: Tree, name: Name) = tree match { case PackageDef(Ident(nme.scala_), defs) => firstDefinesClassOrObject(defs, name) case _ => false } - ( isUnitInScala(body, nme.Predef) - || containsLeadingPredefImport(List(body))) + isUnitInScala(body, nme.Predef) || isLeadingPredefImport(body) } def isAbsTypeDef(tree: Tree) = tree match { @@ -591,4 +589,24 @@ abstract class TreeInfo { object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic) object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName) object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed) + + object MacroImplReference { + private def refPart(tree: Tree): Tree = tree match { + case TypeApply(fun, _) => refPart(fun) + case ref: RefTree => ref + case _ => EmptyTree + } + + def unapply(tree: Tree) = refPart(tree) match { + case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, typeArguments(tree))) + case _ => None + } + } + + def isNullaryInvocation(tree: Tree): Boolean = + tree.symbol != null && tree.symbol.isMethod && (tree match { + case TypeApply(fun, _) => isNullaryInvocation(fun) + case tree: RefTree => true + case _ => false + }) } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 3894870252..0180ed4c4f 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -19,7 +19,7 @@ trait Trees extends api.Trees { self: SymbolTable => val id = nodeCount // TODO: add to attachment? nodeCount += 1 - Statistics.incCounter(TreesStats.nodeByType, getClass) + if (Statistics.canEnable) Statistics.incCounter(TreesStats.nodeByType, getClass) @inline final override def pos: Position = rawatt.pos @@ -29,7 +29,7 @@ trait Trees extends api.Trees { self: SymbolTable => def setType(tp: Type): this.type = { rawtpe = tp; this } def defineType(tp: Type): this.type = setType(tp) - def symbol: Symbol = null + def symbol: Symbol = null //!!!OPT!!! symbol is about 3% of hot compile times -- megamorphic dispatch? def symbol_=(sym: Symbol) { throw new UnsupportedOperationException("symbol_= inapplicable for " + this) } def setSymbol(sym: Symbol): this.type = { symbol = sym; this } def hasSymbol = false @@ -210,7 +210,7 @@ trait Trees extends api.Trees { self: SymbolTable => trait TypTree extends Tree with TypTreeApi - trait SymTree extends Tree with SymTreeContextApi { + abstract class SymTree extends Tree with SymTreeContextApi { override def hasSymbol = true override var symbol: Symbol = NoSymbol } @@ -230,6 +230,7 @@ trait Trees extends api.Trees { self: SymbolTable => } case object EmptyTree extends TermTree { + val asList = List(this) super.tpe_=(NoType) override def tpe_=(t: Type) = if (t != NoType) throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for <empty>") @@ -290,7 +291,10 @@ trait Trees extends api.Trees { self: SymbolTable => object LabelDef extends LabelDefExtractor case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) extends ImportSelectorApi - object ImportSelector extends ImportSelectorExtractor + object ImportSelector extends ImportSelectorExtractor { + val wild = ImportSelector(nme.WILDCARD, -1, null, -1) + val wildList = List(wild) + } case class Import(expr: Tree, selectors: List[ImportSelector]) extends SymTree with ImportApi @@ -329,7 +333,7 @@ trait Trees extends api.Trees { self: SymbolTable => object ArrayValue extends ArrayValueExtractor case class Function(vparams: List[ValDef], body: Tree) - extends TermTree with SymTree with FunctionApi + extends SymTree with TermTree with FunctionApi object Function extends FunctionExtractor case class Assign(lhs: Tree, rhs: Tree) @@ -349,7 +353,7 @@ trait Trees extends api.Trees { self: SymbolTable => object Match extends MatchExtractor case class Return(expr: Tree) - extends TermTree with SymTree with ReturnApi + extends SymTree with TermTree with ReturnApi object Return extends ReturnExtractor case class Try(block: Tree, catches: List[CaseDef], finalizer: Tree) @@ -397,7 +401,7 @@ trait Trees extends api.Trees { self: SymbolTable => def ApplyConstructor(tpt: Tree, args: List[Tree]) = Apply(Select(New(tpt), nme.CONSTRUCTOR), args) case class ApplyDynamic(qual: Tree, args: List[Tree]) - extends TermTree with SymTree with ApplyDynamicApi + extends SymTree with TermTree with ApplyDynamicApi object ApplyDynamic extends ApplyDynamicExtractor case class Super(qual: Tree, mix: TypeName) extends TermTree with SuperApi { @@ -407,7 +411,7 @@ trait Trees extends api.Trees { self: SymbolTable => object Super extends SuperExtractor case class This(qual: TypeName) - extends TermTree with SymTree with ThisApi + extends SymTree with TermTree with ThisApi object This extends ThisExtractor case class Select(qualifier: Tree, name: Name) @@ -443,7 +447,7 @@ trait Trees extends api.Trees { self: SymbolTable => object SingletonTypeTree extends SingletonTypeTreeExtractor case class SelectFromTypeTree(qualifier: Tree, name: TypeName) - extends TypTree with RefTree with SelectFromTypeTreeApi + extends RefTree with TypTree with SelectFromTypeTreeApi object SelectFromTypeTree extends SelectFromTypeTreeExtractor case class CompoundTypeTree(templ: Template) @@ -1141,29 +1145,25 @@ trait Trees extends api.Trees { self: SymbolTable => } } + //OPT ordered according to frequency to speed it up. override protected def itransform(transformer: Transformer, tree: Tree): Tree = { import transformer._ val treeCopy = transformer.treeCopy + + // begin itransform tree match { - case EmptyTree => - tree - case PackageDef(pid, stats) => - treeCopy.PackageDef( - tree, transform(pid).asInstanceOf[RefTree], - atOwner(mclass(tree.symbol)) { - transformStats(stats, currentOwner) - } - ) - case ClassDef(mods, name, tparams, impl) => - atOwner(tree.symbol) { - treeCopy.ClassDef(tree, transformModifiers(mods), name, - transformTypeDefs(tparams), transformTemplate(impl)) - } - case ModuleDef(mods, name, impl) => - atOwner(mclass(tree.symbol)) { - treeCopy.ModuleDef(tree, transformModifiers(mods), - name, transformTemplate(impl)) - } + case Ident(name) => + treeCopy.Ident(tree, name) + case Select(qualifier, selector) => + treeCopy.Select(tree, transform(qualifier), selector) + case Apply(fun, args) => + treeCopy.Apply(tree, transform(fun), transformTrees(args)) + case TypeTree() => + treeCopy.TypeTree(tree) + case Literal(value) => + treeCopy.Literal(tree, value) + case This(qual) => + treeCopy.This(tree, qual) case ValDef(mods, name, tpt, rhs) => atOwner(tree.symbol) { treeCopy.ValDef(tree, transformModifiers(mods), @@ -1175,73 +1175,70 @@ trait Trees extends api.Trees { self: SymbolTable => transformTypeDefs(tparams), transformValDefss(vparamss), transform(tpt), transform(rhs)) } - case TypeDef(mods, name, tparams, rhs) => - atOwner(tree.symbol) { - treeCopy.TypeDef(tree, transformModifiers(mods), name, - transformTypeDefs(tparams), transform(rhs)) - } - case LabelDef(name, params, rhs) => - treeCopy.LabelDef(tree, name, transformIdents(params), transform(rhs)) //bq: Martin, once, atOwner(...) works, also change `LamdaLifter.proxy' - case Import(expr, selectors) => - treeCopy.Import(tree, transform(expr), selectors) - case Template(parents, self, body) => - treeCopy.Template(tree, transformTrees(parents), transformValDef(self), transformStats(body, tree.symbol)) case Block(stats, expr) => treeCopy.Block(tree, transformStats(stats, currentOwner), transform(expr)) + case If(cond, thenp, elsep) => + treeCopy.If(tree, transform(cond), transform(thenp), transform(elsep)) case CaseDef(pat, guard, body) => treeCopy.CaseDef(tree, transform(pat), transform(guard), transform(body)) - case Alternative(trees) => - treeCopy.Alternative(tree, transformTrees(trees)) - case Star(elem) => - treeCopy.Star(tree, transform(elem)) + case TypeApply(fun, args) => + treeCopy.TypeApply(tree, transform(fun), transformTrees(args)) + case AppliedTypeTree(tpt, args) => + treeCopy.AppliedTypeTree(tree, transform(tpt), transformTrees(args)) case Bind(name, body) => treeCopy.Bind(tree, name, transform(body)) - case UnApply(fun, args) => - treeCopy.UnApply(tree, fun, transformTrees(args)) // bq: see test/.../unapplyContexts2.scala - case ArrayValue(elemtpt, trees) => - treeCopy.ArrayValue(tree, transform(elemtpt), transformTrees(trees)) case Function(vparams, body) => atOwner(tree.symbol) { treeCopy.Function(tree, transformValDefs(vparams), transform(body)) } + case Match(selector, cases) => + treeCopy.Match(tree, transform(selector), transformCaseDefs(cases)) + case New(tpt) => + treeCopy.New(tree, transform(tpt)) case Assign(lhs, rhs) => treeCopy.Assign(tree, transform(lhs), transform(rhs)) case AssignOrNamedArg(lhs, rhs) => treeCopy.AssignOrNamedArg(tree, transform(lhs), transform(rhs)) - case If(cond, thenp, elsep) => - treeCopy.If(tree, transform(cond), transform(thenp), transform(elsep)) - case Match(selector, cases) => - treeCopy.Match(tree, transform(selector), transformCaseDefs(cases)) - case Return(expr) => - treeCopy.Return(tree, transform(expr)) case Try(block, catches, finalizer) => treeCopy.Try(tree, transform(block), transformCaseDefs(catches), transform(finalizer)) + case EmptyTree => + tree case Throw(expr) => treeCopy.Throw(tree, transform(expr)) - case New(tpt) => - treeCopy.New(tree, transform(tpt)) - case Typed(expr, tpt) => - treeCopy.Typed(tree, transform(expr), transform(tpt)) - case TypeApply(fun, args) => - treeCopy.TypeApply(tree, transform(fun), transformTrees(args)) - case Apply(fun, args) => - treeCopy.Apply(tree, transform(fun), transformTrees(args)) - case ApplyDynamic(qual, args) => - treeCopy.ApplyDynamic(tree, transform(qual), transformTrees(args)) case Super(qual, mix) => treeCopy.Super(tree, transform(qual), mix) - case This(qual) => - treeCopy.This(tree, qual) - case Select(qualifier, selector) => - treeCopy.Select(tree, transform(qualifier), selector) - case Ident(name) => - treeCopy.Ident(tree, name) - case ReferenceToBoxed(idt) => - treeCopy.ReferenceToBoxed(tree, transform(idt) match { case idt1: Ident => idt1 }) - case Literal(value) => - treeCopy.Literal(tree, value) - case TypeTree() => - treeCopy.TypeTree(tree) + case TypeBoundsTree(lo, hi) => + treeCopy.TypeBoundsTree(tree, transform(lo), transform(hi)) + case Typed(expr, tpt) => + treeCopy.Typed(tree, transform(expr), transform(tpt)) + case Import(expr, selectors) => + treeCopy.Import(tree, transform(expr), selectors) + case Template(parents, self, body) => + treeCopy.Template(tree, transformTrees(parents), transformValDef(self), transformStats(body, tree.symbol)) + case ClassDef(mods, name, tparams, impl) => + atOwner(tree.symbol) { + treeCopy.ClassDef(tree, transformModifiers(mods), name, + transformTypeDefs(tparams), transformTemplate(impl)) + } + case ModuleDef(mods, name, impl) => + atOwner(mclass(tree.symbol)) { + treeCopy.ModuleDef(tree, transformModifiers(mods), + name, transformTemplate(impl)) + } + case TypeDef(mods, name, tparams, rhs) => + atOwner(tree.symbol) { + treeCopy.TypeDef(tree, transformModifiers(mods), name, + transformTypeDefs(tparams), transform(rhs)) + } + case LabelDef(name, params, rhs) => + treeCopy.LabelDef(tree, name, transformIdents(params), transform(rhs)) //bq: Martin, once, atOwner(...) works, also change `LamdaLifter.proxy' + case PackageDef(pid, stats) => + treeCopy.PackageDef( + tree, transform(pid).asInstanceOf[RefTree], + atOwner(mclass(tree.symbol)) { + transformStats(stats, currentOwner) + } + ) case Annotated(annot, arg) => treeCopy.Annotated(tree, transform(annot), transform(arg)) case SingletonTypeTree(ref) => @@ -1250,12 +1247,22 @@ trait Trees extends api.Trees { self: SymbolTable => treeCopy.SelectFromTypeTree(tree, transform(qualifier), selector) case CompoundTypeTree(templ) => treeCopy.CompoundTypeTree(tree, transformTemplate(templ)) - case AppliedTypeTree(tpt, args) => - treeCopy.AppliedTypeTree(tree, transform(tpt), transformTrees(args)) - case TypeBoundsTree(lo, hi) => - treeCopy.TypeBoundsTree(tree, transform(lo), transform(hi)) case ExistentialTypeTree(tpt, whereClauses) => treeCopy.ExistentialTypeTree(tree, transform(tpt), transformTrees(whereClauses)) + case Return(expr) => + treeCopy.Return(tree, transform(expr)) + case Alternative(trees) => + treeCopy.Alternative(tree, transformTrees(trees)) + case Star(elem) => + treeCopy.Star(tree, transform(elem)) + case UnApply(fun, args) => + treeCopy.UnApply(tree, fun, transformTrees(args)) // bq: see test/.../unapplyContexts2.scala + case ArrayValue(elemtpt, trees) => + treeCopy.ArrayValue(tree, transform(elemtpt), transformTrees(trees)) + case ApplyDynamic(qual, args) => + treeCopy.ApplyDynamic(tree, transform(qual), transformTrees(args)) + case ReferenceToBoxed(idt) => + treeCopy.ReferenceToBoxed(tree, transform(idt) match { case idt1: Ident => idt1 }) case _ => xtransform(transformer, tree) } @@ -1536,6 +1543,8 @@ trait Trees extends api.Trees { self: SymbolTable => sys.error("Not a LabelDef: " + t + "/" + t.getClass) } +// -------------- Classtags -------------------------------------------------------- + implicit val TreeTag = ClassTag[Tree](classOf[Tree]) implicit val TermTreeTag = ClassTag[TermTree](classOf[TermTree]) implicit val TypTreeTag = ClassTag[TypTree](classOf[TypTree]) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 56506246ca..01f615c5cc 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -15,6 +15,7 @@ import scala.util.control.ControlThrowable import scala.annotation.tailrec import util.Statistics import scala.runtime.ObjectRef +import util.ThreeValues._ /* A standard type pattern match: case ErrorType => @@ -117,13 +118,34 @@ trait Types extends api.Types { self: SymbolTable => class UndoLog extends Clearable { private type UndoPairs = List[(TypeVar, TypeConstraint)] - private var log: UndoPairs = List() + //OPT this method is public so we can do `manual inlining` + var log: UndoPairs = List() + + /* + * These two methods provide explicit locking mechanism that is overridden in SynchronizedUndoLog. + * + * The idea behind explicit locking mechanism is that all public methods that access mutable state + * will have to obtain the lock for their entire execution so both reads and writes can be kept in + * right order. Originally, that was achieved by overriding those public methods in + * `SynchronizedUndoLog` which was fine but expensive. The reason is that those public methods take + * thunk as argument and if we keep them non-final there's no way to make them inlined so thunks + * can go away. + * + * By using explicit locking we can achieve inlining. + * + * NOTE: They are made public for now so we can apply 'manual inlining' (copy&pasting into hot + * places implementation of `undo` or `undoUnless`). This should be changed back to protected + * once inliner is fixed. + */ + def lock(): Unit = () + def unlock(): Unit = () // register with the auto-clearing cache manager perRunCaches.recordCache(this) /** Undo all changes to constraints to type variables upto `limit`. */ - private def undoTo(limit: UndoPairs) { + //OPT this method is public so we can do `manual inlining` + def undoTo(limit: UndoPairs) { while ((log ne limit) && log.nonEmpty) { val (tv, constr) = log.head tv.constr = constr @@ -140,30 +162,41 @@ trait Types extends api.Types { self: SymbolTable => } def clear() { - if (settings.debug.value) - self.log("Clearing " + log.size + " entries from the undoLog.") - - log = Nil + lock() + try { + if (settings.debug.value) + self.log("Clearing " + log.size + " entries from the undoLog.") + log = Nil + } finally unlock() + } + def size = { + lock() + try log.size finally unlock() } - def size = log.size // `block` should not affect constraints on typevars def undo[T](block: => T): T = { - val before = log + lock() + try { + val before = log - try block - finally undoTo(before) + try block + finally undoTo(before) + } finally unlock() } // if `block` evaluates to false, it should not affect constraints on typevars def undoUnless(block: => Boolean): Boolean = { - val before = log - var result = false + lock() + try { + val before = log + var result = false - try result = block - finally if (!result) undoTo(before) + try result = block + finally if (!result) undoTo(before) - result + result + } finally unlock() } } @@ -293,7 +326,7 @@ trait Types extends api.Types { self: SymbolTable => case SuperType(_, _) => false case SingleType(pre, sym) => notConcreteSym(sym) case ConstantType(_) => false - case TypeRef(_, sym, args) => notConcreteSym(sym) || (args exists (arg => notConcreteTpe(arg))) + case TypeRef(_, sym, args) => notConcreteSym(sym) || (args exists notConcreteTpe) case RefinedType(_, _) => false case ExistentialType(_, _) => false case AnnotatedType(_, tp, _) => notConcreteTpe(tp) @@ -343,8 +376,8 @@ trait Types extends api.Types { self: SymbolTable => */ def isImmediatelyDependent: Boolean = false - /** Does this depend on an enclosing method parameter? */ - def isDependent: Boolean = IsDependentCollector.collect(this) + /** Is this type a dependent method type? */ + def isDependentMethodType: Boolean = false /** True for WildcardType or BoundedWildcardType. */ def isWildcard = false @@ -363,7 +396,7 @@ trait Types extends api.Types { self: SymbolTable => * and all type parameters (if any) are invariant. */ def isFinalType = - typeSymbol.isFinal && (typeSymbol.typeParams forall (_.variance == 0)) + typeSymbol.isFinal && (typeSymbol.typeParams forall symbolIsNonVariant) /** Is this type completed (i.e. not a lazy type)? */ def isComplete: Boolean = true @@ -442,7 +475,7 @@ trait Types extends api.Types { self: SymbolTable => if (phase.erasedTypes) this else { val cowner = commonOwner(this) - refinedType(List(this), cowner, EmptyScope, cowner.pos).narrow + refinedType(this :: Nil, cowner, EmptyScope, cowner.pos).narrow } /** For a TypeBounds type, itself; @@ -692,7 +725,8 @@ trait Types extends api.Types { self: SymbolTable => * = Int */ def asSeenFrom(pre: Type, clazz: Symbol): Type = { - TypesStats.timedTypeOp(asSeenFromNanos) { + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, asSeenFromNanos) else null + try { val trivial = ( this.isTrivial || phase.erasedTypes && pre.typeSymbol != ArrayClass @@ -707,7 +741,7 @@ trait Types extends api.Types { self: SymbolTable => if (m.capturedSkolems.isEmpty) tp1 else deriveType(m.capturedSkolems, _.cloneSymbol setFlag CAPTURED)(tp1) } - } + } finally if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) } /** The info of `sym`, seen as a member of this type. @@ -815,7 +849,7 @@ trait Types extends api.Types { self: SymbolTable => /** Is this type a subtype of that type? */ def <:<(that: Type): Boolean = { - if (util.Statistics.enabled) stat_<:<(that) + if (Statistics.canEnable) stat_<:<(that) else { (this eq that) || (if (explainSwitch) explain("<:", isSubType, this, that) @@ -843,26 +877,26 @@ trait Types extends api.Types { self: SymbolTable => } def stat_<:<(that: Type): Boolean = { - Statistics.incCounter(subtypeCount) - val start = Statistics.pushTimer(typeOpsStack, subtypeNanos) + if (Statistics.canEnable) Statistics.incCounter(subtypeCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, subtypeNanos) else null val result = (this eq that) || (if (explainSwitch) explain("<:", isSubType, this, that) else isSubType(this, that, AnyDepth)) - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) result } /** Is this type a weak subtype of that type? True also for numeric types, i.e. Int weak_<:< Long. */ def weak_<:<(that: Type): Boolean = { - Statistics.incCounter(subtypeCount) - val start = Statistics.pushTimer(typeOpsStack, subtypeNanos) + if (Statistics.canEnable) Statistics.incCounter(subtypeCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, subtypeNanos) else null val result = ((this eq that) || (if (explainSwitch) explain("weak_<:", isWeakSubType, this, that) else isWeakSubType(this, that))) - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) result } @@ -1007,7 +1041,7 @@ trait Types extends api.Types { self: SymbolTable => if (!e.sym.hasFlag(excludedFlags)) { if (sym == NoSymbol) sym = e.sym else { - if (alts.isEmpty) alts = List(sym) + if (alts.isEmpty) alts = sym :: Nil alts = e.sym :: alts } } @@ -1025,8 +1059,8 @@ trait Types extends api.Types { self: SymbolTable => // See (t0851) for a situation where this happens. val suspension: List[TypeVar] = if (this.isGround) null else suspendTypeVarsInType(this) - Statistics.incCounter(findMembersCount) - val start = Statistics.pushTimer(typeOpsStack, findMembersNanos) + if (Statistics.canEnable) Statistics.incCounter(findMembersCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMembersNanos) else null //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG var members: Scope = null @@ -1078,7 +1112,7 @@ trait Types extends api.Types { self: SymbolTable => required |= DEFERRED excluded &= ~(DEFERRED.toLong) } // while (continue) - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) if (suspension ne null) suspension foreach (_.suspended = false) if (members eq null) EmptyScope else members } @@ -1101,8 +1135,8 @@ trait Types extends api.Types { self: SymbolTable => // See (t0851) for a situation where this happens. val suspension: List[TypeVar] = if (this.isGround) null else suspendTypeVarsInType(this) - Statistics.incCounter(findMemberCount) - val start = Statistics.pushTimer(typeOpsStack, findMemberNanos) + if (Statistics.canEnable) Statistics.incCounter(findMemberCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMemberNanos) else null //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG var member: Symbol = NoSymbol @@ -1113,7 +1147,6 @@ trait Types extends api.Types { self: SymbolTable => var excluded = excludedFlags | DEFERRED var continue = true var self: Type = null - val fingerPrint: Long = name.fingerPrint while (continue) { continue = false @@ -1121,75 +1154,73 @@ trait Types extends api.Types { self: SymbolTable => var bcs = bcs0 while (!bcs.isEmpty) { val decls = bcs.head.info.decls - if ((fingerPrint & decls.fingerPrints) != 0) { - var entry = decls.lookupEntry(name) - while (entry ne null) { - val sym = entry.sym - val flags = sym.flags - if ((flags & required) == required) { - val excl = flags & excluded - if (excl == 0L && + var entry = decls.lookupEntry(name) + while (entry ne null) { + val sym = entry.sym + val flags = sym.flags + if ((flags & required) == required) { + val excl = flags & excluded + if (excl == 0L && (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. - (bcs eq bcs0) || - (flags & PrivateLocal) != PrivateLocal || - (bcs0.head.hasTransOwner(bcs.head)))) { - if (name.isTypeName || stableOnly && sym.isStable) { - Statistics.popTimer(typeOpsStack, start) - if (suspension ne null) suspension foreach (_.suspended = false) - return sym - } else if (member eq NoSymbol) { - member = sym - } else if (members eq null) { - if ((member ne sym) && - ((member.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = this.narrow - if (membertpe eq null) membertpe = self.memberType(member) - !(membertpe matches self.memberType(sym)) - })) { - lastM = new ::(sym, null) - members = member :: lastM - } - } else { - var others: List[Symbol] = members - var symtpe: Type = null - while ((others ne null) && { - val other = others.head - (other ne sym) && - ((other.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = this.narrow - if (symtpe eq null) symtpe = self.memberType(sym) - !(self.memberType(other) matches symtpe) + (bcs eq bcs0) || + (flags & PrivateLocal) != PrivateLocal || + (bcs0.head.hasTransOwner(bcs.head)))) { + if (name.isTypeName || stableOnly && sym.isStable) { + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + if (suspension ne null) suspension foreach (_.suspended = false) + return sym + } else if (member eq NoSymbol) { + member = sym + } else if (members eq null) { + if ((member ne sym) && + ((member.owner eq sym.owner) || + (flags & PRIVATE) != 0 || { + if (self eq null) self = this.narrow + if (membertpe eq null) membertpe = self.memberType(member) + !(membertpe matches self.memberType(sym)) + })) { + lastM = new ::(sym, null) + members = member :: lastM + } + } else { + var others: List[Symbol] = members + var symtpe: Type = null + while ((others ne null) && { + val other = others.head + (other ne sym) && + ((other.owner eq sym.owner) || + (flags & PRIVATE) != 0 || { + if (self eq null) self = this.narrow + if (symtpe eq null) symtpe = self.memberType(sym) + !(self.memberType(other) matches symtpe) })}) { - others = others.tail - } - if (others eq null) { - val lastM1 = new ::(sym, null) - lastM.tl = lastM1 - lastM = lastM1 - } + others = others.tail + } + if (others eq null) { + val lastM1 = new ::(sym, null) + lastM.tl = lastM1 + lastM = lastM1 } - } else if (excl == DEFERRED) { - continue = true } + } else if (excl == DEFERRED) { + continue = true } - entry = decls lookupNextEntry entry - } // while (entry ne null) - } // if (fingerPrint matches) + } + entry = decls lookupNextEntry entry + } // while (entry ne null) // excluded = excluded | LOCAL bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail } // while (!bcs.isEmpty) required |= DEFERRED excluded &= ~(DEFERRED.toLong) } // while (continue) - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) if (suspension ne null) suspension foreach (_.suspended = false) if (members eq null) { - if (member == NoSymbol) Statistics.incCounter(noMemberCount) + if (member == NoSymbol) if (Statistics.canEnable) Statistics.incCounter(noMemberCount) member } else { - Statistics.incCounter(multMemberCount) + if (Statistics.canEnable) Statistics.incCounter(multMemberCount) lastM.tl = Nil baseClasses.head.newOverloaded(this, members) } @@ -1238,14 +1269,18 @@ trait Types extends api.Types { self: SymbolTable => // Subclasses ------------------------------------------------------------ - trait UniqueType extends Product { - final override val hashCode = scala.runtime.ScalaRunTime._hashCode(this) + /** + * A type that can be passed to unique(..) and be stored in the uniques map. + */ + abstract class UniqueType extends Type with Product { + final override val hashCode = computeHashCode + protected def computeHashCode = scala.runtime.ScalaRunTime._hashCode(this) } /** A base class for types that defer some operations * to their immediate supertype. */ - abstract class SubType extends Type { + abstract class SubType extends UniqueType { def supertype: Type override def parents: List[Type] = supertype.parents override def decls: Scope = supertype.decls @@ -1276,7 +1311,7 @@ trait Types extends api.Types { self: SymbolTable => override def isVolatile = underlying.isVolatile override def widen: Type = underlying.widen override def baseTypeSeq: BaseTypeSeq = { - Statistics.incCounter(singletonBaseTypeSeqCount) + if (Statistics.canEnable) Statistics.incCounter(singletonBaseTypeSeqCount) underlying.baseTypeSeq prepend this } override def isHigherKinded = false // singleton type classifies objects, thus must be kind * @@ -1386,7 +1421,7 @@ trait Types extends api.Types { self: SymbolTable => override def kind = "ThisType" } - final class UniqueThisType(sym: Symbol) extends ThisType(sym) with UniqueType { } + final class UniqueThisType(sym: Symbol) extends ThisType(sym) { } object ThisType extends ThisTypeExtractor { def apply(sym: Symbol): Type = @@ -1398,7 +1433,11 @@ trait Types extends api.Types { self: SymbolTable => * Cannot be created directly; one should always use `singleType` for creation. */ abstract case class SingleType(pre: Type, sym: Symbol) extends SingletonType with SingleTypeApi { - override val isTrivial: Boolean = pre.isTrivial + private var trivial: ThreeValue = UNKNOWN + override def isTrivial: Boolean = { + if (trivial == UNKNOWN) trivial = fromBoolean(pre.isTrivial) + toBoolean(trivial) + } override def isGround = sym.isPackageClass || pre.isGround // override def isNullable = underlying.isNullable @@ -1443,7 +1482,7 @@ trait Types extends api.Types { self: SymbolTable => override def kind = "SingleType" } - final class UniqueSingleType(pre: Type, sym: Symbol) extends SingleType(pre, sym) with UniqueType { } + final class UniqueSingleType(pre: Type, sym: Symbol) extends SingleType(pre, sym) object SingleType extends SingleTypeExtractor { def apply(pre: Type, sym: Symbol): Type = { @@ -1464,7 +1503,11 @@ trait Types extends api.Types { self: SymbolTable => } abstract case class SuperType(thistpe: Type, supertpe: Type) extends SingletonType with SuperTypeApi { - override val isTrivial: Boolean = thistpe.isTrivial && supertpe.isTrivial + private var trivial: ThreeValue = UNKNOWN + override def isTrivial: Boolean = { + if (trivial == UNKNOWN) trivial = fromBoolean(thistpe.isTrivial && supertpe.isTrivial) + toBoolean(trivial) + } override def isNotNull = true; override def typeSymbol = thistpe.typeSymbol override def underlying = supertpe @@ -1474,7 +1517,7 @@ trait Types extends api.Types { self: SymbolTable => override def kind = "SuperType" } - final class UniqueSuperType(thistp: Type, supertp: Type) extends SuperType(thistp, supertp) with UniqueType { } + final class UniqueSuperType(thistp: Type, supertp: Type) extends SuperType(thistp, supertp) object SuperType extends SuperTypeExtractor { def apply(thistp: Type, supertp: Type): Type = { @@ -1487,7 +1530,7 @@ trait Types extends api.Types { self: SymbolTable => */ abstract case class TypeBounds(lo: Type, hi: Type) extends SubType with TypeBoundsApi { def supertype = hi - override val isTrivial: Boolean = lo.isTrivial && hi.isTrivial + override def isTrivial: Boolean = lo.isTrivial && hi.isTrivial override def bounds: TypeBounds = this def containsType(that: Type) = that match { case TypeBounds(_, _) => that <:< this @@ -1495,8 +1538,8 @@ trait Types extends api.Types { self: SymbolTable => } 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 + private def emptyLowerBound = typeIsNothing(lo) + private def emptyUpperBound = typeIsAny(hi) def isEmptyBounds = emptyLowerBound && emptyUpperBound // override def isNullable: Boolean = NullClass.tpe <:< lo; @@ -1504,7 +1547,7 @@ trait Types extends api.Types { self: SymbolTable => override def kind = "TypeBoundsType" } - final class UniqueTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) with UniqueType { } + final class UniqueTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) object TypeBounds extends TypeBoundsExtractor { def empty: TypeBounds = apply(NothingClass.tpe, AnyClass.tpe) @@ -1579,10 +1622,10 @@ trait Types extends api.Types { self: SymbolTable => } override def narrow: Type = typeSymbol.thisType - override def isNotNull: Boolean = parents exists (_.isNotNull) + override def isNotNull: Boolean = parents exists typeIsNotNull override def isStructuralRefinement: Boolean = - typeSymbol.isAnonOrRefinementClass && decls.exists(_.isPossibleInRefinement) + typeSymbol.isAnonOrRefinementClass && (decls exists symbolIsPossibleInRefinement) // override def isNullable: Boolean = // parents forall (p => p.isNullable && !p.typeSymbol.isAbstractType); @@ -1598,7 +1641,7 @@ trait Types extends api.Types { self: SymbolTable => if (period != currentPeriod) { tpe.baseTypeSeqPeriod = currentPeriod if (!isValidForBaseClasses(period)) { - if (tpe.parents.exists(_.exists(_.isInstanceOf[TypeVar]))) { + if (tpe.parents exists typeContainsTypeVar) { // rename type vars to fresh type params, take base type sequence of // resulting type, and rename back all the entries in that sequence var tvs = Set[TypeVar]() @@ -1625,8 +1668,8 @@ trait Types extends api.Types { self: SymbolTable => val bts = copyRefinedType(tpe.asInstanceOf[RefinedType], tpe.parents map varToParam, varToParam mapOver tpe.decls).baseTypeSeq tpe.baseTypeSeqCache = bts lateMap paramToVar } else { - Statistics.incCounter(compoundBaseTypeSeqCount) - val start = Statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) + if (Statistics.canEnable) Statistics.incCounter(compoundBaseTypeSeqCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) else null try { tpe.baseTypeSeqCache = undetBaseTypeSeq tpe.baseTypeSeqCache = @@ -1635,7 +1678,7 @@ trait Types extends api.Types { self: SymbolTable => else compoundBaseTypeSeq(tpe) } finally { - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) } // [Martin] suppressing memo-ization solves the problem with "same type after erasure" errors // when compiling with @@ -1683,12 +1726,12 @@ trait Types extends api.Types { self: SymbolTable => if (period != currentPeriod) { tpe.baseClassesPeriod = currentPeriod if (!isValidForBaseClasses(period)) { - val start = Statistics.pushTimer(typeOpsStack, baseClassesNanos) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, baseClassesNanos) else null try { tpe.baseClassesCache = null tpe.baseClassesCache = tpe.memo(computeBaseClasses)(tpe.typeSymbol :: _.baseClasses.tail) } finally { - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) } } } @@ -1706,7 +1749,7 @@ trait Types extends api.Types { self: SymbolTable => override def isHigherKinded = ( parents.nonEmpty && - (parents forall (_.isHigherKinded)) && + (parents forall typeIsHigherKinded) && !phase.erasedTypes ) @@ -1989,7 +2032,7 @@ trait Types extends api.Types { self: SymbolTable => override def kind = "ConstantType" } - final class UniqueConstantType(value: Constant) extends ConstantType(value) with UniqueType { + final class UniqueConstantType(value: Constant) extends ConstantType(value) { /** Save the type of `value`. For Java enums, it depends on finding the linked class, * which might not be found after `flatten`. */ private lazy val _tpe: Type = value.tpe @@ -2037,7 +2080,7 @@ trait Types extends api.Types { self: SymbolTable => private var volatileRecursions: Int = 0 private val pendingVolatiles = new mutable.HashSet[Symbol] - class ArgsTypeRef(pre0: Type, sym0: Symbol, args0: List[Type]) extends TypeRef(pre0, sym0, args0) with UniqueType { + class ArgsTypeRef(pre0: Type, sym0: Symbol, args0: List[Type]) extends TypeRef(pre0, sym0, args0) { require(args0.nonEmpty, this) /** No unapplied type params size it has (should have) equally as many args. */ @@ -2090,7 +2133,7 @@ trait Types extends api.Types { self: SymbolTable => override protected def finishPrefix(rest: String) = "" + thisInfo } - class NoArgsTypeRef(pre0: Type, sym0: Symbol) extends TypeRef(pre0, sym0, Nil) with UniqueType { + class NoArgsTypeRef(pre0: Type, sym0: Symbol) extends TypeRef(pre0, sym0, Nil) { // A reference (in a Scala program) to a type that has type parameters, but where the reference // does not include type arguments. Note that it doesn't matter whether the symbol refers // to a java or scala symbol, but it does matter whether it occurs in java or scala code. @@ -2294,15 +2337,32 @@ trait Types extends api.Types { self: SymbolTable => * * @M: a higher-kinded type is represented as a TypeRef with sym.typeParams.nonEmpty, but args.isEmpty */ - abstract case class TypeRef(pre: Type, sym: Symbol, args: List[Type]) extends Type with TypeRefApi { - override val isTrivial: Boolean = !sym.isTypeParameter && pre.isTrivial && args.forall(_.isTrivial) - + abstract case class TypeRef(pre: Type, sym: Symbol, args: List[Type]) extends UniqueType with TypeRefApi { + private var trivial: ThreeValue = UNKNOWN + override def isTrivial: Boolean = { + if (trivial == UNKNOWN) + trivial = fromBoolean(!sym.isTypeParameter && pre.isTrivial && areTrivialTypes(args)) + toBoolean(trivial) + } private[reflect] var parentsCache: List[Type] = _ private[reflect] var parentsPeriod = NoPeriod private[reflect] var baseTypeSeqCache: BaseTypeSeq = _ private[reflect] var baseTypeSeqPeriod = NoPeriod private var normalized: Type = _ + //OPT specialize hashCode + override final def computeHashCode = { + import scala.util.hashing.MurmurHash3._ + val hasArgs = args.nonEmpty + var h = productSeed + h = mix(h, pre.hashCode) + h = mix(h, sym.hashCode) + if (hasArgs) + finalizeHash(mix(h, args.hashCode), 3) + else + finalizeHash(h, 2) + } + // @M: propagate actual type params (args) to `tp`, by replacing // formal type parameters with actual ones. If tp is higher kinded, // the "actual" type arguments are types that simply reference the @@ -2399,7 +2459,7 @@ trait Types extends api.Types { self: SymbolTable => private def needsPreString = ( settings.debug.value || !shorthands(sym.fullName) - || sym.ownerChain.exists(s => !s.isClass) + || (sym.ownersIterator exists (s => !s.isClass)) ) private def preString = if (needsPreString) pre.prefixString else "" private def argsString = if (args.isEmpty) "" else args.mkString("[", ",", "]") @@ -2500,13 +2560,13 @@ trait Types extends api.Types { self: SymbolTable => if (period != currentPeriod) { tpe.baseTypeSeqPeriod = currentPeriod if (!isValidForBaseClasses(period)) { - Statistics.incCounter(typerefBaseTypeSeqCount) - val start = Statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) + if (Statistics.canEnable) Statistics.incCounter(typerefBaseTypeSeqCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) else null try { tpe.baseTypeSeqCache = undetBaseTypeSeq tpe.baseTypeSeqCache = tpe.baseTypeSeqImpl } finally { - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) } } } @@ -2523,14 +2583,22 @@ trait Types extends api.Types { self: SymbolTable => case class MethodType(override val params: List[Symbol], override val resultType: Type) extends Type with MethodTypeApi { - override lazy val isTrivial: Boolean = - isTrivialResult && (params forall isTrivialParam) + private var trivial: ThreeValue = UNKNOWN + override def isTrivial: Boolean = { + if (trivial == UNKNOWN) trivial = fromBoolean(isTrivialResult && areTrivialParams(params)) + toBoolean(trivial) + } private def isTrivialResult = resultType.isTrivial && (resultType eq resultType.withoutAnnotations) - private def isTrivialParam(p: Symbol) = - p.tpe.isTrivial && !(params.exists(_.tpe contains p) || (resultType contains p)) + private def areTrivialParams(ps: List[Symbol]): Boolean = ps match { + case p :: rest => + p.tpe.isTrivial && !typesContain(paramTypes, p) && !(resultType contains p) && + areTrivialParams(rest) + case _ => + true + } def isImplicit = params.nonEmpty && params.head.isImplicit def isJava = false // can we do something like for implicits? I.e. do Java methods without parameters need to be recognized? @@ -2546,13 +2614,19 @@ trait Types extends api.Types { self: SymbolTable => override def resultType(actuals: List[Type]) = if (isTrivial || phase.erasedTypes) resultType - else if (sameLength(actuals, params)) { + else if (/*isDependentMethodType &&*/ sameLength(actuals, params)) { val idm = new InstantiateDependentMap(params, actuals) val res = idm(resultType) existentialAbstraction(idm.existentialsNeeded, res) } else existentialAbstraction(params, resultType) + private var isdepmeth: ThreeValue = UNKNOWN + override def isDependentMethodType: Boolean = { + if (isdepmeth == UNKNOWN) isdepmeth = fromBoolean(IsDependentCollector.collect(resultType)) + toBoolean(isdepmeth) + } + // implicit args can only be depended on in result type: //TODO this may be generalised so that the only constraint is dependencies are acyclic def approximate: MethodType = MethodType(params, resultApprox) @@ -2567,7 +2641,7 @@ trait Types extends api.Types { self: SymbolTable => } override def atOwner(owner: Symbol) = - if ((params exists (_.owner != owner)) || (resultType.atOwner(owner) ne resultType)) + if (!allSymbolsHaveOwner(params, owner) || (resultType.atOwner(owner) ne resultType)) cloneInfo(owner) else this @@ -2655,7 +2729,7 @@ trait Types extends api.Types { self: SymbolTable => } override def atOwner(owner: Symbol) = - if ((typeParams exists (_.owner != owner)) || (resultType.atOwner(owner) ne resultType)) + if (!allSymbolsHaveOwner(typeParams, owner) || (resultType.atOwner(owner) ne resultType)) cloneInfo(owner) else this @@ -2759,7 +2833,7 @@ trait Types extends api.Types { self: SymbolTable => createFromClonedSymbolsAtOwner(quantified, owner, underlying)(newExistentialType) override def atOwner(owner: Symbol) = - if (quantified exists (_.owner != owner)) cloneInfo(owner) else this + if (!allSymbolsHaveOwner(quantified, owner)) cloneInfo(owner) else this override def kind = "ExistentialType" @@ -2870,14 +2944,13 @@ trait Types extends api.Types { self: SymbolTable => * any results. */ if (propagateParameterBoundsToTypeVars) { - val exclude = bounds.isEmptyBounds || bounds.exists(_.typeSymbolDirect.isNonClassType) + val exclude = bounds.isEmptyBounds || (bounds exists typeIsNonClassType) if (exclude) new TypeConstraint else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds)) } else new TypeConstraint } - def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr)) def untouchable(tparam: Symbol): TypeVar = createTypeVar(tparam, untouchable = true) def apply(tparam: Symbol): TypeVar = createTypeVar(tparam, untouchable = false) def apply(origin: Type, constr: TypeConstraint): TypeVar = apply(origin, constr, Nil, Nil) @@ -2890,7 +2963,7 @@ trait Types extends api.Types { self: SymbolTable => val tv = ( if (args.isEmpty && params.isEmpty) { if (untouchable) new TypeVar(origin, constr) with UntouchableTypeVar - else new TypeVar(origin, constr) + else new TypeVar(origin, constr) {} } else if (args.size == params.size) { if (untouchable) new AppliedTypeVar(origin, constr, params zip args) with UntouchableTypeVar @@ -2916,13 +2989,12 @@ trait Types extends api.Types { self: SymbolTable => if (tp == NoType) tp else existentialAbstraction(existentialsInType(tp), tp) ) + def containsExistential(tpe: Type) = - tpe exists (_.typeSymbol.isExistentiallyBound) + tpe exists typeIsExistentiallyBound - def existentialsInType(tpe: Type) = ( - for (tp <- tpe ; if tp.typeSymbol.isExistentiallyBound) yield - tp.typeSymbol - ) + def existentialsInType(tpe: Type) = + tpe withFilter typeIsExistentiallyBound map (_.typeSymbol) /** Precondition: params.nonEmpty. (args.nonEmpty enforced structurally.) */ @@ -2981,9 +3053,9 @@ trait Types extends api.Types { self: SymbolTable => * * Precondition for this class, enforced structurally: args.isEmpty && params.isEmpty. */ - class TypeVar( + abstract case class TypeVar( val origin: Type, - val constr0: TypeConstraint + var constr: TypeConstraint ) extends Type { def untouchable = false // by other typevars override def params: List[Symbol] = Nil @@ -2996,7 +3068,7 @@ trait Types extends api.Types { self: SymbolTable => * in operations that are exposed from types. Hence, no syncing of `constr` * or `encounteredHigherLevel` or `suspended` accesses should be necessary. */ - var constr = constr0 +// var constr = constr0 def instValid = constr.instValid override def isGround = instValid && constr.inst.isGround @@ -3395,16 +3467,16 @@ trait Types extends api.Types { self: SymbolTable => } } - /** A temporary type representing the reasure of a user-defined value type. - * Created during phase reasure, elimintaed again in posterasure. + /** A temporary type representing the erasure of a user-defined value type. + * Created during phase reasure, eliminated again in posterasure. * @param sym The value class symbol * @param underlying The underlying type before erasure */ - abstract case class ErasedValueType(original: TypeRef) extends Type { + abstract case class ErasedValueType(original: TypeRef) extends UniqueType { override def safeToString = "ErasedValueType("+original+")" } - final class UniqueErasedValueType(original: TypeRef) extends ErasedValueType(original) with UniqueType + final class UniqueErasedValueType(original: TypeRef) extends ErasedValueType(original) object ErasedValueType { def apply(original: TypeRef): Type = { @@ -3715,10 +3787,11 @@ trait Types extends api.Types { self: SymbolTable => case TypeRef(_, SingletonClass, _) => AnyClass.tpe case tp1 @ RefinedType(parents, decls) => - var parents1 = parents filter (_.typeSymbol != SingletonClass) - if (parents1.isEmpty) parents1 = List(AnyClass.tpe) - if (parents1.tail.isEmpty && decls.isEmpty) mapOver(parents1.head) - else mapOver(copyRefinedType(tp1, parents1, decls)) + parents filter (_.typeSymbol != SingletonClass) match { + case Nil => AnyClass.tpe + case p :: Nil if decls.isEmpty => mapOver(p) + case ps => mapOver(copyRefinedType(tp1, ps, decls)) + } case tp1 => mapOver(tp1) } @@ -3823,7 +3896,7 @@ trait Types extends api.Types { self: SymbolTable => private var uniqueRunId = NoRunId protected def unique[T <: Type](tp: T): T = { - Statistics.incCounter(rawTypeCount) + if (Statistics.canEnable) Statistics.incCounter(rawTypeCount) if (uniqueRunId != currentRunId) { uniques = util.HashSet[Type]("uniques", initialUniquesCapacity) perRunCaches.recordCache(uniques) @@ -3860,8 +3933,8 @@ trait Types extends api.Types { self: SymbolTable => * guarding addLoBound/addHiBound somehow broke raw types so it * only guards against being created with them.] */ - private var lobounds = lo0 filterNot (_.typeSymbolDirect eq NothingClass) - private var hibounds = hi0 filterNot (_.typeSymbolDirect eq AnyClass) + private var lobounds = lo0 filterNot typeIsNothing + private var hibounds = hi0 filterNot typeIsAny private var numlo = numlo0 private var numhi = numhi0 private var avoidWidening = avoidWidening0 @@ -3917,8 +3990,8 @@ trait Types extends api.Types { self: SymbolTable => override def toString = { val boundsStr = { - val lo = loBounds filterNot (_.typeSymbolDirect eq NothingClass) - val hi = hiBounds filterNot (_.typeSymbolDirect eq AnyClass) + val lo = loBounds filterNot typeIsNothing + val hi = hiBounds filterNot typeIsAny val lostr = if (lo.isEmpty) Nil else List(lo.mkString(" >: (", ", ", ")")) val histr = if (hi.isEmpty) Nil else List(hi.mkString(" <: (", ", ", ")")) @@ -3965,15 +4038,18 @@ trait Types extends api.Types { self: SymbolTable => override def variance = _variance def variance_=(x: Int) = _variance = x - override protected def noChangeToSymbols(origSyms: List[Symbol]) = { - origSyms forall { sym => - val v = variance - if (sym.isAliasType) variance = 0 - val result = this(sym.info) - variance = v - result eq sym.info + override protected def noChangeToSymbols(origSyms: List[Symbol]) = + //OPT inline from forall to save on #closures + origSyms match { + case sym :: rest => + val v = variance + if (sym.isAliasType) variance = 0 + val result = this(sym.info) + variance = v + (result eq sym.info) && noChangeToSymbols(rest) + case _ => + true } - } override protected def mapOverArgs(args: List[Type], tparams: List[Symbol]): List[Type] = map2Conserve(args, tparams) { (arg, tparam) => @@ -4478,7 +4554,7 @@ trait Types extends api.Types { self: SymbolTable => if (sameLength(basesym.typeParams, baseargs)) instParam(basesym.typeParams, baseargs) else - if (symclazz.tpe.parents.exists(_.isErroneous)) + if (symclazz.tpe.parents exists typeIsErroneous) ErrorType // don't be to overzealous with throwing exceptions, see #2641 else throw new Error( @@ -4529,7 +4605,7 @@ trait Types extends api.Types { self: SymbolTable => else subst(tp, sym, from.tail, to.tail) val boundSyms = tp0.boundSyms - val tp1 = if (boundSyms exists from.contains) renameBoundSyms(tp0) else tp0 + val tp1 = if (boundSyms.nonEmpty && (boundSyms exists from.contains)) renameBoundSyms(tp0) else tp0 val tp = mapOver(tp1) tp match { @@ -4569,10 +4645,12 @@ trait Types extends api.Types { self: SymbolTable => tp match { case TypeRef(pre, sym, args) if pre ne NoPrefix => val newSym = subst(sym, from, to) + // mapOver takes care of subst'ing in args + mapOver ( if (sym eq newSym) tp else copyTypeRef(tp, pre, newSym, args) ) // assert(newSym.typeParams.length == sym.typeParams.length, "typars mismatch in SubstSymMap: "+(sym, sym.typeParams, newSym, newSym.typeParams)) - mapOver(copyTypeRef(tp, pre, newSym, args)) // mapOver takes care of subst'ing in args case SingleType(pre, sym) if pre ne NoPrefix => - mapOver(singleType(pre, subst(sym, from, to))) + val newSym = subst(sym, from, to) + mapOver( if (sym eq newSym) tp else singleType(pre, newSym) ) case _ => super.apply(tp) } @@ -4668,6 +4746,8 @@ trait Types extends api.Types { self: SymbolTable => else mapOver(tp) } + /** Note: This map is needed even for non-dependent method types, despite what the name might imply. + */ class InstantiateDependentMap(params: List[Symbol], actuals0: List[Type]) extends TypeMap with KeepOnlyTypeConstraints { private val actuals = actuals0.toIndexedSeq private val existentials = new Array[Symbol](actuals.size) @@ -5101,14 +5181,21 @@ trait Types extends api.Types { self: SymbolTable => 1 } - private def maxDepth(tps: Seq[Type], by: Type => Int): Int = { - var d = 0 - for (tp <- tps) d = d max by(tp) - d + private def maxDepth(tps: List[Type], by: Type => Int): Int = { + //OPT replaced with tailrecursive function to save on #closures + // was: + // var d = 0 + // for (tp <- tps) d = d max by(tp) //!!!OPT!!! + // d + def loop(tps: List[Type], acc: Int): Int = tps match { + case tp :: rest => loop(rest, acc max by(tp)) + case _ => acc + } + loop(tps, 0) } - private def typeDepth(tps: Seq[Type]): Int = maxDepth(tps, typeDepth) - private def baseTypeSeqDepth(tps: Seq[Type]): Int = maxDepth(tps, _.baseTypeSeqDepth) + private def typeDepth(tps: List[Type]): Int = maxDepth(tps, typeDepth) + private def baseTypeSeqDepth(tps: List[Type]): Int = maxDepth(tps, _.baseTypeSeqDepth) /** Is intersection of given types populated? That is, * for all types tp1, tp2 in intersection @@ -5217,11 +5304,24 @@ trait Types extends api.Types { self: SymbolTable => /** Do `tp1` and `tp2` denote equivalent types? */ def isSameType(tp1: Type, tp2: Type): Boolean = try { - Statistics.incCounter(sametypeCount) + if (Statistics.canEnable) Statistics.incCounter(sametypeCount) subsametypeRecursions += 1 - undoLog undoUnless { - isSameType1(tp1, tp2) - } + //OPT cutdown on Function0 allocation + //was: +// undoLog undoUnless { +// isSameType1(tp1, tp2) +// } + + undoLog.lock() + try { + val before = undoLog.log + var result = false + + try result = { + isSameType1(tp1, tp2) + } finally if (!result) undoLog.undoTo(before) + result + } finally undoLog.unlock() } finally { subsametypeRecursions -= 1 // XXX AM TODO: figure out when it is safe and needed to clear the log -- the commented approach below is too eager (it breaks #3281, #3866) @@ -5572,22 +5672,49 @@ trait Types extends api.Types { self: SymbolTable => def isSubType(tp1: Type, tp2: Type, depth: Int): Boolean = try { subsametypeRecursions += 1 - undoLog undoUnless { // if subtype test fails, it should not affect constraints on typevars - if (subsametypeRecursions >= LogPendingSubTypesThreshold) { - val p = new SubTypePair(tp1, tp2) - if (pendingSubTypes(p)) - false - else - try { - pendingSubTypes += p - isSubType2(tp1, tp2, depth) - } finally { - pendingSubTypes -= p - } - } else { - isSubType2(tp1, tp2, depth) - } - } + //OPT cutdown on Function0 allocation + //was: +// undoLog undoUnless { // if subtype test fails, it should not affect constraints on typevars +// if (subsametypeRecursions >= LogPendingSubTypesThreshold) { +// val p = new SubTypePair(tp1, tp2) +// if (pendingSubTypes(p)) +// false +// else +// try { +// pendingSubTypes += p +// isSubType2(tp1, tp2, depth) +// } finally { +// pendingSubTypes -= p +// } +// } else { +// isSubType2(tp1, tp2, depth) +// } +// } + + undoLog.lock() + try { + val before = undoLog.log + var result = false + + try result = { // if subtype test fails, it should not affect constraints on typevars + if (subsametypeRecursions >= LogPendingSubTypesThreshold) { + val p = new SubTypePair(tp1, tp2) + if (pendingSubTypes(p)) + false + else + try { + pendingSubTypes += p + isSubType2(tp1, tp2, depth) + } finally { + pendingSubTypes -= p + } + } else { + isSubType2(tp1, tp2, depth) + } + } finally if (!result) undoLog.undoTo(before) + + result + } finally undoLog.unlock() } finally { subsametypeRecursions -= 1 // XXX AM TODO: figure out when it is safe and needed to clear the log -- the commented approach below is too eager (it breaks #3281, #3866) @@ -5611,6 +5738,7 @@ trait Types extends api.Types { self: SymbolTable => false } + @deprecated("The compiler doesn't use this so you shouldn't either - it will be removed", "2.10.0") def instTypeVar(tp: Type): Type = tp match { case TypeRef(pre, sym, args) => copyTypeRef(tp, instTypeVar(pre), sym, args) @@ -5893,9 +6021,17 @@ trait Types extends api.Types { self: SymbolTable => def specializesSym(tp: Type, sym: Symbol, depth: Int): Boolean = tp.typeSymbol == NothingClass || - tp.typeSymbol == NullClass && containsNull(sym.owner) || - (tp.nonPrivateMember(sym.name).alternatives exists - (alt => sym == alt || specializesSym(tp.narrow, alt, sym.owner.thisType, sym, depth))) + tp.typeSymbol == NullClass && containsNull(sym.owner) || { + def specializedBy(membr: Symbol): Boolean = + membr == sym || specializesSym(tp.narrow, membr, sym.owner.thisType, sym, depth) + val member = tp.nonPrivateMember(sym.name) + if (member eq NoSymbol) false + else if (member.isOverloaded) member.alternatives exists specializedBy + else specializedBy(member) + // was + // (tp.nonPrivateMember(sym.name).alternatives exists + // (alt => sym == alt || specializesSym(tp.narrow, alt, sym.owner.thisType, sym, depth))) + } /** Does member `sym1` of `tp1` have a stronger type * than member `sym2` of `tp2`? @@ -6141,9 +6277,9 @@ trait Types extends api.Types { self: SymbolTable => */ def isWithinBounds(pre: Type, owner: Symbol, tparams: List[Symbol], targs: List[Type]): Boolean = { var bounds = instantiatedBounds(pre, owner, tparams, targs) - if (targs.exists(_.annotations.nonEmpty)) + if (targs exists typeHasAnnotations) bounds = adaptBoundsToAnnotations(bounds, tparams, targs) - (bounds corresponds targs)(_ containsType _) + (bounds corresponds targs)(boundsContainType) } def instantiatedBounds(pre: Type, owner: Symbol, tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = @@ -6211,7 +6347,7 @@ trait Types extends api.Types { self: SymbolTable => private def lubList(ts: List[Type], depth: Int): List[Type] = { // Matching the type params of one of the initial types means dummies. val initialTypeParams = ts map (_.typeParams) - def isHotForTs(xs: List[Type]) = initialTypeParams contains xs.map(_.typeSymbol) + def isHotForTs(xs: List[Type]) = initialTypeParams contains (xs map (_.typeSymbol)) def elimHigherOrderTypeParam(tp: Type) = tp match { case TypeRef(pre, sym, args) if args.nonEmpty && isHotForTs(args) => tp.typeConstructor @@ -6221,7 +6357,7 @@ trait Types extends api.Types { self: SymbolTable => def loop(tsBts: List[List[Type]]): List[Type] = { lubListDepth += 1 - if (tsBts.isEmpty || tsBts.exists(_.isEmpty)) Nil + if (tsBts.isEmpty || (tsBts exists typeListIsEmpty)) Nil else if (tsBts.tail.isEmpty) tsBts.head else { // ts0 is the 1-dimensional frontier of symbols cutting through 2-dimensional tsBts. @@ -6303,10 +6439,12 @@ trait Types extends api.Types { self: SymbolTable => * of some other element of the list. */ private def elimSuper(ts: List[Type]): List[Type] = ts match { case List() => List() + case List(t) => List(t) case t :: ts1 => val rest = elimSuper(ts1 filter (t1 => !(t <:< t1))) if (rest exists (t1 => t1 <:< t)) rest else t :: rest } + def elimAnonymousClass(t: Type) = t match { case TypeRef(pre, clazz, Nil) if clazz.isAnonymousClass => clazz.classBound.asSeenFrom(pre, clazz.owner) @@ -6323,6 +6461,7 @@ trait Types extends api.Types { self: SymbolTable => private def elimSub(ts: List[Type], depth: Int): List[Type] = { def elimSub0(ts: List[Type]): List[Type] = ts match { case List() => List() + case List(t) => List(t) case t :: ts1 => val rest = elimSub0(ts1 filter (t1 => !isSubType(t1, t, decr(depth)))) if (rest exists (t1 => isSubType(t, t1, decr(depth)))) rest else t :: rest @@ -6356,7 +6495,7 @@ trait Types extends api.Types { self: SymbolTable => def weakLub(ts: List[Type]) = if (ts.nonEmpty && (ts forall isNumericValueType)) (numericLub(ts), true) - else if (ts.nonEmpty && (ts exists (_.annotations.nonEmpty))) + else if (ts exists typeHasAnnotations) (annotationsLub(lub(ts map (_.withoutAnnotations)), ts), true) else (lub(ts), false) @@ -6365,7 +6504,7 @@ trait Types extends api.Types { self: SymbolTable => val nglb = numericGlb(ts) if (nglb != NoType) (nglb, true) else (glb(ts), false) - } else if (ts.nonEmpty && (ts exists (_.annotations.nonEmpty))) { + } else if (ts exists typeHasAnnotations) { (annotationsGlb(glb(ts map (_.withoutAnnotations)), ts), true) } else (glb(ts), false) } @@ -6421,14 +6560,14 @@ trait Types extends api.Types { self: SymbolTable => case List() => NothingClass.tpe case List(t) => t case _ => - Statistics.incCounter(lubCount) - val start = Statistics.pushTimer(typeOpsStack, lubNanos) + if (Statistics.canEnable) Statistics.incCounter(lubCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, lubNanos) else null try { lub(ts, lubDepth(ts)) } finally { lubResults.clear() glbResults.clear() - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) } } @@ -6441,8 +6580,8 @@ trait Types extends api.Types { self: SymbolTable => val tparams1 = map2(tparams, matchingBounds(ts, tparams).transpose)((tparam, bounds) => tparam.cloneSymbol.setInfo(glb(bounds, depth))) PolyType(tparams1, lub0(matchingInstTypes(ts, tparams1))) - case ts @ MethodType(params, _) :: rest => - MethodType(params, lub0(matchingRestypes(ts, params map (_.tpe)))) + case ts @ (mt @ MethodType(params, _)) :: rest => + MethodType(params, lub0(matchingRestypes(ts, mt.paramTypes))) case ts @ NullaryMethodType(_) :: rest => NullaryMethodType(lub0(matchingRestypes(ts, Nil))) case ts @ TypeBounds(_, _) :: rest => @@ -6544,13 +6683,13 @@ trait Types extends api.Types { self: SymbolTable => indent = indent + " " assert(indent.length <= 100) } - Statistics.incCounter(nestedLubCount) + if (Statistics.canEnable) Statistics.incCounter(nestedLubCount) val res = lub0(ts) if (printLubs) { indent = indent stripSuffix " " println(indent + "lub of " + ts + " is " + res)//debug } - if (ts forall (_.isNotNull)) res.notNull else res + if (ts forall typeIsNotNull) res.notNull else res } val GlbFailure = new Throwable @@ -6569,14 +6708,14 @@ trait Types extends api.Types { self: SymbolTable => case List() => AnyClass.tpe case List(t) => t case ts0 => - Statistics.incCounter(lubCount) - val start = Statistics.pushTimer(typeOpsStack, lubNanos) + if (Statistics.canEnable) Statistics.incCounter(lubCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, lubNanos) else null try { glbNorm(ts0, lubDepth(ts0)) } finally { lubResults.clear() glbResults.clear() - Statistics.popTimer(typeOpsStack, start) + if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) } } @@ -6596,8 +6735,8 @@ trait Types extends api.Types { self: SymbolTable => val tparams1 = map2(tparams, matchingBounds(ts, tparams).transpose)((tparam, bounds) => tparam.cloneSymbol.setInfo(lub(bounds, depth))) PolyType(tparams1, glbNorm(matchingInstTypes(ts, tparams1), depth)) - case ts @ MethodType(params, _) :: rest => - MethodType(params, glbNorm(matchingRestypes(ts, params map (_.tpe)), depth)) + case ts @ (mt @ MethodType(params, _)) :: rest => + MethodType(params, glbNorm(matchingRestypes(ts, mt.paramTypes), depth)) case ts @ NullaryMethodType(_) :: rest => NullaryMethodType(glbNorm(matchingRestypes(ts, Nil), depth)) case ts @ TypeBounds(_, _) :: rest => @@ -6691,12 +6830,12 @@ trait Types extends api.Types { self: SymbolTable => } // if (settings.debug.value) { println(indent + "glb of " + ts + " at depth "+depth); indent = indent + " " } //DEBUG - Statistics.incCounter(nestedLubCount) + if (Statistics.canEnable) Statistics.incCounter(nestedLubCount) val res = glb0(ts) // if (settings.debug.value) { indent = indent.substring(0, indent.length() - 2); log(indent + "glb of " + ts + " is " + res) }//DEBUG - if (ts exists (_.isNotNull)) res.notNull else res + if (ts exists typeIsNotNull) res.notNull else res } /** A list of the typevars in a type. */ @@ -6738,7 +6877,7 @@ trait Types extends api.Types { self: SymbolTable => // special treatment for lubs of array types after erasure: // if argss contain one value type and some other type, the lub is Object // if argss contain several reference types, the lub is an array over lub of argtypes - if (argss exists (_.isEmpty)) { + if (argss exists typeListIsEmpty) { None // something is wrong: an array without a type arg. } else { val args = argss map (_.head) @@ -6860,7 +6999,7 @@ trait Types extends api.Types { self: SymbolTable => */ private def matchingRestypes(tps: List[Type], pts: List[Type]): List[Type] = tps map { - case MethodType(params1, res) if (isSameTypes(params1 map (_.tpe), pts)) => + case mt @ MethodType(params1, res) if isSameTypes(mt.paramTypes, pts) => res case NullaryMethodType(res) if pts.isEmpty => res @@ -6931,7 +7070,7 @@ trait Types extends api.Types { self: SymbolTable => } // Add serializable to a list of parents, unless one of them already is def addSerializable(ps: Type*): List[Type] = ( - if (ps exists (_ <:< SerializableClass.tpe)) ps.toList + if (ps exists typeIsSubTypeOfSerializable) ps.toList else (ps :+ SerializableClass.tpe).toList ) @@ -6972,6 +7111,35 @@ trait Types extends api.Types { self: SymbolTable => tostringRecursions -= 1 } +// ----- Hoisted closures and convenience methods, for compile time reductions ------- + + private[scala] val typeIsNotNull = (tp: Type) => tp.isNotNull + private[scala] val isTypeVar = (tp: Type) => tp.isInstanceOf[TypeVar] + private[scala] val typeContainsTypeVar = (tp: Type) => tp exists isTypeVar + private[scala] val typeIsNonClassType = (tp: Type) => tp.typeSymbolDirect.isNonClassType + private[scala] val typeIsExistentiallyBound = (tp: Type) => tp.typeSymbol.isExistentiallyBound + private[scala] val typeIsErroneous = (tp: Type) => tp.isErroneous + private[scala] val typeIsError = (tp: Type) => tp.isError + private[scala] val typeHasAnnotations = (tp: Type) => tp.annotations.nonEmpty + private[scala] val boundsContainType = (bounds: TypeBounds, tp: Type) => bounds containsType tp + private[scala] val typeListIsEmpty = (ts: List[Type]) => ts.isEmpty + private[scala] val typeIsSubTypeOfSerializable = (tp: Type) => tp <:< SerializableClass.tpe + private[scala] val typeIsNothing = (tp: Type) => tp.typeSymbolDirect eq NothingClass + private[scala] val typeIsAny = (tp: Type) => tp.typeSymbolDirect eq AnyClass + private[scala] val typeIsHigherKinded = (tp: Type) => tp.isHigherKinded + + @tailrec private def typesContain(tps: List[Type], sym: Symbol): Boolean = tps match { + case tp :: rest => (tp contains sym) || typesContain(rest, sym) + case _ => false + } + + @tailrec private def areTrivialTypes(tps: List[Type]): Boolean = tps match { + case tp :: rest => tp.isTrivial && areTrivialTypes(rest) + case _ => true + } + +// -------------- Classtags -------------------------------------------------------- + implicit val AnnotatedTypeTag = ClassTag[AnnotatedType](classOf[AnnotatedType]) implicit val BoundedWildcardTypeTag = ClassTag[BoundedWildcardType](classOf[BoundedWildcardType]) implicit val ClassInfoTypeTag = ClassTag[ClassInfoType](classOf[ClassInfoType]) @@ -6990,7 +7158,10 @@ trait Types extends api.Types { self: SymbolTable => implicit val TypeRefTag = ClassTag[TypeRef](classOf[TypeRef]) implicit val TypeTagg = ClassTag[Type](classOf[Type]) +// -------------- Statistics -------------------------------------------------------- + Statistics.newView("#unique types") { if (uniques == null) 0 else uniques.size } + } object TypesStats { @@ -7018,9 +7189,11 @@ object TypesStats { val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) val typeOpsStack = Statistics.newTimerStack() + /** Commented out, because right now this does not inline, so creates a closure which will distort statistics @inline final def timedTypeOp[T](c: Statistics.StackableTimer)(op: => T): T = { val start = Statistics.pushTimer(typeOpsStack, c) try op - finally Statistics.popTimer(typeOpsStack, start) + finally } + */ } diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 9234ccca7b..2e00316d5b 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -769,8 +769,21 @@ abstract class UnPickler /*extends reflect.generic.UnPickler*/ { } /* Read a reference to a pickled item */ + protected def readSymbolRef(): Symbol = {//OPT inlined from: at(readNat(), readSymbol) to save on closure creation + val i = readNat() + var r = entries(i) + if (r eq null) { + val savedIndex = readIndex + readIndex = index(i) + r = readSymbol() + assert(entries(i) eq null, entries(i)) + entries(i) = r + readIndex = savedIndex + } + r.asInstanceOf[Symbol] + } + protected def readNameRef(): Name = at(readNat(), readName) - protected def readSymbolRef(): Symbol = at(readNat(), readSymbol) protected def readTypeRef(): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... () protected def readConstantRef(): Constant = at(readNat(), readConstant) protected def readAnnotationRef(): AnnotationInfo = at(readNat(), readAnnotation) diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 368d55a59c..cc5b5bb406 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -141,7 +141,7 @@ trait Erasure { if (restpe.typeSymbol == UnitClass) erasedTypeRef(UnitClass) // this replaces each typeref that refers to an argument // by the type `p.tpe` of the actual argument p (p in params) - else apply(mt.resultType(params map (_.tpe)))) + else apply(mt.resultType(mt.paramTypes))) case RefinedType(parents, decls) => apply(mergeParents(parents)) case AnnotatedType(_, atp, _) => @@ -220,7 +220,7 @@ trait Erasure { MethodType( cloneSymbolsAndModify(params, specialErasureAvoiding(clazz, _)), if (restpe.typeSymbol == UnitClass) erasedTypeRef(UnitClass) - else specialErasureAvoiding(clazz, (mt.resultType(params map (_.tpe))))) + else specialErasureAvoiding(clazz, (mt.resultType(mt.paramTypes)))) case TypeRef(pre, `clazz`, args) => typeRef(pre, clazz, List()) case _ => diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index 2ac0e64edd..14b5d3003d 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -69,7 +69,7 @@ trait Collections { } lb.toList } - + final def flatCollect[A, B](elems: List[A])(pf: PartialFunction[A, Traversable[B]]): List[B] = { val lb = new ListBuffer[B] for (x <- elems ; if pf isDefinedAt x) @@ -104,7 +104,7 @@ trait Collections { index += 1 } } - + // @inline final def findOrElse[A](xs: TraversableOnce[A])(p: A => Boolean)(orElse: => A): A = { xs find p getOrElse orElse diff --git a/src/reflect/scala/reflect/internal/util/Statistics.scala b/src/reflect/scala/reflect/internal/util/Statistics.scala index e503d812e6..f7b81ca252 100644 --- a/src/reflect/scala/reflect/internal/util/Statistics.scala +++ b/src/reflect/scala/reflect/internal/util/Statistics.scala @@ -237,6 +237,23 @@ quant) private var _enabled = false private val qs = new mutable.HashMap[String, Quantity] + /** replace with + * + * final val canEnable = false + * + * to remove all Statistics code from build + */ + final val canEnable = _enabled + + /** replace with + * + * final def hotEnabled = _enabled + * + * and rebuild, to also count tiny but super-hot methods + * such as phase, flags, owner, name. + */ + final val hotEnabled = false + def enabled = _enabled def enabled_=(cond: Boolean) = { if (cond && !_enabled) { @@ -253,9 +270,4 @@ quant) _enabled = true } } - - /** replace rhs with enabled and rebuild to also count tiny but super-hot methods - * such as phase, flags, owner, name. - */ - final val hotEnabled = false } diff --git a/src/reflect/scala/reflect/internal/util/ThreeValues.scala b/src/reflect/scala/reflect/internal/util/ThreeValues.scala new file mode 100644 index 0000000000..d89f11c407 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/ThreeValues.scala @@ -0,0 +1,14 @@ +package scala.reflect.internal.util + +/** A simple three value type for booleans with an unknown value */ +object ThreeValues { + + type ThreeValue = Byte + + final val YES = 1 + final val NO = -1 + final val UNKNOWN = 0 + + @inline def fromBoolean(b: Boolean): ThreeValue = if (b) YES else NO + @inline def toBoolean(x: ThreeValue): Boolean = x == YES +} diff --git a/src/reflect/scala/reflect/macros/Aliases.scala b/src/reflect/scala/reflect/macros/Aliases.scala index 46b7066902..ad100d7e89 100644 --- a/src/reflect/scala/reflect/macros/Aliases.scala +++ b/src/reflect/scala/reflect/macros/Aliases.scala @@ -21,6 +21,8 @@ trait Aliases { type TypeTag[T] = universe.TypeTag[T] val AbsTypeTag = universe.AbsTypeTag val TypeTag = universe.TypeTag + def absTypeTag[T](implicit attag: AbsTypeTag[T]) = attag def typeTag[T](implicit ttag: TypeTag[T]) = ttag + def absTypeOf[T](implicit attag: AbsTypeTag[T]): Type = attag.tpe def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 967ac69148..9f2c3fc79c 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -124,7 +124,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym private def ErrorInnerModule(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") private def ErrorStaticClass(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") private def ErrorStaticModule(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") - private def ErrorNotMember(wannabe: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${wannabe.kind} ${wannabe.fullName}") + private def ErrorNotMember(wannabe: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${wannabe.kindString} ${wannabe.fullName}") private def ErrorNotField(wannabe: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $wannabe") private def ErrorNonExistentField(wannabe: Symbol) = throw new ScalaReflectionException(s""" |Scala field ${wannabe.name} isn't represented as a Java field, neither it has a Java accessor method diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala index e1eb7a57fe..de029ca658 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala @@ -14,15 +14,10 @@ trait SynchronizedTypes extends internal.Types { self: SymbolTable => override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { super.unique(tp) } class SynchronizedUndoLog extends UndoLog { + private val actualLock = new java.util.concurrent.locks.ReentrantLock - override def clear() = - synchronized { super.clear() } - - override def undo[T](block: => T): T = - synchronized { super.undo(block) } - - override def undoUnless(block: => Boolean): Boolean = - synchronized { super.undoUnless(block) } + final override def lock(): Unit = actualLock.lock() + final override def unlock(): Unit = actualLock.unlock() } override protected def newUndoLog = new SynchronizedUndoLog diff --git a/src/reflect/scala/tools/nsc/io/ZipArchive.scala b/src/reflect/scala/tools/nsc/io/ZipArchive.scala index 852dba9ec8..d1a91294a5 100644 --- a/src/reflect/scala/tools/nsc/io/ZipArchive.scala +++ b/src/reflect/scala/tools/nsc/io/ZipArchive.scala @@ -96,14 +96,25 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq } } - private def ensureDir(dirs: mutable.Map[String, DirEntry], path: String, zipEntry: ZipEntry): DirEntry = { - dirs.getOrElseUpdate(path, { - val parent = ensureDir(dirs, dirName(path), null) - val dir = new DirEntry(path) - parent.entries(baseName(path)) = dir - dir - }) - } + private def ensureDir(dirs: mutable.Map[String, DirEntry], path: String, zipEntry: ZipEntry): DirEntry = + //OPT inlined from getOrElseUpdate; saves ~50K closures on test run. + // was: + // dirs.getOrElseUpdate(path, { + // val parent = ensureDir(dirs, dirName(path), null) + // val dir = new DirEntry(path) + // parent.entries(baseName(path)) = dir + // dir + // }) + dirs get path match { + case Some(v) => v + case None => + val parent = ensureDir(dirs, dirName(path), null) + val dir = new DirEntry(path) + parent.entries(baseName(path)) = dir + dirs(path) = dir + dir + } + protected def getDir(dirs: mutable.Map[String, DirEntry], entry: ZipEntry): DirEntry = { if (entry.isDirectory) ensureDir(dirs, entry.getName, entry) else ensureDir(dirs, dirName(entry.getName), null) diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala index a8a9c65f63..49c272cc28 100644 --- a/src/scalap/scala/tools/scalap/Main.scala +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -10,10 +10,10 @@ package scala.tools.scalap import java.io.{ PrintStream, OutputStreamWriter, ByteArrayOutputStream } import scala.reflect.NameTransformer import scalax.rules.scalasig._ -import tools.nsc.util.{ ClassPath, JavaClassPath } -import tools.util.PathResolver +import scala.tools.nsc.util.{ ClassPath, JavaClassPath } +import scala.tools.util.PathResolver import ClassPath.DefaultJavaContext -import tools.nsc.io.{ PlainFile, AbstractFile } +import scala.tools.nsc.io.{ PlainFile, AbstractFile } /**The main object used to execute scalap on the command-line. * diff --git a/test/files/jvm/future-spec/TryTests.scala b/test/files/jvm/future-spec/TryTests.scala index 82ca12276f..5d1b9b84b4 100644 --- a/test/files/jvm/future-spec/TryTests.scala +++ b/test/files/jvm/future-spec/TryTests.scala @@ -46,6 +46,12 @@ object TryTests extends MinimalScalaTest { val e2 = new Exception Failure[Int](e) map(_ => throw e2) mustEqual Failure(e) } + "when there is a fatal exception" in { + val e3 = new ThreadDeath + intercept[ThreadDeath] { + Success(1) map (_ => throw e3) + } + } } "flatMap" in { @@ -60,6 +66,12 @@ object TryTests extends MinimalScalaTest { val e2 = new Exception Failure[Int](e).flatMap[Int](_ => throw e2) mustEqual Failure(e) } + "when there is a fatal exception" in { + val e3 = new ThreadDeath + intercept[ThreadDeath] { + Success(1).flatMap[Int](_ => throw e3) + } + } } "flatten" in { @@ -115,4 +127,4 @@ object TryTests extends MinimalScalaTest { } } } -}
\ No newline at end of file +} diff --git a/test/files/jvm/manifests-new.scala b/test/files/jvm/manifests-new.scala index 8706881640..f730be67bb 100644 --- a/test/files/jvm/manifests-new.scala +++ b/test/files/jvm/manifests-new.scala @@ -106,6 +106,6 @@ trait TestUtil { // val t1: TypeTag[T] = read(write(t)) val t1: TypeTag[T] = t val x1 = x.toString.replaceAll("@[0-9a-z]+$", "") - println("x="+x1+", t="+t1+", k="+t1.tpe.kind+", s="+t1.tpe.typeSymbol.toString) + println("x="+x1+", t="+t1+", k="+t1.tpe.asInstanceOf[Product].productPrefix+", s="+t1.tpe.typeSymbol.toString) } }
\ No newline at end of file diff --git a/test/files/neg/javaConversions-2.10-ambiguity.check b/test/files/neg/javaConversions-2.10-ambiguity.check new file mode 100644 index 0000000000..c064a22964 --- /dev/null +++ b/test/files/neg/javaConversions-2.10-ambiguity.check @@ -0,0 +1,6 @@ +javaConversions-2.10-ambiguity.scala:8: error: type mismatch; + found : scala.collection.concurrent.Map[String,String] + required: scala.collection.mutable.ConcurrentMap[String,String] + assertType[mutable.ConcurrentMap[String, String]](a) + ^ +one error found diff --git a/test/files/neg/javaConversions-2.10-ambiguity.scala b/test/files/neg/javaConversions-2.10-ambiguity.scala new file mode 100644 index 0000000000..e856846a29 --- /dev/null +++ b/test/files/neg/javaConversions-2.10-ambiguity.scala @@ -0,0 +1,10 @@ +import collection.{JavaConversions, mutable, concurrent} +import JavaConversions._ +import java.util.concurrent.{ConcurrentHashMap => CHM} + +object Bar { + def assertType[T](t: T) = t + val a = new CHM[String, String]() += (("", "")) + assertType[mutable.ConcurrentMap[String, String]](a) +} +// vim: set et: diff --git a/test/files/neg/macro-invalidshape-a.check b/test/files/neg/macro-invalidshape-a.check index 246b5c3226..098ec35a00 100644 --- a/test/files/neg/macro-invalidshape-a.check +++ b/test/files/neg/macro-invalidshape-a.check @@ -1,6 +1,5 @@ Macros_Test_2.scala:2: error: macro body has wrong shape:
- required: macro <reference to implementation object>.<implementation method name>
- or : macro <implementation method name>
+ required: macro [<implementation object>].<method name>[[<type args>]]
def foo(x: Any) = macro 2
^
one error found
diff --git a/test/files/neg/macro-invalidshape-b.check b/test/files/neg/macro-invalidshape-b.check index 59701d023b..297ff69199 100644 --- a/test/files/neg/macro-invalidshape-b.check +++ b/test/files/neg/macro-invalidshape-b.check @@ -1,6 +1,5 @@ Macros_Test_2.scala:2: error: macro body has wrong shape:
- required: macro <reference to implementation object>.<implementation method name>
- or : macro <implementation method name>
+ required: macro [<implementation object>].<method name>[[<type args>]]
def foo(x: Any) = macro Impls.foo(null)(null)
^
one error found
diff --git a/test/files/neg/macro-invalidshape-c.check b/test/files/neg/macro-invalidshape-c.check index 84d8c35222..6513df166e 100644 --- a/test/files/neg/macro-invalidshape-c.check +++ b/test/files/neg/macro-invalidshape-c.check @@ -1,6 +1,9 @@ -Macros_Test_2.scala:2: error: macro body has wrong shape:
- required: macro <reference to implementation object>.<implementation method name>
- or : macro <implementation method name>
+Macros_Test_2.scala:2: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
def foo(x: Any) = macro {2; Impls.foo}
- ^
+ ^
+Macros_Test_2.scala:2: error: missing arguments for method foo in object Impls;
+follow this method with `_' if you want to treat it as a partially applied function
+ def foo(x: Any) = macro {2; Impls.foo}
+ ^
+one warning found
one error found
diff --git a/test/files/neg/macro-invalidsig-context-bounds.check b/test/files/neg/macro-invalidsig-context-bounds.check index 6c9482e537..5d42ebc515 100644 --- a/test/files/neg/macro-invalidsig-context-bounds.check +++ b/test/files/neg/macro-invalidsig-context-bounds.check @@ -1,4 +1,7 @@ -Impls_1.scala:5: error: macro implementations cannot have implicit parameters other than AbsTypeTag evidences - def foo[U: c.AbsTypeTag: Numeric](c: Ctx) = { - ^ -one error found +Macros_Test_1.scala:2: error: macro implementation has wrong shape:
+ required: (c: scala.reflect.macros.Context): c.Expr[Any]
+ found : (c: scala.reflect.macros.Context)(implicit evidence$2: Numeric[U]): c.universe.Literal
+macro implementations cannot have implicit parameters other than AbsTypeTag evidences
+ def foo[U] = macro Impls.foo[U]
+ ^
+one error found
diff --git a/test/files/neg/macro-invalidsig-implicit-params.check b/test/files/neg/macro-invalidsig-implicit-params.check index 98b3167b7a..62156770e4 100644 --- a/test/files/neg/macro-invalidsig-implicit-params.check +++ b/test/files/neg/macro-invalidsig-implicit-params.check @@ -1,4 +1,7 @@ -Impls_Macros_1.scala:5: error: macro implementations cannot have implicit parameters other than AbsTypeTag evidences - def foo_targs[T, U: c.AbsTypeTag](c: Ctx)(implicit x: c.Expr[Int]) = { - ^ -one error found +Impls_Macros_1.scala:18: error: macro implementation has wrong shape:
+ required: (c: scala.reflect.macros.Context)(x: c.Expr[Int]): c.Expr[Unit]
+ found : (c: scala.reflect.macros.Context)(implicit x: c.Expr[Int]): c.Expr[Unit]
+macro implementations cannot have implicit parameters other than AbsTypeTag evidences
+ def foo_targs[U](x: Int) = macro Impls.foo_targs[T, U]
+ ^
+one error found
diff --git a/test/files/neg/macro-invalidsig-params-namemismatch.check b/test/files/neg/macro-invalidsig-params-namemismatch.check index f2639d9350..00d781a2ac 100644 --- a/test/files/neg/macro-invalidsig-params-namemismatch.check +++ b/test/files/neg/macro-invalidsig-params-namemismatch.check @@ -2,7 +2,6 @@ Impls_Macros_1.scala:8: error: macro implementation has wrong shape: required: (c: scala.reflect.macros.Context)(x: c.Expr[Int], y: c.Expr[Int]): c.Expr[Any]
found : (c: scala.reflect.macros.Context)(y: c.Expr[Int], x: c.Expr[Int]): Nothing
parameter names differ: x != y
-parameter names differ: y != x
def foo(x: Int, y: Int) = macro Impls.foo
^
one error found
diff --git a/test/files/neg/macro-invalidsig-tparams-badtype.check b/test/files/neg/macro-invalidsig-tparams-badtype.check index e5e8366ba4..20c58ff2bb 100644 --- a/test/files/neg/macro-invalidsig-tparams-badtype.check +++ b/test/files/neg/macro-invalidsig-tparams-badtype.check @@ -3,5 +3,5 @@ Macros_Test_2.scala:2: error: macro implementation has wrong shape: found : (c: scala.reflect.macros.Context)(U: c.universe.Type): Nothing
number of parameter sections differ
def foo[U] = macro Impls.foo[U]
- ^
+ ^
one error found
diff --git a/test/files/neg/macro-invalidsig-tparams-notparams-a.check b/test/files/neg/macro-invalidsig-tparams-notparams-a.check index 5b4ef42ea5..5aee62b9e5 100644 --- a/test/files/neg/macro-invalidsig-tparams-notparams-a.check +++ b/test/files/neg/macro-invalidsig-tparams-notparams-a.check @@ -1,4 +1,4 @@ -Macros_Test_2.scala:2: error: macro implementation reference needs type arguments
+Macros_Test_2.scala:2: error: wrong number of type parameters for method foo: [U](c: scala.reflect.macros.Context)(implicit evidence$1: c.AbsTypeTag[U])Nothing
def foo = macro Impls.foo
^
one error found
diff --git a/test/files/neg/macro-invalidsig-tparams-notparams-b.check b/test/files/neg/macro-invalidsig-tparams-notparams-b.check index 261e3b8293..e8de8a4c6b 100644 --- a/test/files/neg/macro-invalidsig-tparams-notparams-b.check +++ b/test/files/neg/macro-invalidsig-tparams-notparams-b.check @@ -1,4 +1,4 @@ -Macros_Test_2.scala:3: error: macro implementation reference needs type arguments
+Macros_Test_2.scala:3: error: wrong number of type parameters for method foo: [T, U, V](c: scala.reflect.macros.Context)(implicit evidence$1: c.AbsTypeTag[T], implicit evidence$2: c.AbsTypeTag[U], implicit V: c.AbsTypeTag[V])c.Expr[Unit]
def foo[V] = macro Impls.foo
^
one error found
diff --git a/test/files/neg/macro-invalidusage-badbounds.check b/test/files/neg/macro-invalidusage-badbounds-a.check index fd0b64533e..fd0b64533e 100644 --- a/test/files/neg/macro-invalidusage-badbounds.check +++ b/test/files/neg/macro-invalidusage-badbounds-a.check diff --git a/test/files/neg/macro-invalidusage-badbounds.flags b/test/files/neg/macro-invalidusage-badbounds-a.flags index cd66464f2f..cd66464f2f 100644 --- a/test/files/neg/macro-invalidusage-badbounds.flags +++ b/test/files/neg/macro-invalidusage-badbounds-a.flags diff --git a/test/files/neg/macro-invalidusage-badbounds-a/Impls_1.scala b/test/files/neg/macro-invalidusage-badbounds-a/Impls_1.scala new file mode 100644 index 0000000000..6ee71a3628 --- /dev/null +++ b/test/files/neg/macro-invalidusage-badbounds-a/Impls_1.scala @@ -0,0 +1,5 @@ +import scala.reflect.macros.{Context => Ctx} + +object Impls { + def foo[U <: String](c: Ctx) = c.literalUnit +} diff --git a/test/files/neg/macro-invalidusage-badbounds/Macros_Test_2.scala b/test/files/neg/macro-invalidusage-badbounds-a/Macros_Test_2.scala index 3139599108..3139599108 100644 --- a/test/files/neg/macro-invalidusage-badbounds/Macros_Test_2.scala +++ b/test/files/neg/macro-invalidusage-badbounds-a/Macros_Test_2.scala diff --git a/test/files/neg/static-annot.scala b/test/files/neg/static-annot.scala index c6c626d42b..c0b5ed30d8 100644 --- a/test/files/neg/static-annot.scala +++ b/test/files/neg/static-annot.scala @@ -45,3 +45,9 @@ class PrivateProtectedLazy { println(PrivateProtectedLazy.baz) println(PrivateProtectedLazy.bam) } + + +class StaticDef { + // this should not crash the compiler + @static def x = 42 +} diff --git a/test/files/neg/t6227.check b/test/files/neg/t6227.check new file mode 100644 index 0000000000..5e3c636712 --- /dev/null +++ b/test/files/neg/t6227.check @@ -0,0 +1,4 @@ +t6227.scala:2: error: illegal combination of modifiers: implicit and case for: class IntOps + implicit case class IntOps( i: Int ) { + ^ +one error found diff --git a/test/files/neg/t6227.scala b/test/files/neg/t6227.scala new file mode 100644 index 0000000000..46416839d1 --- /dev/null +++ b/test/files/neg/t6227.scala @@ -0,0 +1,6 @@ +object Test { + implicit case class IntOps( i: Int ) { + def twice = i * 2 + } +} + diff --git a/test/files/neg/t6264.check b/test/files/neg/t6264.check new file mode 100644 index 0000000000..438be4c39f --- /dev/null +++ b/test/files/neg/t6264.check @@ -0,0 +1,4 @@ +t6264.scala:3: error: non-variable type argument Tuple1[_] in type Tuple2[_, Tuple1[_]] is unchecked since it is eliminated by erasure + x.isInstanceOf[Tuple2[_, Tuple1[_]]] + ^ +one error found diff --git a/test/files/neg/t6264.flags b/test/files/neg/t6264.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/t6264.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/t6264.scala b/test/files/neg/t6264.scala new file mode 100644 index 0000000000..dc3b727934 --- /dev/null +++ b/test/files/neg/t6264.scala @@ -0,0 +1,6 @@ +class Foo { + def foo(x: AnyRef): Unit = { + x.isInstanceOf[Tuple2[_, Tuple1[_]]] + () + } +} diff --git a/test/files/neg/t6283.check b/test/files/neg/t6283.check new file mode 100644 index 0000000000..69e417ee93 --- /dev/null +++ b/test/files/neg/t6283.check @@ -0,0 +1,4 @@ +t6283.scala:1: error: `abstract' modifier cannot be used with value classes +abstract class Funky(val i: Int) extends AnyVal + ^ +one error found diff --git a/test/files/neg/t6283.scala b/test/files/neg/t6283.scala new file mode 100644 index 0000000000..d41eb18a74 --- /dev/null +++ b/test/files/neg/t6283.scala @@ -0,0 +1 @@ +abstract class Funky(val i: Int) extends AnyVal diff --git a/test/files/pos/SI-5788.scala b/test/files/pos/SI-5788.scala index 93b84bde87..f292461804 100644 --- a/test/files/pos/SI-5788.scala +++ b/test/files/pos/SI-5788.scala @@ -1,4 +1,3 @@ -trait Test { - trait B[T] - private final def grow[T](): B[T] = grow[T]() +trait Foo[@specialized(Int) A] { + final def bar(a:A):A = bar(a) } diff --git a/test/files/pos/hk-match/a.scala b/test/files/pos/hk-match/a.scala new file mode 100644 index 0000000000..7144068f3c --- /dev/null +++ b/test/files/pos/hk-match/a.scala @@ -0,0 +1,5 @@ +trait A { + type HKAlias[X] = List[X] + + (null: Any) match { case f: Bippy[HKAlias] => f } +} diff --git a/test/files/pos/hk-match/b.scala b/test/files/pos/hk-match/b.scala new file mode 100644 index 0000000000..f7d21f6383 --- /dev/null +++ b/test/files/pos/hk-match/b.scala @@ -0,0 +1 @@ +trait Bippy[E[X]] diff --git a/test/files/pos/javaConversions-2.10-regression.scala b/test/files/pos/javaConversions-2.10-regression.scala new file mode 100644 index 0000000000..e1b81015ba --- /dev/null +++ b/test/files/pos/javaConversions-2.10-regression.scala @@ -0,0 +1,17 @@ +import collection.{JavaConversions, mutable, concurrent} +import JavaConversions._ +import java.util.concurrent.{ConcurrentHashMap => CHM} + +object Foo { + def buildCache2_9_simple[K <: AnyRef, V <: AnyRef]: mutable.ConcurrentMap[K, V] = + asScalaConcurrentMap(new CHM()) + + def buildCache2_9_implicit[K <: AnyRef, V <: AnyRef]: mutable.ConcurrentMap[K, V] = + new CHM[K, V]() +} + +object Bar { + def assertType[T](t: T) = t + val a = new CHM[String, String]() += (("", "")) + assertType[concurrent.Map[String, String]](a) +} diff --git a/test/files/pos/specializes-sym-crash.scala b/test/files/pos/specializes-sym-crash.scala new file mode 100644 index 0000000000..c46f435ac4 --- /dev/null +++ b/test/files/pos/specializes-sym-crash.scala @@ -0,0 +1,26 @@ +import scala.collection._ + +trait Foo[+A, + +Coll, + +This <: GenSeqView[A, Coll] with GenSeqViewLike[A, Coll, This]] +extends GenSeq[A] with GenSeqLike[A, This] with GenIterableView[A, Coll] with GenIterableViewLike[A, Coll, This] { +self => + + trait Transformed[+B] extends GenSeqView[B, Coll] with super.Transformed[B] { + def length: Int + def apply(idx: Int): B + override def toString = viewToString + } + trait Reversed extends Transformed[A] { + override def iterator: Iterator[A] = createReversedIterator + def length: Int = self.length + def apply(idx: Int): A = self.apply(length - 1 - idx) + final override protected[this] def viewIdentifier = "R" + + private def createReversedIterator = { + var lst = List[A]() + for (elem <- self) lst ::= elem + lst.iterator + } + } +} diff --git a/test/files/pos/t5667.scala b/test/files/pos/t5667.scala index 513de5b663..353eec93d6 100644 --- a/test/files/pos/t5667.scala +++ b/test/files/pos/t5667.scala @@ -1,6 +1,4 @@ object Main { implicit class C(val s: String) extends AnyVal implicit class C2(val s: String) extends AnyRef - - implicit case class Foo(i: Int) } diff --git a/test/files/pos/t6208.scala b/test/files/pos/t6208.scala new file mode 100644 index 0000000000..dac571346d --- /dev/null +++ b/test/files/pos/t6208.scala @@ -0,0 +1,4 @@ +object Test { + val col = collection.mutable.Queue(1,2,3) + val WORK: collection.mutable.Queue[Int] = col filterNot (_ % 2 == 0) +} diff --git a/test/files/run/classtags_core.check b/test/files/run/classtags_core.check index 2241108ba0..6519db2178 100644 --- a/test/files/run/classtags_core.check +++ b/test/files/run/classtags_core.check @@ -1,30 +1,30 @@ -true -ClassTag[byte] -true -ClassTag[short] -true -ClassTag[char] -true -ClassTag[int] -true -ClassTag[long] -true -ClassTag[float] -true -ClassTag[double] -true -ClassTag[boolean] -true -ClassTag[void] -true -ClassTag[class java.lang.Object] -true -ClassTag[class java.lang.Object] -true -ClassTag[class java.lang.Object] -true -ClassTag[class java.lang.Object] -true -ClassTag[class scala.runtime.Null$] -true -ClassTag[Nothing] +true
+ClassTag[byte]
+true
+ClassTag[short]
+true
+ClassTag[char]
+true
+ClassTag[int]
+true
+ClassTag[long]
+true
+ClassTag[float]
+true
+ClassTag[double]
+true
+ClassTag[boolean]
+true
+ClassTag[void]
+true
+ClassTag[class java.lang.Object]
+true
+ClassTag[class java.lang.Object]
+true
+ClassTag[class java.lang.Object]
+true
+ClassTag[class java.lang.Object]
+true
+ClassTag[class scala.runtime.Null$]
+true
+ClassTag[class scala.runtime.Nothing$]
diff --git a/test/files/run/existentials3-new.check b/test/files/run/existentials3-new.check index 0d6f694a68..a036d92a24 100644 --- a/test/files/run/existentials3-new.check +++ b/test/files/run/existentials3-new.check @@ -1,24 +1,24 @@ -Bar.type, t=AbstractTypeRef, s= <: scala.runtime.AbstractFunction0[Bar] with Serializable{case def unapply(x$0: Bar): Boolean} with Singleton
-Bar, t=AbstractTypeRef, s= <: Test.ToS with Product with Serializable{def copy(): Bar}
+Bar.type, t=TypeRef, s= <: scala.runtime.AbstractFunction0[Bar] with Serializable{case def unapply(x$0: Bar): Boolean} with Singleton
+Bar, t=TypeRef, s= <: Test.ToS with Product with Serializable{def copy(): Bar}
Test.ToS, t=RefinedType, s=f3
Test.ToS, t=RefinedType, s=f4
Test.ToS, t=RefinedType, s=f5
() => Test.ToS, t=TypeRef, s=class Function0
() => Test.ToS, t=TypeRef, s=class Function0
-$anon, t=AbstractTypeRef, s= <: B with Test.ToS
-$anon, t=AbstractTypeRef, s= <: B with A with Test.ToS
+$anon, t=TypeRef, s= <: B with Test.ToS
+$anon, t=TypeRef, s= <: B with A with Test.ToS
List[java.lang.Object{type T1}#T1], t=TypeRef, s=class List
List[Seq[Int]], t=TypeRef, s=class List
List[Seq[U forSome { type U <: Int }]], t=TypeRef, s=class List
-Bar.type, t=AbstractTypeRef, s= <: scala.runtime.AbstractFunction0[Bar] with Serializable{case def unapply(x$0: Bar): Boolean} with Singleton
-Bar, t=AbstractTypeRef, s= <: Test.ToS with Product with Serializable{def copy(): Bar}
+Bar.type, t=TypeRef, s= <: scala.runtime.AbstractFunction0[Bar] with Serializable{case def unapply(x$0: Bar): Boolean} with Singleton
+Bar, t=TypeRef, s= <: Test.ToS with Product with Serializable{def copy(): Bar}
Test.ToS, t=RefinedType, s=g3
Test.ToS, t=RefinedType, s=g4
Test.ToS, t=RefinedType, s=g5
() => Test.ToS, t=TypeRef, s=class Function0
() => Test.ToS, t=TypeRef, s=class Function0
-$anon, t=AbstractTypeRef, s= <: B with Test.ToS
-$anon, t=AbstractTypeRef, s= <: B with A with Test.ToS
+$anon, t=TypeRef, s= <: B with Test.ToS
+$anon, t=TypeRef, s= <: B with A with Test.ToS
List[java.lang.Object{type T1}#T1], t=TypeRef, s=class List
List[Seq[Int]], t=TypeRef, s=class List
List[Seq[U forSome { type U <: Int }]], t=TypeRef, s=class List
diff --git a/test/files/run/existentials3-new.scala b/test/files/run/existentials3-new.scala index 649fac8327..16735eab4f 100644 --- a/test/files/run/existentials3-new.scala +++ b/test/files/run/existentials3-new.scala @@ -35,7 +35,7 @@ object Test { def printTpe(t: Type) = { val s = if (t.typeSymbol.isFreeType) t.typeSymbol.typeSignature.toString else t.typeSymbol.toString - println("%s, t=%s, s=%s".format(t, t.kind, s)) + println("%s, t=%s, s=%s".format(t, t.asInstanceOf[Product].productPrefix, s)) } def m[T: TypeTag](x: T) = printTpe(typeOf[T]) def m2[T: AbsTypeTag](x: T) = printTpe(implicitly[AbsTypeTag[T]].tpe) diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check index a4c9f215e8..2bc72893e7 100644 --- a/test/files/run/inline-ex-handlers.check +++ b/test/files/run/inline-ex-handlers.check @@ -61,22 +61,22 @@ > ? LOAD_LOCAL(variable monitor4) > 305 MONITOR_EXIT > ? JUMP 12 -550c557,563 +550c557 < ? THROW(Throwable) --- > ? JUMP 12 +556c563,570 +< ? THROW(Throwable) +--- +> ? STORE_LOCAL(value t) +> ? JUMP 13 > > 12: > ? LOAD_LOCAL(variable monitor3) > 304 MONITOR_EXIT > ? STORE_LOCAL(value t) > ? JUMP 13 -556c569,582 -< ? THROW(Throwable) ---- -> ? STORE_LOCAL(value t) -> ? JUMP 13 -> +571a586,597 > 13: > 310 LOAD_MODULE object Predef > 310 CALL_PRIMITIVE(StartConcat) @@ -88,6 +88,7 @@ > 310 CALL_PRIMITIVE(EndConcat) > 310 CALL_METHOD scala.Predef.println (dynamic) > 310 JUMP 2 +> 580c606 < catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6 --- @@ -110,12 +111,12 @@ > 81 LOAD_LOCAL(value e) > ? STORE_LOCAL(variable exc1) > ? JUMP 12 -668c700,714 +668c700,701 < 81 THROW(Exception) --- > ? STORE_LOCAL(variable exc1) > ? JUMP 12 -> +684a718,730 > 12: > 83 LOAD_MODULE object Predef > 83 CONSTANT("finally") @@ -128,6 +129,7 @@ > 84 STORE_LOCAL(variable result) > 84 LOAD_LOCAL(variable exc1) > 84 THROW(Throwable) +> 690c736 < catch (<none>) in ArrayBuffer(4, 6, 7, 9) starting at: 3 --- @@ -171,17 +173,18 @@ --- > ? STORE_LOCAL(value ex6) > ? JUMP 33 -802c854,861 +802c854,855 < 170 THROW(Throwable) --- > ? STORE_LOCAL(value ex6) > ? JUMP 33 -> +811a865,870 > 33: > 169 LOAD_LOCAL(value ex6) > 169 STORE_LOCAL(value x4) > 169 SCOPE_ENTER value x4 > 169 JUMP 5 +> 826,829d884 < 180 LOAD_LOCAL(value x5) < 180 CALL_METHOD MyException.message (dynamic) @@ -207,7 +210,7 @@ --- > ? STORE_LOCAL(variable exc2) > ? JUMP 34 -842a902,914 +857a917,929 > 34: > 184 LOAD_MODULE object Predef > 184 CONSTANT("finally") diff --git a/test/files/run/macro-def-path-dependent-d.check b/test/files/run/macro-def-path-dependent-d1.check index 1ea14b4e20..1ea14b4e20 100644 --- a/test/files/run/macro-def-path-dependent-d.check +++ b/test/files/run/macro-def-path-dependent-d1.check diff --git a/test/files/run/macro-def-path-dependent-d.flags b/test/files/run/macro-def-path-dependent-d1.flags index cd66464f2f..cd66464f2f 100644 --- a/test/files/run/macro-def-path-dependent-d.flags +++ b/test/files/run/macro-def-path-dependent-d1.flags diff --git a/test/files/run/macro-def-path-dependent-d/Impls_Macros_1.scala b/test/files/run/macro-def-path-dependent-d1/Impls_Macros_1.scala index 2daf6fc3fb..2daf6fc3fb 100644 --- a/test/files/run/macro-def-path-dependent-d/Impls_Macros_1.scala +++ b/test/files/run/macro-def-path-dependent-d1/Impls_Macros_1.scala diff --git a/test/files/run/macro-def-path-dependent-d/Test_2.scala b/test/files/run/macro-def-path-dependent-d1/Test_2.scala index 7dffc5107d..7dffc5107d 100644 --- a/test/files/run/macro-def-path-dependent-d/Test_2.scala +++ b/test/files/run/macro-def-path-dependent-d1/Test_2.scala diff --git a/test/files/run/macro-def-path-dependent-d2.check b/test/files/run/macro-def-path-dependent-d2.check new file mode 100644 index 0000000000..1ea14b4e20 --- /dev/null +++ b/test/files/run/macro-def-path-dependent-d2.check @@ -0,0 +1 @@ +it works
diff --git a/test/files/run/macro-def-path-dependent-d2.flags b/test/files/run/macro-def-path-dependent-d2.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/macro-def-path-dependent-d2.flags @@ -0,0 +1 @@ +-language:experimental.macros
\ No newline at end of file diff --git a/test/files/run/macro-def-path-dependent-d2/Impls_1.scala b/test/files/run/macro-def-path-dependent-d2/Impls_1.scala new file mode 100644 index 0000000000..1cda64e43e --- /dev/null +++ b/test/files/run/macro-def-path-dependent-d2/Impls_1.scala @@ -0,0 +1,7 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.macros.Context +import scala.reflect.api.Universe + +object Impls { + def materializeTypeTag_impl[T: c.AbsTypeTag](c: Context)(u: c.Expr[Universe])(e: c.Expr[T]): c.Expr[u.value.TypeTag[T]] = ??? +}
\ No newline at end of file diff --git a/test/files/run/macro-def-path-dependent-d2/Macros_2.scala b/test/files/run/macro-def-path-dependent-d2/Macros_2.scala new file mode 100644 index 0000000000..65ce4d8bd2 --- /dev/null +++ b/test/files/run/macro-def-path-dependent-d2/Macros_2.scala @@ -0,0 +1,7 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.macros.Context +import scala.reflect.api.Universe + +object Macros { + def materializeTypeTag[T](u: Universe)(e: T) = macro Impls.materializeTypeTag_impl[T] +}
\ No newline at end of file diff --git a/test/files/run/macro-def-path-dependent-d2/Test_3.scala b/test/files/run/macro-def-path-dependent-d2/Test_3.scala new file mode 100644 index 0000000000..7dffc5107d --- /dev/null +++ b/test/files/run/macro-def-path-dependent-d2/Test_3.scala @@ -0,0 +1,3 @@ +object Test extends App { + println("it works") +}
\ No newline at end of file diff --git a/test/files/run/macro-expand-implicit-argument.check b/test/files/run/macro-expand-implicit-argument.check new file mode 100644 index 0000000000..fb42345748 --- /dev/null +++ b/test/files/run/macro-expand-implicit-argument.check @@ -0,0 +1 @@ +List(1, 2, 3)
diff --git a/test/files/run/macro-expand-implicit-argument.flags b/test/files/run/macro-expand-implicit-argument.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/macro-expand-implicit-argument.flags @@ -0,0 +1 @@ +-language:experimental.macros
\ No newline at end of file diff --git a/test/files/run/macro-expand-implicit-argument/Macros_1.scala b/test/files/run/macro-expand-implicit-argument/Macros_1.scala new file mode 100644 index 0000000000..7629c5a9e2 --- /dev/null +++ b/test/files/run/macro-expand-implicit-argument/Macros_1.scala @@ -0,0 +1,59 @@ +import annotation.tailrec +import scala.math.{min, max} +import scala.{specialized => spec} + +import language.experimental.macros + +import scala.reflect.{ClassTag, TypeTag} +import scala.reflect.macros.Context + +object Macros { + def alloc[@spec A:ClassTag](src:Array[A], s1:Int, len:Int) = { + val as = Array.ofDim[A](len) + System.arraycopy(src, s1, as, 0, len) + as + } + + /** + * Efficient alternative to Array.apply. + * + * "As seen on scala-internals!" + */ + def array[A](as:A*)(implicit ct: ClassTag[A]) = macro arrayMacro[A] + + /** + * Takes in something like: + * ArrayUtil.alloc[Int](11, 22, 33, 44)(ct) + * + * and builds a tree like: + * { + * val arr:Array[Int] = ct.newArray(4) + * arr.update(0, 11) + * arr.update(1, 22) + * arr.update(2, 33) + * arr.update(3, 44) + * arr + * } + */ + def arrayMacro[A:c.AbsTypeTag](c:Context)(as:c.Expr[A]*)(ct: c.Expr[ClassTag[A]]): c.Expr[Array[A]] = { + import c.mirror._ + import c.universe._ + def const(x:Int) = Literal(Constant(x)) + + val n = as.length + val arr = newTermName("arr") + + val create = Apply(Select(ct.tree, "newArray"), List(const(n))) + val arrtpe = TypeTree(implicitly[c.AbsTypeTag[Array[A]]].tpe) + val valdef = ValDef(Modifiers(), arr, arrtpe, create) + + val updates = (0 until n).map { + i => Apply(Select(Ident(arr), "update"), List(const(i), as(i).tree)) + } + + val exprs = Seq(valdef) ++ updates ++ Seq(Ident(arr)) + val block = Block(exprs:_*) + + c.Expr[Array[A]](block) + } +}
\ No newline at end of file diff --git a/test/files/run/macro-expand-implicit-argument/Test_2.scala b/test/files/run/macro-expand-implicit-argument/Test_2.scala new file mode 100644 index 0000000000..ce8a068fb4 --- /dev/null +++ b/test/files/run/macro-expand-implicit-argument/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + import Macros._ + println(array(1, 2, 3).toList) +}
\ No newline at end of file diff --git a/test/files/run/partialfun.check b/test/files/run/partialfun.check new file mode 100644 index 0000000000..a317f7b150 --- /dev/null +++ b/test/files/run/partialfun.check @@ -0,0 +1,6 @@ +47
+147
+100
+0:isDefinedAt
+1:isDefinedAt
+2:apply
diff --git a/test/files/run/partialfun.scala b/test/files/run/partialfun.scala new file mode 100644 index 0000000000..4b360750c9 --- /dev/null +++ b/test/files/run/partialfun.scala @@ -0,0 +1,86 @@ +import collection._
+import collection.generic._
+
+object Test {
+ def collectIDA[A, B, Repr, That](_this: TraversableLike[A, Repr])(pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+ val repr: Repr = _this.asInstanceOf[Repr]
+ val b = bf(repr)
+ _this foreach { x => if (pf isDefinedAt x) b += pf(x) }
+ b.result
+ }
+
+ def collectRW[A, B, Repr, That](_this: TraversableLike[A, Repr])(pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+ val repr: Repr = _this.asInstanceOf[Repr]
+ val b = bf(repr)
+ val f = pf runWith { b += _ }
+ _this foreach f
+ b.result
+ }
+
+ var cnt = 0
+
+ object Ex1 {
+ def unapply(x: Int) : Option[Int] = {
+ cnt += 1
+ if ((x % 3) == 0) Some(-x) else None
+ }
+ }
+
+ object Ex2 {
+ def unapply(x: Int) : Option[Int] = {
+ //cnt += 1
+ if ((x % 5) == 0) Some(x) else None
+ }
+ }
+
+ def resetCnt() = { val r = cnt; cnt = 0; r }
+
+ val pf: PartialFunction[Int,Int] = {
+ case Ex1(result) => result
+ case Ex2(result) => result
+ }
+
+ def collectTest() {
+ val xs = 1 to 100
+ resetCnt()
+
+ val ysIDA = collectIDA(xs)(pf)
+ val cntIDA = resetCnt()
+
+ val ysRW = collectRW(xs)(pf)
+ val cntRW = resetCnt()
+
+ val ys = xs collect pf
+
+ assert(ys == ysIDA)
+ assert(ys == ysRW)
+ assert(cntIDA == xs.length + ys.length)
+ assert(cntRW == xs.length)
+ println(ys.length)
+ println(cntIDA)
+ println(cntRW)
+ }
+
+ def orElseTest() {
+ val pf0 = new PartialFunction[Unit, Unit] {
+ def apply(u: Unit) { println("0:apply") }
+ def isDefinedAt(u: Unit) = { println("0:isDefinedAt"); false }
+ }
+ val pf1 = new PartialFunction[Unit, Unit] {
+ def apply(u: Unit) { println("1:apply") }
+ def isDefinedAt(u: Unit) = { println("1:isDefinedAt"); false }
+ }
+ val pf2 = new PartialFunction[Unit, Unit] {
+ def apply(u: Unit) { println("2:apply") }
+ def isDefinedAt(u: Unit) = { println("2:isDefinedAt"); true }
+ }
+
+ val chained = pf0 orElse pf1 orElse pf2
+ chained()
+ }
+
+ def main(args: Array[String]): Unit = {
+ collectTest()
+ orElseTest()
+ }
+}
diff --git a/test/files/run/reflection-sorted-decls.check b/test/files/run/reflection-sorted-decls.check index 9a9832a683..02168c4e5f 100644 --- a/test/files/run/reflection-sorted-decls.check +++ b/test/files/run/reflection-sorted-decls.check @@ -1,7 +1,3 @@ value a
value b
value c
-method c
-method b
-method a
-constructor Foo$1
diff --git a/test/files/run/reflection-sorted-decls.scala b/test/files/run/reflection-sorted-decls.scala index 242f17d9bb..5616e10b3b 100644 --- a/test/files/run/reflection-sorted-decls.scala +++ b/test/files/run/reflection-sorted-decls.scala @@ -3,6 +3,6 @@ object Test { class Foo(val a: Int, val b: Int, val c: Int) import scala.reflect.runtime.{currentMirror => cm} val decls = cm.classSymbol(classOf[Foo]).typeSignature.declarations - decls.sorted.toList foreach System.out.println + decls.sorted.toList.filter(!_.isMethod) foreach System.out.println } } diff --git a/test/files/run/reflection-sorted-members.check b/test/files/run/reflection-sorted-members.check index d58b691c42..bb85b5a13c 100644 --- a/test/files/run/reflection-sorted-members.check +++ b/test/files/run/reflection-sorted-members.check @@ -1,34 +1,4 @@ value a
value b
value c
-method c
-method b
-method a
-constructor Foo$1
value x
-method x
-constructor Bar$1
-method finalize
-method wait
-method wait
-method wait
-method equals
-method toString
-method hashCode
-method getClass
-method clone
-method notify
-method notifyAll
-constructor Object
-method eq
-method ne
-method ==
-method !=
-method ##
-method synchronized
-method $isInstanceOf
-method $asInstanceOf
-method ==
-method !=
-method isInstanceOf
-method asInstanceOf
diff --git a/test/files/run/reflection-sorted-members.scala b/test/files/run/reflection-sorted-members.scala index 9980d79999..a8379234c0 100644 --- a/test/files/run/reflection-sorted-members.scala +++ b/test/files/run/reflection-sorted-members.scala @@ -6,6 +6,6 @@ object Test { class Foo(val a: Int, val b: Int, val c: Int) extends Bar(a + b + c) with T1 with T2 import scala.reflect.runtime.{currentMirror => cm} val members = cm.classSymbol(classOf[Foo]).typeSignature.members - members.sorted.toList foreach System.out.println + members.sorted.toList.filter(!_.isMethod) foreach System.out.println } } diff --git a/test/files/run/static-annot-repl.check b/test/files/run/static-annot-repl.check new file mode 100644 index 0000000000..d1029a9809 --- /dev/null +++ b/test/files/run/static-annot-repl.check @@ -0,0 +1,32 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> import annotation.static +import annotation.static + +scala> @static var x1 = 42 +x1: Int = 42 + +scala> @static val x2 = 43 +x2: Int = 43 + +scala> @static def x3 = 44 +x3: Int + +scala> x1 +res0: Int = 42 + +scala> x2 +res1: Int = 43 + +scala> x3 +res2: Int = 44 + +scala> class Test { + @static def x = 42 +} +defined class Test + +scala>
\ No newline at end of file diff --git a/test/files/run/static-annot-repl.scala b/test/files/run/static-annot-repl.scala new file mode 100644 index 0000000000..1d2e9b2d7e --- /dev/null +++ b/test/files/run/static-annot-repl.scala @@ -0,0 +1,22 @@ + + + +import scala.tools.partest.ReplTest + + + +object Test extends ReplTest { + def code = """ +import annotation.static +@static var x1 = 42 +@static val x2 = 43 +@static def x3 = 44 +x1 +x2 +x3 +class Test { + @static def x = 42 +} +""" + +} diff --git a/test/files/run/static-annot/field.scala b/test/files/run/static-annot/field.scala index a7d8158321..8408a51800 100644 --- a/test/files/run/static-annot/field.scala +++ b/test/files/run/static-annot/field.scala @@ -23,8 +23,10 @@ class Foo trait Check { def checkStatic(cls: Class[_]) { cls.getDeclaredFields.find(_.getName == "bar") match { - case Some(f) => assert(Modifier.isStatic(f.getModifiers), "no static modifier") - case None => assert(false, "no static field bar in class") + case Some(f) => + assert(Modifier.isStatic(f.getModifiers), "no static modifier") + case None => + assert(false, "no static field bar in class") } } @@ -170,6 +172,10 @@ object Foo7 { @static val bar = "string" } class AndHisFriend + + object AndHisLonelyFriend { + @static val bar = "another" + } } @@ -177,6 +183,9 @@ object Test7 extends Check { def test() { checkStatic(classOf[Foo7.AndHisFriend]) assert(Foo7.AndHisFriend.bar == "string") + + checkStatic(Class.forName("Foo7$AndHisLonelyFriend")) + assert(Foo7.AndHisLonelyFriend.bar == "another") } } diff --git a/test/files/run/stream-stack-overflow-filter-map.scala b/test/files/run/stream-stack-overflow-filter-map.scala new file mode 100644 index 0000000000..f3a9dd49cb --- /dev/null +++ b/test/files/run/stream-stack-overflow-filter-map.scala @@ -0,0 +1,44 @@ +import collection.generic.{FilterMonadic, CanBuildFrom} + +object Test extends App { + def mapSucc[Repr, That](s: FilterMonadic[Int, Repr])(implicit cbf: CanBuildFrom[Repr, Int, That]) = s map (_ + 1) + def flatMapId[T, Repr, That](s: FilterMonadic[T, Repr])(implicit cbf: CanBuildFrom[Repr, T, That]) = s flatMap (Seq(_)) + + def testStreamPred(s: Stream[Int])(p: Int => Boolean) { + val res1 = s withFilter p + val res2 = s filter p + + val expected = s.toSeq filter p + + val fMapped1 = flatMapId(res1) + val fMapped2 = flatMapId(res2) + assert(fMapped1 == fMapped2) + assert(fMapped1.toSeq == expected) + + val mapped1 = mapSucc(res1) + val mapped2 = mapSucc(res2) + assert(mapped1 == mapped2) + assert(mapped1.toSeq == (expected map (_ + 1))) + + assert((res1 map identity).toSeq == res2.toSeq) + } + + def testStream(s: Stream[Int]) { + testStreamPred(s)(_ => false) + testStreamPred(s)(_ => true) + testStreamPred(s)(_ % 2 == 0) + testStreamPred(s)(_ % 3 == 0) + } + + //Reduced version of the test case - either invocation used to cause a stack + //overflow before commit 80b3f433e5536d086806fa108ccdfacf10719cc2. + val resFMap = (1 to 10000).toStream withFilter (_ => false) flatMap (Seq(_)) + val resMap = (1 to 10000).toStream withFilter (_ => false) map (_ + 1) + + //Complete test case for withFilter + map/flatMap, as requested by @axel22. + for (j <- (0 to 3) :+ 10000) { + val stream = (1 to j).toStream + assert(stream.toSeq == (1 to j).toSeq) + testStream(stream) + } +} diff --git a/test/files/run/t1987b.check b/test/files/run/t1987b.check new file mode 100644 index 0000000000..68d4b10e12 --- /dev/null +++ b/test/files/run/t1987b.check @@ -0,0 +1 @@ +ok! diff --git a/test/files/run/t1987b/PullIteratees.scala b/test/files/run/t1987b/PullIteratees.scala new file mode 100644 index 0000000000..a5a3e65d8f --- /dev/null +++ b/test/files/run/t1987b/PullIteratees.scala @@ -0,0 +1,17 @@ +package scales.xml + +trait PullType +class QName +trait RetUrn[T] + +/** + * Iteratees related to pull parsing + */ +trait PullIteratees { + /** + * Without the overload it doesn't trigger the CCE, even though its + * not used + */ + def iterate(path: List[QName], xml: String): RetUrn[String] = null + def iterate(path: List[QName], xml: Iterator[PullType]): RetUrn[String] = null +} diff --git a/test/files/run/t1987b/a.scala b/test/files/run/t1987b/a.scala new file mode 100644 index 0000000000..c1be5fe3e0 --- /dev/null +++ b/test/files/run/t1987b/a.scala @@ -0,0 +1,6 @@ +object Test { + def main(args: Array[String]): Unit = { + scales.xml.CCE_Test.main(args) + println("ok!") + } +} diff --git a/test/files/run/t1987b/cce_test.scala b/test/files/run/t1987b/cce_test.scala new file mode 100644 index 0000000000..4f9acf0264 --- /dev/null +++ b/test/files/run/t1987b/cce_test.scala @@ -0,0 +1,15 @@ +package scales.xml +//import scales.xml._ // using another pacakge and importing doesn't CCE + +object CCE_Test { + def main(args: Array[String]): Unit = { + // without the import it doesn't trigger the CCE + import scaley.funny._ + + val pull = null.asInstanceOf[Iterator[PullType]] + val LogEntries = null.asInstanceOf[List[QName]] + // fully qualify with scales.xml. and it won't trigger it + iterate(LogEntries, + pull) + } +} diff --git a/test/files/run/t1987b/pkg1.scala b/test/files/run/t1987b/pkg1.scala new file mode 100644 index 0000000000..6e749fc6b3 --- /dev/null +++ b/test/files/run/t1987b/pkg1.scala @@ -0,0 +1,4 @@ +package scaley + +package object funny { +} diff --git a/test/files/run/t1987b/pkg2.scala b/test/files/run/t1987b/pkg2.scala new file mode 100644 index 0000000000..38056a199e --- /dev/null +++ b/test/files/run/t1987b/pkg2.scala @@ -0,0 +1,3 @@ +package scales + +package object xml extends PullIteratees diff --git a/test/files/run/t6052.scala b/test/files/run/t6052.scala new file mode 100644 index 0000000000..385d5390d3 --- /dev/null +++ b/test/files/run/t6052.scala @@ -0,0 +1,21 @@ + + + + + + + +object Test extends App { + def seqarr(i: Int) = Array[Int]() ++ (0 until i) + def pararr(i: Int) = seqarr(i).par + + def check[T](i: Int, f: Int => T) { + val gseq = seqarr(i).toSeq.groupBy(f) + val gpar = pararr(i).groupBy(f) + assert(gseq == gpar, (gseq, gpar)) + } + + for (i <- 0 until 20) check(i, _ > 0) + for (i <- 0 until 20) check(i, _ % 2) + for (i <- 0 until 20) check(i, _ % 4) +} diff --git a/test/files/run/t6236.check b/test/files/run/t6236.check new file mode 100644 index 0000000000..a0a2e88d0a --- /dev/null +++ b/test/files/run/t6236.check @@ -0,0 +1,2 @@ +353 +353
\ No newline at end of file diff --git a/test/files/run/t6236/file_1.scala b/test/files/run/t6236/file_1.scala new file mode 100644 index 0000000000..92d22799fc --- /dev/null +++ b/test/files/run/t6236/file_1.scala @@ -0,0 +1,9 @@ + + +package p { + object y { + object x { + @scala.annotation.static val foo: Int = 353 + } + } +} diff --git a/test/files/run/t6236/file_2.scala b/test/files/run/t6236/file_2.scala new file mode 100644 index 0000000000..51823004ca --- /dev/null +++ b/test/files/run/t6236/file_2.scala @@ -0,0 +1,10 @@ + + + +object Test { + def main(args: Array[String]): Unit = { + println(p.y.x.foo) + println(p.y.x.foo) + } +} + diff --git a/test/files/run/t6261.scala b/test/files/run/t6261.scala new file mode 100644 index 0000000000..b4463256c9 --- /dev/null +++ b/test/files/run/t6261.scala @@ -0,0 +1,130 @@ +import scala.collection.immutable._ + +object Test extends App { + + def test0() { + val m=ListMap(1->2,3->4) + if(m.tail ne m.tail) + println("ListMap.tail uses a builder, so it is not O(1)") + } + + def test1() { + // test that a HashTrieMap with one leaf element is not created! + val x = HashMap.empty + (1->1) + (2->2) + if(x.getClass.getSimpleName != "HashTrieMap") + println("A hash map containing two non-colliding values should be a HashTrieMap") + + val y = x - 1 + if(y.getClass.getSimpleName != "HashMap1") + println("A hash map containing one element should always use HashMap1") + } + + def test2() { + // class that always causes hash collisions + case class Collision(value:Int) { override def hashCode = 0 } + + // create a set that should have a collison + val x = HashMap.empty + (Collision(0)->0) + (Collision(1) ->0) + if(x.getClass.getSimpleName != "HashMapCollision1") + println("HashMap of size >1 with collisions should use HashMapCollision") + + // remove the collision again by removing all but one element + val y = x - Collision(0) + if(y.getClass.getSimpleName != "HashMap1") + println("HashMap of size 1 should use HashMap1" + y.getClass) + } + def test3() { + // finds an int x such that improved(x) differs in the first bit to improved(0), + // which is the worst case for the HashTrieSet + def findWorstCaseInts() { + // copy of improve from HashSet + def improve(hcode: Int) = { + var h: Int = hcode + ~(hcode << 9) + h = h ^ (h >>> 14) + h = h + (h << 4) + h ^ (h >>> 10) + } + + // find two hashes which have a large separation + val x = 0 + var y = 1 + val ix = improve(x) + while(y!=0 && improve(y)!=ix+(1<<31)) + y+=1 + printf("%s %s %x %x\n",x,y,improve(x), improve(y)) + } + // this is not done every test run since it would slow down ant test.suite too much. + // findWorstCaseInts() + + // two numbers that are immediately adiacent when fed through HashSet.improve + val h0 = 0 + val h1 = 1270889724 + + // h is the hashcode, i is ignored for the hashcode but relevant for equality + case class Collision(h:Int, i:Int) { + override def hashCode = h + } + val a = Collision(h0,0)->0 + val b = Collision(h0,1)->0 + val c = Collision(h1,0)->0 + + // create a HashSetCollision1 + val x = HashMap(a) + b + if(x.getClass.getSimpleName != "HashMapCollision1") + println("x should be a HashMapCollision") + StructureTests.validate(x) + //StructureTests.printStructure(x) + require(x.size==2 && x.contains(a._1) && x.contains(b._1)) + + // go from a HashSetCollision1 to a HashTrieSet with maximum depth + val y = x + c + if(y.getClass.getSimpleName != "HashTrieMap") + println("y should be a HashTrieMap") + StructureTests.validate(y) + // StructureTests.printStructure(y) + require(y.size==3 && y.contains(a._1) && y.contains(b._1) && y.contains(c._1)) + + // go from a HashSet1 directly to a HashTrieSet with maximum depth + val z = HashMap(a) + c + if(y.getClass.getSimpleName != "HashTrieMap") + println("y should be a HashTrieMap") + StructureTests.validate(z) + // StructureTests.printStructure(z) + require(z.size == 2 && z.contains(a._1) && z.contains(c._1)) + } + test0() + test1() + test2() + test3() +} + + +package scala.collection.immutable { + object StructureTests { + def printStructure(x:HashMap[_,_], prefix:String="") { + x match { + case m:HashMap.HashTrieMap[_,_] => + println(prefix+m.getClass.getSimpleName + " " + m.size) + m.elems.foreach(child => printStructure(child, prefix + " ")) + case m:HashMap.HashMapCollision1[_,_] => + println(prefix+m.getClass.getSimpleName + " " + m.kvs.size) + case m:HashMap.HashMap1[_,_] => + println(prefix+m.getClass.getSimpleName + " " + m.head) + case _ => + println(prefix+"empty") + } + } + + def validate(x:HashMap[_,_]) { + x match { + case m:HashMap.HashTrieMap[_,_] => + require(m.elems.size>1 || (m.elems.size==1 && m.elems(0).isInstanceOf[HashMap.HashTrieMap[_,_]])) + m.elems.foreach(validate _) + case m:HashMap.HashMapCollision1[_,_] => + require(m.kvs.size>1) + case m:HashMap.HashMap1[_,_] => + case _ => + } + } + } +} diff --git a/test/files/run/t6272.check b/test/files/run/t6272.check new file mode 100644 index 0000000000..f00c965d83 --- /dev/null +++ b/test/files/run/t6272.check @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/test/files/run/t6272.scala b/test/files/run/t6272.scala new file mode 100644 index 0000000000..174436919b --- /dev/null +++ b/test/files/run/t6272.scala @@ -0,0 +1,62 @@ +// x1, x2, and x3 resulted in: symbol variable bitmap$0 does not exist in A.<init> +object A { + + try { + lazy val x1 = 1 + println(x1) + sys.error("!") + } catch { + case _: Throwable => + lazy val x2 = 2 + println(x2) + } finally { + lazy val x3 = 3 + println(x3) + } + + if ("".isEmpty) { + lazy val x4 = 4 + println(x4) + } + + var b = true + while(b) { + lazy val x5 = 5 + println(x5) + b = false + } + + + def method { + try { + lazy val x6 = 6 + println(x6) + sys.error("!") + } catch { + case _: Throwable => + lazy val x7 = 7 + println(x7) + } finally { + lazy val x8 = 8 + println(x8) + } + + if ("".isEmpty) { + lazy val x9 = 9 + println(x9) + } + + var b = true + while(b) { + lazy val x10 = 10 + println(x10) + b = false + } + } +} + +object Test { + def main(args: Array[String]) { + A.method + } +} diff --git a/test/files/run/t6292.scala b/test/files/run/t6292.scala new file mode 100644 index 0000000000..51e31f95fc --- /dev/null +++ b/test/files/run/t6292.scala @@ -0,0 +1,18 @@ + import scala.collection.mutable.DoubleLinkedList + +object Test { + def main(args: Array[String]): Unit = { + cloneAndtest(DoubleLinkedList[Int]()) + cloneAndtest(DoubleLinkedList[Int](1)) + cloneAndtest(DoubleLinkedList[Int](1,2,3,4)) + } + + def cloneAndtest(l: DoubleLinkedList[Int]): Unit = + testSame(l, l.clone.asInstanceOf[DoubleLinkedList[Int]]) + + def testSame(one: DoubleLinkedList[Int], two: DoubleLinkedList[Int]): Unit = { + def msg = s" for ${one} and ${two} !" + assert(one.size == two.size, s"Cloned sizes are not the same $msg!") + assert(one == two, s"Cloned lists are not equal $msg") + } +} diff --git a/test/files/run/try-catch-unify.check b/test/files/run/try-catch-unify.check new file mode 100644 index 0000000000..67a8c64a33 --- /dev/null +++ b/test/files/run/try-catch-unify.check @@ -0,0 +1,4 @@ +Failure(java.lang.NumberFormatException: For input string: "Hi") +Success(5.0) +O NOES +Failure(java.lang.NumberFormatException: For input string: "Hi") diff --git a/test/files/run/try-catch-unify.scala b/test/files/run/try-catch-unify.scala new file mode 100644 index 0000000000..8cb14d060e --- /dev/null +++ b/test/files/run/try-catch-unify.scala @@ -0,0 +1,16 @@ +import util._ + +import control.Exception._ + +object Test { + def main(args: Array[String]): Unit = { + println(catching(classOf[NumberFormatException]) withTry ("Hi".toDouble)) + println(catching(classOf[NumberFormatException]) withTry ("5".toDouble)) + try { + catching(classOf[NumberFormatException]) withTry (sys.error("O NOES")) + } catch { + case t => println(t.getMessage) + } + println(nonFatalCatch withTry ("Hi".toDouble)) + } +} diff --git a/test/pending/neg/macro-invalidusage-badbounds-b.check b/test/pending/neg/macro-invalidusage-badbounds-b.check new file mode 100644 index 0000000000..fd0b64533e --- /dev/null +++ b/test/pending/neg/macro-invalidusage-badbounds-b.check @@ -0,0 +1,4 @@ +Macros_Test_2.scala:7: error: type arguments [Int] do not conform to macro method foo's type parameter bounds [U <: String]
+ foo[Int]
+ ^
+one error found
diff --git a/test/pending/neg/macro-invalidusage-badbounds-b.flags b/test/pending/neg/macro-invalidusage-badbounds-b.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/pending/neg/macro-invalidusage-badbounds-b.flags @@ -0,0 +1 @@ +-language:experimental.macros
\ No newline at end of file diff --git a/test/files/neg/macro-invalidusage-badbounds/Impls_1.scala b/test/pending/neg/macro-invalidusage-badbounds-b/Impls_1.scala index 89020de7dd..89020de7dd 100644 --- a/test/files/neg/macro-invalidusage-badbounds/Impls_1.scala +++ b/test/pending/neg/macro-invalidusage-badbounds-b/Impls_1.scala diff --git a/test/pending/neg/macro-invalidusage-badbounds-b/Macros_Test_2.scala b/test/pending/neg/macro-invalidusage-badbounds-b/Macros_Test_2.scala new file mode 100644 index 0000000000..3139599108 --- /dev/null +++ b/test/pending/neg/macro-invalidusage-badbounds-b/Macros_Test_2.scala @@ -0,0 +1,8 @@ +object Macros { + def foo[U <: String] = macro Impls.foo[U] +} + +object Test extends App { + import Macros._ + foo[Int] +}
\ No newline at end of file |