diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-08-14 08:56:26 +0200 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-08-14 09:37:19 +0200 |
commit | 3ccaa1026e7e74d99fe39c9608e28c48b422e2c9 (patch) | |
tree | a1187b22900d5904f9ba6e5664440a8248491f5a | |
parent | 48d9fb7307fb6519fe786a7d9be97996c5812fb7 (diff) | |
parent | 12baa2ea399b4f281c62fd20479c1e626f244d03 (diff) | |
download | scala-3ccaa1026e7e74d99fe39c9608e28c48b422e2c9.tar.gz scala-3ccaa1026e7e74d99fe39c9608e28c48b422e2c9.tar.bz2 scala-3ccaa1026e7e74d99fe39c9608e28c48b422e2c9.zip |
Merge branch '2.10.x'
Conflicts:
src/compiler/scala/tools/nsc/Global.scala
src/compiler/scala/tools/nsc/typechecker/Typers.scala
test/files/neg/t6048.check
120 files changed, 2133 insertions, 861 deletions
@@ -225,7 +225,7 @@ PROPERTIES <property name="dists.dir" value="${basedir}/dists"/> - <property name="copyright.string" value="Copyright 2002-2011, LAMP/EPFL"/> + <property name="copyright.string" value="Copyright 2002-2012, LAMP/EPFL"/> <property name="partest.version.number" value="0.9.2"/> <!-- These are NOT the flags used to run SuperSabbus, but the ones written diff --git a/docs/LICENSE b/docs/LICENSE index 38d16361bd..de950bdf57 100644 --- a/docs/LICENSE +++ b/docs/LICENSE @@ -1,6 +1,6 @@ SCALA LICENSE -Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. +Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. All rights reserved. This software was developed by the Programming Methods Laboratory of the diff --git a/project/Versions.scala b/project/Versions.scala index b588ec55ac..cc0ab7ff2b 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -67,7 +67,7 @@ object Versions { IO.write(f, "version.number = "+versions.canonical+"\n"+ "osgi.number = "+versions.osgi+"\n"+ "maven.number = "+versions.maven+"\n"+ - "copyright.string = Copyright 2002-2011, LAMP/EPFL") + "copyright.string = Copyright 2002-2012, LAMP/EPFL") def makeCanonicalVersion(isRelease: Boolean, mvnVersion: String, base: BaseBuildNumber, gitDate: String, gitSha: String): String = if(isRelease) mvnVersion diff --git a/src/build/pack.xml b/src/build/pack.xml index 564d290967..1735b93f3f 100644 --- a/src/build/pack.xml +++ b/src/build/pack.xml @@ -29,10 +29,15 @@ MAIN DISTRIBUTION PACKAGING <tarfileset dir="${dist.dir}" prefix="${dist.name}" excludes="bin/**"/> </tar> <gzip src="${dists.dir}/archives/${dist.name}.tar" destfile="${dists.dir}/archives/${dist.name}.tgz"/> - <exec executable="xz" failifexecutionfails="false"> - <arg line="-k -9e -S .xz ${dists.dir}/archives/${dist.name}.tar"/> - </exec> - <move file="${dists.dir}/archives/${dist.name}.tar.xz" tofile="${dists.dir}/archives/${dist.name}.txz" failonerror="false"/> + <if> + <not><equals arg1="${archives.skipxz}" arg2="true" /></not> + <then> + <exec executable="xz" failifexecutionfails="false"> + <arg line="-k -9e -S .xz ${dists.dir}/archives/${dist.name}.tar"/> + </exec> + <move file="${dists.dir}/archives/${dist.name}.tar.xz" tofile="${dists.dir}/archives/${dist.name}.txz" failonerror="false"/> + </then> + </if> <delete file="${dists.dir}/archives/${dist.name}.tar" /> <checksum fileext=".md5"> <fileset dir="${dists.dir}/archives"> @@ -54,10 +59,15 @@ MAIN DISTRIBUTION PACKAGING <tarfileset dir="${dist.dir}/doc/scala-devel-docs" prefix="${dist.name}-devel-docs"/> </tar> <gzip src="${dists.dir}/archives/${dist.name}-devel-docs.tar" destfile="${dists.dir}/archives/${dist.name}-devel-docs.tgz"/> - <exec executable="xz" failifexecutionfails="false"> - <arg line="-k -9e -S .xz ${dists.dir}/archives/${dist.name}-devel-docs.tar"/> - </exec> - <move file="${dists.dir}/archives/${dist.name}-devel-docs.tar.xz" tofile="${dists.dir}/archives/${dist.name}-devel-docs.txz" failonerror="false"/> + <if> + <not><equals arg1="${archives.skipxz}" arg2="true" /></not> + <then> + <exec executable="xz" failifexecutionfails="false"> + <arg line="-k -9e -S .xz ${dists.dir}/archives/${dist.name}-devel-docs.tar"/> + </exec> + <move file="${dists.dir}/archives/${dist.name}-devel-docs.tar.xz" tofile="${dists.dir}/archives/${dist.name}-devel-docs.txz" failonerror="false"/> + </then> + </if> <delete file="${dists.dir}/archives/${dist.name}-devel-docs.tar" /> <checksum fileext=".md5"> <fileset dir="${dists.dir}/archives"> @@ -84,10 +94,15 @@ MAIN DISTRIBUTION PACKAGING </tarfileset> </tar> <gzip src="${dists.dir}/archives/${dist.name}-sources.tar" destfile="${dists.dir}/archives/${dist.name}-sources.tgz"/> - <exec executable="xz" failifexecutionfails="false"> - <arg line="-k -9e -S .xz ${dists.dir}/archives/${dist.name}-sources.tar"/> - </exec> - <move file="${dists.dir}/archives/${dist.name}-sources.tar.xz" tofile="${dists.dir}/archives/${dist.name}-sources.txz" failonerror="false"/> + <if> + <not><equals arg1="${archives.skipxz}" arg2="true" /></not> + <then> + <exec executable="xz" failifexecutionfails="false"> + <arg line="-k -9e -S .xz ${dists.dir}/archives/${dist.name}-sources.tar"/> + </exec> + <move file="${dists.dir}/archives/${dist.name}-sources.tar.xz" tofile="${dists.dir}/archives/${dist.name}-sources.txz" failonerror="false"/> + </then> + </if> <delete file="${dists.dir}/archives/${dist.name}-sources.tar" /> <checksum fileext=".md5"> <fileset dir="${dists.dir}/archives"> diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 59651bcdf9..7f066a2cc3 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -36,7 +36,8 @@ trait GenSymbols { else if (sym.isEmptyPackageClass) mirrorMirrorSelect(nme.EmptyPackageClass) else if (sym.isModuleClass) - Select(Select(reify(sym.sourceModule), nme.asModule), nme.moduleClass) + if (sym.sourceModule.isLocatable) Select(Select(reify(sym.sourceModule), nme.asModule), nme.moduleClass) + else reifySymDef(sym) else if (sym.isPackage) mirrorMirrorCall(nme.staticPackage, reify(sym.fullName)) else if (sym.isLocatable) { diff --git a/src/compiler/scala/reflect/reify/utils/SymbolTables.scala b/src/compiler/scala/reflect/reify/utils/SymbolTables.scala index a7ac299317..3892c86dd3 100644 --- a/src/compiler/scala/reflect/reify/utils/SymbolTables.scala +++ b/src/compiler/scala/reflect/reify/utils/SymbolTables.scala @@ -174,15 +174,18 @@ trait SymbolTables { if (sym.annotations.isEmpty) EmptyTree else Apply(Select(currtab.symRef(sym), nme.setAnnotations), List(reifier.reify(sym.annotations))) } else { - import scala.reflect.internal.Flags._ - if (sym hasFlag LOCKED) { - // [Eugene] better to have a symbol without a type signature, than to crash with a CyclicReference - EmptyTree - } else { - val rset = reifier.mirrorBuildCall(nme.setTypeSignature, currtab.symRef(sym), reifier.reify(sym.info)) - if (sym.annotations.isEmpty) rset - else reifier.mirrorBuildCall(nme.setAnnotations, rset, reifier.mkList(sym.annotations map reifier.reifyAnnotationInfo)) - } + // SI-6204 don't reify signatures for incomplete symbols, because this might lead to cyclic reference errors + val signature = + if (sym.isInitialized) { + if (sym.isCapturedVariable) capturedVariableType(sym) + else sym.info + } else NoType + val rset = reifier.mirrorBuildCall(nme.setTypeSignature, currtab.symRef(sym), reifier.reify(signature)) + // `Symbol.annotations` doesn't initialize the symbol, so we don't need to do anything special here + // also since we call `sym.info` a few lines above, by now the symbol will be initialized (if possible) + // so the annotations will be filled in and will be waiting to be reified (unless symbol initialization is prohibited as described above) + if (sym.annotations.isEmpty) rset + else reifier.mirrorBuildCall(nme.setAnnotations, rset, reifier.mkList(sym.annotations map reifier.reifyAnnotationInfo)) } } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 3f4c51748c..6ecdbf1f3f 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -219,10 +219,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // not deprecated yet, but a method called "error" imported into // nearly every trait really must go. For now using globalError. - def error(msg: String) = globalError(msg) - def globalError(msg: String) = reporter.error(NoPosition, msg) - def inform(msg: String) = reporter.echo(msg) - def warning(msg: String) = reporter.warning(NoPosition, msg) + def error(msg: String) = globalError(msg) + def inform(msg: String) = reporter.echo(msg) + override def globalError(msg: String) = reporter.error(NoPosition, msg) + override def warning(msg: String) = + if (settings.fatalWarnings.value) globalError(msg) + else reporter.warning(NoPosition, msg) // Getting in front of Predef's asserts to supplement with more info. // This has the happy side effect of masking the one argument forms diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 687106de09..5828a6200e 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -661,13 +661,15 @@ abstract class Inliners extends SubComponent { * * TODO handle more robustly the case of a trait var changed at the source-level from public to private[this] * (eg by having ICodeReader use unpickler, see SI-5442). - * */ + + DISABLED + def potentiallyPublicized(f: Symbol): Boolean = { (m.sourceFile eq NoSourceFile) && f.name.containsChar('$') } + */ - def checkField(f: Symbol) = check(f, potentiallyPublicized(f) || - (f.isPrivate && !canMakePublic(f))) + def checkField(f: Symbol) = check(f, f.isPrivate && !canMakePublic(f)) def checkSuper(n: Symbol) = check(n, n.isPrivate || !n.isClassConstructor) def checkMethod(n: Symbol) = check(n, n.isPrivate) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index d76d72bf70..e4338a6421 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -728,8 +728,7 @@ abstract class ICodeReader extends ClassfileParser { import analysis.typeFlowLattice.IState /** Abstract interpretation for one instruction. */ - override def interpret(in: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = { - var out = IState(new VarBinding(in.vars), new TypeStack(in.stack)) + override def mutatingInterpret(out: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = { val bindings = out.vars val stack = out.stack import stack.push @@ -737,12 +736,10 @@ abstract class ICodeReader extends ClassfileParser { case DUP_X1 => val (one, two) = stack.pop2 push(one); push(two); push(one); - out = IState(bindings, stack) case DUP_X2 => val (one, two, three) = stack.pop3 push(one); push(three); push(two); push(one); - out = IState(bindings, stack) case DUP2_X1 => val (one, two) = stack.pop2 @@ -752,7 +749,6 @@ abstract class ICodeReader extends ClassfileParser { val three = stack.pop push(two); push(one); push(three); push(two); push(one); } - out = IState(bindings, stack) case DUP2_X2 => val (one, two) = stack.pop2 @@ -771,10 +767,9 @@ abstract class ICodeReader extends ClassfileParser { push(two); push(one); push(four); push(one); push(three); push(two); push(one); } } - out = IState(bindings, stack) case _ => - out = super.interpret(in, i) + super.mutatingInterpret(out, i) } out } diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 108c5ced6f..436867257a 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyrights 2005-2011 LAMP/EPFL + * Copyright 2005-2012 LAMP/EPFL * @author Martin Odersky */ diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index f7d7f35f69..34f5ac611c 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -497,16 +497,10 @@ abstract class ExplicitOuter extends InfoTransform else atPos(tree.pos)(outerPath(outerValue, currentClass.outerClass, sym)) // (5) case Select(qual, name) => - /** return closest enclosing method, unless shadowed by an enclosing class; - * no use of closures here in the interest of speed. - */ - def closestEnclMethod(from: Symbol): Symbol = - if (from.isSourceMethod) from - else if (from.isClass) NoSymbol - else closestEnclMethod(from.owner) - + // make not private symbol acessed from inner classes, as well as + // symbols accessed from @inline methods if (currentClass != sym.owner || - (closestEnclMethod(currentOwner) hasAnnotation ScalaInlineClass)) + (sym.owner.enclMethod hasAnnotation ScalaInlineClass)) sym.makeNotPrivate(sym.owner) val qsym = qual.tpe.widen.typeSymbol diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 32ddd37877..21ed10dfdf 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -28,9 +28,6 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { /** the following two members override abstract members in Transform */ val phaseName: String = "extmethods" - /** The following flags may be set by this phase: */ - override def phaseNewFlags: Long = notPRIVATE - def newTransformer(unit: CompilationUnit): Transformer = new Extender(unit) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 651f172fe2..e6c886b6d6 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -452,7 +452,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { def survivingParams(params: List[Symbol], env: TypeEnv) = params filter { p => - !p.isSpecialized || + !p.isSpecialized || !env.contains(p) || !isPrimitiveValueType(env(p)) } @@ -506,16 +506,16 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * was both already used for a map and mucho long. So "sClass" is the * specialized subclass of "clazz" throughout this file. */ - + // SI-5545: Eliminate classes with the same name loaded from the bytecode already present - all we need to do is // to force .info on them, as their lazy type will be evaluated and the symbols will be eliminated. Unfortunately // evaluating the info after creating the specialized class will mess the specialized class signature, so we'd - // better evaluate it before creating the new class symbol + // better evaluate it before creating the new class symbol val clazzName = specializedName(clazz, env0).toTypeName - val bytecodeClazz = clazz.owner.info.decl(clazzName) + val bytecodeClazz = clazz.owner.info.decl(clazzName) // debuglog("Specializing " + clazz + ", but found " + bytecodeClazz + " already there") bytecodeClazz.info - + val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE) def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long, newName: Name = null) = @@ -652,11 +652,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { info(specMember) = Implementation(original) typeEnv(specMember) = env ++ typeEnv(m) - } - else debuglog({ + } else { val om = forwardToOverload(m) - "normalizedMember " + m + " om: " + om + " " + pp(typeEnv(om)) - }) + debuglog("normalizedMember " + m + " om: " + om + " " + pp(typeEnv(om))) + } } else debuglog("conflicting env for " + m + " env: " + env) @@ -762,7 +761,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } } - + val subclasses = specializations(clazz.info.typeParams) filter satisfiable subclasses foreach { env => @@ -1006,7 +1005,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * Fails if such an environment cannot be found. * * If `strict` is true, a UnifyError is thrown if unification is impossible. - * + * * If `tparams` is true, then the methods tries to unify over type params in polytypes as well. */ private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean, tparams: Boolean = false): TypeEnv = (tp1, tp2) match { @@ -1185,7 +1184,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { || specializedTypeVars(t1).nonEmpty || specializedTypeVars(t2).nonEmpty) } - + env forall { case (tvar, tpe) => matches(tvar.info.bounds.lo, tpe) && matches(tpe, tvar.info.bounds.hi) || { if (warnings) @@ -1201,7 +1200,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } } - + def satisfiabilityConstraints(env: TypeEnv): Option[TypeEnv] = { val noconstraints = Some(emptyEnv) def matches(tpe1: Type, tpe2: Type): Option[TypeEnv] = { @@ -1232,7 +1231,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } with typechecker.Duplicators { private val (castfrom, castto) = casts.unzip private object CastMap extends SubstTypeMap(castfrom.toList, castto.toList) - + class BodyDuplicator(_context: Context) extends super.BodyDuplicator(_context) { override def castType(tree: Tree, pt: Type): Tree = { // log(" expected type: " + pt) @@ -1249,9 +1248,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { ntree } } - + protected override def newBodyDuplicator(context: Context) = new BodyDuplicator(context) - + } /** A tree symbol substituter that substitutes on type skolems. @@ -1359,7 +1358,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } } - + def reportError[T](body: =>T)(handler: TypeError => T): T = try body catch { @@ -1396,7 +1395,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { else None } else None } - + curTree = tree tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => @@ -1570,7 +1569,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { }) debuglog("created special overload tree " + t) debuglog("created " + t) - reportError { + reportError { localTyper.typed(t) } { _ => super.transform(tree) @@ -1629,9 +1628,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { super.transform(tree) } } - + /** Duplicate the body of the given method `tree` to the new symbol `source`. - * + * * Knowing that the method can be invoked only in the `castmap` type environment, * this method will insert casts for all the expressions of types mappend in the * `castmap`. diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 68a2c6d2ba..1e5f8cb604 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -198,6 +198,69 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL import typer.{typed, context, silent, reallyExists} // import typer.infer.containsUnchecked + // Why is it so difficult to say "here's a name and a context, give me any + // matching symbol in scope" ? I am sure this code is wrong, but attempts to + // use the scopes of the contexts in the enclosing context chain discover + // nothing. How to associate a name with a symbol would would be a wonderful + // linkage for which to establish a canonical acquisition mechanism. + def matchingSymbolInScope(pat: Tree): Symbol = { + def declarationOfName(tpe: Type, name: Name): Symbol = tpe match { + case PolyType(tparams, restpe) => tparams find (_.name == name) getOrElse declarationOfName(restpe, name) + case MethodType(params, restpe) => params find (_.name == name) getOrElse declarationOfName(restpe, name) + case ClassInfoType(_, _, clazz) => clazz.rawInfo member name + case _ => NoSymbol + } + pat match { + case Bind(name, _) => + context.enclosingContextChain.foldLeft(NoSymbol: Symbol)((res, ctx) => + res orElse declarationOfName(ctx.owner.rawInfo, name)) + case _ => NoSymbol + } + } + + // Issue better warnings than "unreachable code" when people mis-use + // variable patterns thinking they bind to existing identifiers. + // + // Possible TODO: more deeply nested variable patterns, like + // case (a, b) => 1 ; case (c, d) => 2 + // However this is a pain (at least the way I'm going about it) + // and I have to think these detailed errors are primarily useful + // for beginners, not people writing nested pattern matches. + def checkMatchVariablePatterns(m: Match) { + // A string describing the first variable pattern + var vpat: String = null + // Using an iterator so we can recognize the last case + val it = m.cases.iterator + + def addendum(pat: Tree) = { + matchingSymbolInScope(pat) match { + case NoSymbol => "" + case sym => + val desc = if (sym.isParameter) s"parameter ${sym.nameString} of" else sym + " in" + s"\nIf you intended to match against $desc ${sym.owner}, you must use backticks, like: case `${sym.nameString}` =>" + } + } + + while (it.hasNext) { + val cdef = it.next + // If a default case has been seen, then every succeeding case is unreachable. + if (vpat != null) + context.unit./*error*/warning(cdef.body.pos, "unreachable code due to " + vpat + addendum(cdef.pat)) + // If this is a default case and more cases follow, warn about this one so + // we have a reason to mention its pattern variable name and any corresponding + // symbol in scope. Errors will follow from the remaining cases, at least + // once we make the above warning an error. + else if (it.hasNext && (treeInfo isDefaultCase cdef)) { + val vpatName = cdef.pat match { + case Bind(name, _) => s" '$name'" + case _ => "" + } + vpat = s"variable pattern$vpatName on line ${cdef.pat.pos.line}" + context.unit.warning(cdef.pos, s"patterns after a variable pattern cannot match (SLS 8.1.1)" + addendum(cdef.pat)) + } + } + } + /** Implement a pattern match by turning its cases (including the implicit failure case) * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. * @@ -210,6 +273,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ def translateMatch(match_ : Match): Tree = { val Match(selector, cases) = match_ + checkMatchVariablePatterns(match_) // we don't transform after uncurry // (that would require more sophistication when generating trees, @@ -230,17 +294,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL removeCPSAdaptAnnotations(origPt) else origPt - // we've packed the type for each case in typedMatch so that if all cases have the same existential case, we get a clean lub - // here, we should open up the existential again // relevant test cases: pos/existentials-harmful.scala, pos/gadt-gilles.scala, pos/t2683.scala, pos/virtpatmat_exist4.scala - // TODO: fix skolemizeExistential (it should preserve annotations, right?) - val pt = repeatedToSeq(ptUnCPS.skolemizeExistential(context.owner, context.tree) withAnnotations ptUnCPS.annotations) + // pt is the skolemized version + val pt = repeatedToSeq(ptUnCPS) + + // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner)) // the alternative to attaching the default case override would be to simply // append the default to the list of cases and suppress the unreachable case error that may arise (once we detect that...) val matchFailGenOverride = match_.attachments.get[DefaultOverrideMatchAttachment].map{case DefaultOverrideMatchAttachment(default) => ((scrut: Tree) => default)} - val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS + val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS // 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) @@ -663,8 +727,15 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // binder has type paramType def treeMaker(binder: Symbol, pos: Position): TreeMaker = { + val paramAccessors = binder.constrParamAccessors + // binders corresponding to mutable fields should be stored (SI-5158, SI-6070) + val mutableBinders = + if (paramAccessors exists (_.isMutable)) + subPatBinders.zipWithIndex.collect{ case (binder, idx) if paramAccessors(idx).isMutable => binder } + else Nil + // checks binder ne null before chaining to the next extractor - ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder)) + ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder), mutableBinders) } // reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component @@ -926,10 +997,27 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL atPos(pos)(casegen.flatMapCond(cond, res, nextBinder, substitution(next))) } - trait PreserveSubPatBinders extends NoNewBinders { + // unless we're optimizing, emit local variable bindings for all subpatterns of extractor/case class patterns + protected val debugInfoEmitVars = !settings.optimise.value + + trait PreserveSubPatBinders extends TreeMaker { val subPatBinders: List[Symbol] val subPatRefs: List[Tree] + // unless `debugInfoEmitVars`, this set should contain the bare minimum for correctness + // mutable case class fields need to be stored regardless (SI-5158, SI-6070) -- see override in ProductExtractorTreeMaker + def storedBinders: Set[Symbol] = if (debugInfoEmitVars) subPatBinders.toSet else Set.empty + + def emitVars = storedBinders.nonEmpty + + private lazy val (stored, substed) = (subPatBinders, subPatRefs).zipped.partition{ case (sym, _) => storedBinders(sym) } + + protected lazy val localSubstitution: Substitution = if (!emitVars) Substitution(subPatBinders, subPatRefs) + else { + val (subPatBindersSubstituted, subPatRefsSubstituted) = substed.unzip + Substitution(subPatBindersSubstituted.toList, subPatRefsSubstituted.toList) + } + /** The substitution that specifies the trees that compute the values of the subpattern binders. * * We pretend to replace the subpattern binders by subpattern refs @@ -939,7 +1027,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL Substitution(subPatBinders, subPatRefs) >> super.subPatternsAsSubstitution import CODE._ - def bindSubPats(in: Tree): Tree = Block(map2(subPatBinders, subPatRefs)(VAL(_) === _), in) + def bindSubPats(in: Tree): Tree = if (!emitVars) in + else { + val (subPatBindersStored, subPatRefsStored) = stored.unzip + Block(map2(subPatBindersStored.toList, subPatRefsStored.toList)(VAL(_) === _), in) + } } /** @@ -1000,11 +1092,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree])( val subPatBinders: List[Symbol], - val subPatRefs: List[Tree]) extends FunTreeMaker with PreserveSubPatBinders { + val subPatRefs: List[Tree], + val mutableBinders: List[Symbol]) extends FunTreeMaker with PreserveSubPatBinders { import CODE._ val nextBinder = prevBinder // just passing through + // mutable binders must be stored to avoid unsoundness or seeing mutation of fields after matching (SI-5158, SI-6070) + // (the implementation could be optimized by duplicating code from `super.storedBinders`, but this seems more elegant) + override def storedBinders: Set[Symbol] = super.storedBinders ++ mutableBinders.toSet + def chainBefore(next: Tree)(casegen: Casegen): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL val cond = extraCond map (nullCheck AND _) getOrElse nullCheck @@ -1043,8 +1140,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def outerTest(testedBinder: Symbol, expectedTp: Type): Tree = { val expectedOuter = expectedTp.prefix match { - case ThisType(clazz) => THIS(clazz) - case pre => REF(pre.prefix, pre.termSymbol) + case ThisType(clazz) => THIS(clazz) + case pre if pre != NoType => REF(pre.prefix, pre.termSymbol) + case _ => TRUE_typed // fallback for SI-6183 } // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` @@ -1326,7 +1424,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // local / context-free def _asInstanceOf(b: Symbol, tp: Type): Tree - def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree + def _asInstanceOf(t: Tree, tp: Type): Tree def _equals(checker: Tree, binder: Symbol): Tree def _isInstanceOf(b: Symbol, tp: Type): Tree def and(a: Tree, b: Tree): Tree @@ -1384,7 +1482,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL Typed(gen.mkAsInstanceOf(t, tp.withoutAnnotations, true, false), TypeTree() setType tp) // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly) - def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tp)) t else mkCast(t, tp) + def _asInstanceOf(t: Tree, tp: Type): Tree = if (t.tpe != NoType && t.isTyped && typesConform(t.tpe, tp)) t else mkCast(t, tp) def _asInstanceOf(b: Symbol, tp: Type): Tree = if (typesConform(b.info, tp)) REF(b) else mkCast(REF(b), tp) def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), tp.withoutAnnotations, true, false) // if (typesConform(b.info, tpX)) { patmatDebug("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE } @@ -1753,20 +1851,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL class Prop case class Eq(p: Var, q: Const) extends Prop - type Const <: AbsConst - trait AbsConst { - // when we know V = C, which other equalities must hold - // in general, equality to some type implies equality to its supertypes - // (this multi-valued kind of equality is necessary for unreachability) - // note that we use subtyping as a model for implication between instanceof tests - // i.e., when S <:< T we assume x.isInstanceOf[S] implies x.isInstanceOf[T] - // unfortunately this is not true in general (see e.g. SI-6022) - def implies(other: Const): Boolean - - // does V = C preclude V having value `other`? V = null is an exclusive assignment, - // but V = 1 does not preclude V = Int, or V = Any - def excludes(other: Const): Boolean - } + type Const type TypeConst <: Const def TypeConst: TypeConstExtractor @@ -1801,8 +1886,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def propForEqualsTo(c: Const): Prop // populated by registerEquality - // once equalitySyms has been called, must not call registerEquality anymore - def equalitySyms: List[Sym] + // once implications has been called, must not call registerEquality anymore + def implications: List[(Sym, List[Sym], List[Sym])] } // would be nice to statically check whether a prop is equational or pure, @@ -1901,19 +1986,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL var eqAxioms: Prop = True @inline def addAxiom(p: Prop) = eqAxioms = And(eqAxioms, p) - case class ExcludedPair(a: Const, b: Const) { - override def equals(o: Any) = o match { - case ExcludedPair(aa, bb) => (a == aa && b == bb) || (a == bb && b == aa) - case _ => false - } - // make ExcludedPair(a, b).hashCode == ExcludedPair(b, a).hashCode - override def hashCode = a.hashCode ^ b.hashCode - } - patmatDebug("removeVarEq vars: "+ vars) vars.foreach { v => - val excludedPair = new collection.mutable.HashSet[ExcludedPair] - // if v.domainSyms.isEmpty, we must consider the domain to be infinite // otherwise, since the domain fully partitions the type of the value, // exactly one of the types (and whatever it implies, imposed separately) must be chosen @@ -1928,27 +2002,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL else addAxiom(symForStaticTp) } - val syms = v.equalitySyms - patmatDebug("eqSyms "+(v, syms)) - syms foreach { sym => - // if we've already excluded the pair at some point (-A \/ -B), then don't exclude the symmetric one (-B \/ -A) - // (nor the positive implications -B \/ A, or -A \/ B, which would entail the equality axioms falsifying the whole formula) - val todo = syms filterNot (b => (b.const == sym.const) || excludedPair(ExcludedPair(b.const, sym.const))) - val (excluded, notExcluded) = todo partition (b => sym.const.excludes(b.const)) - val implied = notExcluded filter (b => sym.const.implies(b.const)) - - patmatDebug("eq axioms for: "+ sym.const) - patmatDebug("excluded: "+ excluded) - patmatDebug("implied: "+ implied) - - // when this symbol is true, what must hold... - implied foreach (impliedSym => addAxiom(Or(Not(sym), impliedSym))) - + v.implications foreach { case (sym, implied, excluded) => + // when sym is true, what must hold... + implied foreach (impliedSym => addAxiom(Or(Not(sym), impliedSym))) // ... and what must not? - excluded foreach {excludedSym => - excludedPair += ExcludedPair(sym.const, excludedSym.const) - addAxiom(Or(Not(sym), Not(excludedSym))) - } + excluded foreach (excludedSym => addAxiom(Or(Not(sym), Not(excludedSym)))) } } @@ -2244,22 +2302,121 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL observed; allConsts } - // accessing after calling registerNull will result in inconsistencies - lazy val domainSyms: Option[Set[Sym]] = domain map { _ map symForEqualsTo } - - lazy val symForStaticTp: Option[Sym] = symForEqualsTo.get(TypeConst(staticTpCheckable)) - // populate equalitySyms // don't care about the result, but want only one fresh symbol per distinct constant c def registerEquality(c: Const): Unit = {ensureCanModify; symForEqualsTo getOrElseUpdate(c, Sym(this, c))} - // don't access until all potential equalities have been registered using registerEquality - lazy val equalitySyms = {observed; symForEqualsTo.values.toList} - // return the symbol that represents this variable being equal to the constant `c`, if it exists, otherwise False (for robustness) // (registerEquality(c) must have been called prior, either when constructing the domain or from outside) def propForEqualsTo(c: Const): Prop = {observed; symForEqualsTo.getOrElse(c, False)} + // [implementation NOTE: don't access until all potential equalities have been registered using registerEquality]p + /** the information needed to construct the boolean proposition that encods the equality proposition (V = C) + * + * that models a type test pattern `_: C` or constant pattern `C`, where the type test gives rise to a TypeConst C, + * and the constant pattern yields a ValueConst C + * + * for exhaustivity, we really only need implication (e.g., V = 1 implies that V = 1 /\ V = Int, if both tests occur in the match, + * and thus in this variable's equality symbols), but reachability also requires us to model things like V = 1 precluding V = "1" + */ + lazy val implications = { + /** when we know V = C, which other equalities must hold + * + * in general, equality to some type implies equality to its supertypes + * (this multi-valued kind of equality is necessary for unreachability) + * note that we use subtyping as a model for implication between instanceof tests + * i.e., when S <:< T we assume x.isInstanceOf[S] implies x.isInstanceOf[T] + * unfortunately this is not true in general (see e.g. SI-6022) + */ + def implies(lower: Const, upper: Const): Boolean = + // values and null + lower == upper || + // type implication + (lower != NullConst && !upper.isValue && + instanceOfTpImplies(if (lower.isValue) lower.wideTp else lower.tp, upper.tp)) + + // if(r) patmatDebug("implies : "+(lower, lower.tp, upper, upper.tp)) + // else patmatDebug("NOT implies: "+(lower, upper)) + + + /** does V = C preclude V having value `other`? + (1) V = null is an exclusive assignment, + (2) V = A and V = B, for A and B value constants, are mutually exclusive unless A == B + we err on the safe side, for example: + - assume `val X = 1; val Y = 1`, then + (2: Int) match { case X => case Y => <falsely considered reachable> } + - V = 1 does not preclude V = Int, or V = Any, it could be said to preclude V = String, but we don't model that + + (3) for types we could try to do something fancy, but be conservative and just say no + */ + def excludes(a: Const, b: Const): Boolean = + a != b && ((a == NullConst || b == NullConst) || (a.isValue && b.isValue)) + + // if(r) patmatDebug("excludes : "+(a, a.tp, b, b.tp)) + // else patmatDebug("NOT excludes: "+(a, b)) + +/* +[ HALF BAKED FANCINESS: //!equalitySyms.exists(common => implies(common.const, a) && implies(common.const, b))) + when type tests are involved, we reason (conservatively) under a closed world assumption, + since we are really only trying to counter the effects of the symbols that we introduce to model type tests + we don't aim to model the whole subtyping hierarchy, simply to encode enough about subtyping to do unreachability properly + + consider the following hierarchy: + + trait A + trait B + trait C + trait AB extends B with A + + // two types are mutually exclusive if there is no equality symbol whose constant implies both + object Test extends App { + def foo(x: Any) = x match { + case _ : C => println("C") + case _ : AB => println("AB") + case _ : (A with B) => println("AB'") + case _ : B => println("B") + case _ : A => println("A") + } + + of course this kind of reasoning is not true in general, + but we can safely pretend types are mutually exclusive as long as there are no counter-examples in the match we're analyzing} +*/ + + val excludedPair = new collection.mutable.HashSet[ExcludedPair] + + case class ExcludedPair(a: Const, b: Const) { + override def equals(o: Any) = o match { + case ExcludedPair(aa, bb) => (a == aa && b == bb) || (a == bb && b == aa) + case _ => false + } + // make ExcludedPair(a, b).hashCode == ExcludedPair(b, a).hashCode + override def hashCode = a.hashCode ^ b.hashCode + } + + equalitySyms map { sym => + // if we've already excluded the pair at some point (-A \/ -B), then don't exclude the symmetric one (-B \/ -A) + // (nor the positive implications -B \/ A, or -A \/ B, which would entail the equality axioms falsifying the whole formula) + val todo = equalitySyms filterNot (b => (b.const == sym.const) || excludedPair(ExcludedPair(b.const, sym.const))) + val (excluded, notExcluded) = todo partition (b => excludes(sym.const, b.const)) + val implied = notExcluded filter (b => implies(sym.const, b.const)) + + patmatDebug("eq axioms for: "+ sym.const) + patmatDebug("excluded: "+ excluded) + patmatDebug("implied: "+ implied) + + excluded foreach { excludedSym => excludedPair += ExcludedPair(sym.const, excludedSym.const)} + + (sym, implied, excluded) + } + } + + // accessing after calling registerNull will result in inconsistencies + lazy val domainSyms: Option[Set[Sym]] = domain map { _ map symForEqualsTo } + + lazy val symForStaticTp: Option[Sym] = symForEqualsTo.get(TypeConst(staticTpCheckable)) + + // don't access until all potential equalities have been registered using registerEquality + private lazy val equalitySyms = {observed; symForEqualsTo.values.toList} // don't call until all equalities have been registered and registerNull has been called (if needed) def describe = toString + ": " + staticTp + domain.map(_.mkString(" ::= ", " | ", "// "+ symForEqualsTo.keys)).getOrElse(symForEqualsTo.keys.mkString(" ::= ", " | ", " | ...")) + " // = " + path @@ -2315,42 +2472,12 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - sealed abstract class Const extends AbsConst { + sealed abstract class Const { def tp: Type - protected def wideTp: Type + def wideTp: Type def isAny = wideTp.typeSymbol == AnyClass - - final def implies(other: Const): Boolean = { - val r = (this, other) match { - case (_: ValueConst, _: ValueConst) => this == other // hashconsed - case (_: ValueConst, _: TypeConst) => instanceOfTpImplies(tp, other.tp) - case (_: TypeConst, _) => instanceOfTpImplies(tp, other.tp) - case _ => false - } - // if(r) patmatDebug("implies : "+(this, other)) - // else patmatDebug("NOT implies: "+(this, other)) - r - } - - // does V = C preclude V having value `other`? V = null is an exclusive assignment, - // but V = 1 does not preclude V = Int, or V = Any - final def excludes(other: Const): Boolean = { - val r = (this, other) match { - case (_, NullConst) => true - case (NullConst, _) => true - // this causes false negative for unreachability, but that's ok: - // example: val X = 1; val Y = 1; (2: Int) match { case X => case Y => /* considered reachable */ } - case (_: ValueConst, _: ValueConst) => this != other - case (_: ValueConst, _: TypeConst) => !(instanceOfTpImplies(tp, other.tp) || instanceOfTpImplies(other.tp, wideTp)) - case (_: TypeConst, _: ValueConst) => !(instanceOfTpImplies(other.tp, tp) || instanceOfTpImplies(tp, other.wideTp)) - case (_: TypeConst, _: TypeConst) => !(instanceOfTpImplies(tp, other.tp) || instanceOfTpImplies(other.tp, tp)) - case _ => false - } - // if(r) patmatDebug("excludes : "+(this, this.tp, other, other.tp)) - // else patmatDebug("NOT excludes: "+(this, other)) - r - } + def isValue: Boolean //= tp.isStable // note: use reference equality on Const since they're hash-consed (doing type equality all the time is too expensive) // the equals inherited from AnyRef does just this @@ -2362,15 +2489,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // e.g., when we know some value must be of type T, can it still be of type S? (this is the positive formulation of what `excludes` on Const computes) // since we're talking values, there must have been a class involved in creating it, so rephrase our types in terms of classes // (At least conceptually: `true` is an instance of class `Boolean`) - private def widenToClass(tp: Type) = { - // getOrElse to err on the safe side -- all BTS should end in Any, right? - val wideTp = tp.widen - val clsTp = - if (wideTp.typeSymbol.isClass) wideTp - else wideTp.baseTypeSeq.toList.find(_.typeSymbol.isClass).getOrElse(AnyClass.tpe) - // patmatDebug("Widening to class: "+ (tp, clsTp, tp.widen, tp.widen.baseTypeSeq, tp.widen.baseTypeSeq.toList.find(_.typeSymbol.isClass))) - clsTp - } + private def widenToClass(tp: Type): Type = + if (tp.typeSymbol.isClass) tp + else tp.baseType(tp.baseClasses.head) object TypeConst extends TypeConstExtractor { def apply(tp: Type) = { @@ -2387,7 +2508,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL private[this] val id: Int = Const.nextTypeId val wideTp = widenToClass(tp) - + def isValue = false override def toString = tp.toString //+"#"+ id } @@ -2431,14 +2552,15 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } sealed class ValueConst(val tp: Type, val wideTp: Type, override val toString: String) extends Const { // patmatDebug("VC"+(tp, wideTp, toString)) - assert(!(tp =:= NullTp)) + assert(!(tp =:= NullTp)) // TODO: assert(!tp.isStable) private[this] val id: Int = Const.nextValueId + def isValue = true } lazy val NullTp = ConstantType(Constant(null)) case object NullConst extends Const { def tp = NullTp - protected def wideTp = NullTp + def wideTp = NullTp def isValue = true override def toString = "null" @@ -2509,7 +2631,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL var reachable = true var caseIndex = 0 - patmatDebug("reachability, vars:\n"+ ((propsCasesFail flatMap gatherVariables) map (_.describe) mkString ("\n"))) + patmatDebug("reachability, vars:\n"+ ((propsCasesFail flatMap gatherVariables).distinct map (_.describe) mkString ("\n"))) patmatDebug("equality axioms:\n"+ cnfString(eqAxiomsCNF)) // invariant (prefixRest.length == current.length) && (prefix.reverse ++ prefixRest == symbolicCasesFail) @@ -2524,10 +2646,10 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL current = current.tail val model = findModelFor(andFormula(eqFreePropToSolvable(current.head), prefix)) - // patmatDebug("trying to reach:\n"+ cnfString(current.head) +"\nunder prefix:\n"+ cnfString(prefix)) - // if (ok) patmatDebug("reached: "+ modelString(model)) + // patmatDebug("trying to reach:\n"+ cnfString(eqFreePropToSolvable(current.head)) +"\nunder prefix:\n"+ cnfString(prefix)) + // if (NoModel ne model) patmatDebug("reached: "+ modelString(model)) - reachable = model ne NoModel + reachable = NoModel ne model } } @@ -2589,17 +2711,19 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // TODO: this is subject to the availability of TypeTags (since an abstract type with a type tag is checkable at run time) def checkableType(tp: Type): Type = { // TODO: this is extremely rough... - object toCheckable extends TypeMap { - def apply(tp: Type) = tp match { - case TypeRef(pre, sym, a :: as) if sym ne ArrayClass => - // replace type args by existentials, since they can't be checked - // TODO: when type tags are available, we will check -- when this is implemented, can we take that into account here? - // TODO: don't reuse sym.typeParams, they have bounds (and those must not be considered) - newExistentialType(sym.typeParams, sym.tpe).asSeenFrom(pre, sym.owner) - case _ => mapOver(tp) + // replace type args by wildcards, since they can't be checked (don't use existentials: overkill) + // TODO: when type tags are available, we will check -- when this is implemented, can we take that into account here? + // similar to typer.infer.approximateAbstracts + object typeArgsToWildcardsExceptArray extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) if args.nonEmpty && (sym ne ArrayClass) => + TypeRef(pre, sym, args map (_ => WildcardType)) + case _ => + mapOver(tp) } } - val res = toCheckable(tp) + + val res = typeArgsToWildcardsExceptArray(tp) patmatDebug("checkable "+(tp, res)) res } @@ -2815,7 +2939,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // node in the tree that describes how to construct a counter-example case class VariableAssignment(variable: Var, equalTo: List[Const], notEqualTo: List[Const], fields: collection.mutable.Map[Symbol, VariableAssignment]) { // need to prune since the model now incorporates all super types of a constant (needed for reachability) - private lazy val uniqueEqualTo = equalTo filterNot (subsumed => equalTo.exists(better => (better ne subsumed) && (better implies subsumed))) + private lazy val uniqueEqualTo = equalTo filterNot (subsumed => equalTo.exists(better => (better ne subsumed) && instanceOfTpImplies(better.tp, subsumed.tp))) private lazy val prunedEqualTo = uniqueEqualTo filterNot (subsumed => variable.staticTpCheckable <:< subsumed.tp) private lazy val ctor = (prunedEqualTo match { case List(TypeConst(tp)) => tp case _ => variable.staticTpCheckable }).typeSymbol.primaryConstructor private lazy val ctorParams = if (ctor == NoSymbol || ctor.paramss.isEmpty) Nil else ctor.paramss.head @@ -3324,16 +3448,12 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL case Some(cds) => cds } - val allReachable = - if (unchecked) true - else { - val unreachables = unreachableCase(caseDefsWithGuards) - unreachables foreach {cd => reportUnreachable(cd.body.pos)} - // a switch with duplicate cases yields a verify error, - // and a switch with duplicate cases and guards cannot soundly be rewritten to an unguarded switch - // (even though the verify error would disappear, the behaviour would change) - unreachables.isEmpty - } + val allReachable = unchecked || { + // a switch with duplicate cases yields a verify error, + // and a switch with duplicate cases and guards cannot soundly be rewritten to an unguarded switch + // (even though the verify error would disappear, the behaviour would change) + unreachableCase(caseDefsWithGuards) map (cd => reportUnreachable(cd.body.pos)) isEmpty + } if (!allReachable) Nil else if (noGuards(caseDefsWithGuards)) { @@ -3481,7 +3601,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { val matchEnd = newSynthCaseLabel("matchEnd") - val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations // + val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations matchEnd setInfo MethodType(List(matchRes), restpe) def newCaseSym = newSynthCaseLabel("case") setInfo MethodType(Nil, restpe) @@ -3492,7 +3612,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val nextCase = newCaseSym _currCase = nextCase - LabelDef(currCase, Nil, mkCase(new OptimizedCasegen(matchEnd, nextCase, restpe))) + LabelDef(currCase, Nil, mkCase(new OptimizedCasegen(matchEnd, nextCase))) } // must compute catchAll after caseLabels (side-effects nextCase) @@ -3517,14 +3637,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL ) } - class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol, restpe: Type) extends CommonCodegen with Casegen { + class OptimizedCasegen(matchEnd: Symbol, nextCase: Symbol) extends CommonCodegen with Casegen { def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = optimizedCodegen.matcher(scrut, scrutSym, restpe)(cases, matchFailGen) // only used to wrap the RHS of a body // res: T // returns MatchMonad[T] - def one(res: Tree): Tree = matchEnd APPLY (_asInstanceOf(res, restpe)) // need cast for GADT magic + def one(res: Tree): Tree = matchEnd APPLY (res) // a jump to a case label is special-cased in typedApply protected def zero: Tree = nextCase APPLY () // prev: MatchMonad[T] diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 02a556e918..5e511591f3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -34,6 +34,9 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT /** the following two members override abstract members in Transform */ val phaseName: String = "superaccessors" + /** The following flags may be set by this phase: */ + override def phaseNewFlags: Long = notPRIVATE + protected def newTransformer(unit: CompilationUnit): Transformer = new SuperAccTransformer(unit) @@ -192,9 +195,11 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } } super.transform(tree) + case ModuleDef(_, _, _) => checkCompanionNameClashes(sym) super.transform(tree) + case Template(_, _, body) => val ownAccDefs = new ListBuffer[Tree] accDefs(currentOwner) = ownAccDefs @@ -221,72 +226,85 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT typeDef.symbol.deSkolemize.setFlag(SPECIALIZED) typeDef - case sel @ Select(qual @ This(_), name) => - // warn if they are selecting a private[this] member which - // also exists in a superclass, because they may be surprised - // to find out that a constructor parameter will shadow a - // field. See SI-4762. - if (settings.lint.value) { - if (sym.isPrivateLocal && sym.paramss.isEmpty) { - qual.symbol.ancestors foreach { parent => - parent.info.decls filterNot (x => x.isPrivate || x.hasLocalFlag) foreach { m2 => - if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) { - unit.warning(sel.pos, - sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name - + " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within " - + sym.owner + " - you may want to give them distinct names." - ) + case sel @ Select(qual, name) => + /** return closest enclosing method, unless shadowed by an enclosing class; + * no use of closures here in the interest of speed. + */ + def closestEnclMethod(from: Symbol): Symbol = + if (from.isSourceMethod) from + else if (from.isClass) NoSymbol + else closestEnclMethod(from.owner) + + if (closestEnclMethod(currentOwner) hasAnnotation definitions.ScalaInlineClass) + sym.makeNotPrivate(sym.owner) + + qual match { + case This(_) => + // warn if they are selecting a private[this] member which + // also exists in a superclass, because they may be surprised + // to find out that a constructor parameter will shadow a + // field. See SI-4762. + if (settings.lint.value) { + if (sym.isPrivateLocal && sym.paramss.isEmpty) { + qual.symbol.ancestors foreach { parent => + parent.info.decls filterNot (x => x.isPrivate || x.hasLocalFlag) foreach { m2 => + if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) { + unit.warning(sel.pos, + sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name + + " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within " + + sym.owner + " - you may want to give them distinct names.") + } + } } } } - } - } - // direct calls to aliases of param accessors to the superclass in order to avoid - // duplicating fields. - if (sym.isParamAccessor && sym.alias != NoSymbol) { - val result = (localTyper.typedPos(tree.pos) { - Select(Super(qual, tpnme.EMPTY) setPos qual.pos, sym.alias) - }).asInstanceOf[Select] - debuglog("alias replacement: " + tree + " ==> " + result);//debug - localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, true)) - } - else { - /** A trait which extends a class and accesses a protected member - * of that class cannot implement the necessary accessor method - * because its implementation is in an implementation class (e.g. - * Foo$class) which inherits nothing, and jvm access restrictions - * require the call site to be in an actual subclass. So non-trait - * classes inspect their ancestors for any such situations and - * generate the accessors. See SI-2296. - */ - // FIXME - this should be unified with needsProtectedAccessor, but some - // subtlety which presently eludes me is foiling my attempts. - val shouldEnsureAccessor = ( - currentClass.isTrait - && sym.isProtected - && sym.enclClass != currentClass - && !sym.owner.isTrait - && (sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass) - && (qual.symbol.info.member(sym.name) ne NoSymbol) - ) - if (shouldEnsureAccessor) { - log("Ensuring accessor for call to protected " + sym.fullLocationString + " from " + currentClass) - ensureAccessor(sel) - } - else - mayNeedProtectedAccessor(sel, List(EmptyTree), false) - } + // direct calls to aliases of param accessors to the superclass in order to avoid + // duplicating fields. + if (sym.isParamAccessor && sym.alias != NoSymbol) { + val result = (localTyper.typedPos(tree.pos) { + Select(Super(qual, tpnme.EMPTY) setPos qual.pos, sym.alias) + }).asInstanceOf[Select] + debuglog("alias replacement: " + tree + " ==> " + result); //debug + localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, true)) + } else { + /** + * A trait which extends a class and accesses a protected member + * of that class cannot implement the necessary accessor method + * because its implementation is in an implementation class (e.g. + * Foo$class) which inherits nothing, and jvm access restrictions + * require the call site to be in an actual subclass. So non-trait + * classes inspect their ancestors for any such situations and + * generate the accessors. See SI-2296. + */ + // FIXME - this should be unified with needsProtectedAccessor, but some + // subtlety which presently eludes me is foiling my attempts. + val shouldEnsureAccessor = ( + currentClass.isTrait + && sym.isProtected + && sym.enclClass != currentClass + && !sym.owner.isTrait + && (sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass) + && (qual.symbol.info.member(sym.name) ne NoSymbol)) + if (shouldEnsureAccessor) { + log("Ensuring accessor for call to protected " + sym.fullLocationString + " from " + currentClass) + ensureAccessor(sel) + } else + mayNeedProtectedAccessor(sel, List(EmptyTree), false) + } - case sel @ Select(Super(_, mix), name) => - if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) { - if (!settings.overrideVars.value) - unit.error(tree.pos, "super may be not be used on "+ sym.accessedOrSelf) - } - else if (isDisallowed(sym)) { - unit.error(tree.pos, "super not allowed here: use this." + name.decode + " instead") + case Super(_, mix) => + if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) { + if (!settings.overrideVars.value) + unit.error(tree.pos, "super may be not be used on " + sym.accessedOrSelf) + } else if (isDisallowed(sym)) { + unit.error(tree.pos, "super not allowed here: use this." + name.decode + " instead") + } + transformSuperSelect(sel) + + case _ => + mayNeedProtectedAccessor(sel, List(EmptyTree), true) } - transformSuperSelect(sel) case DefDef(mods, name, tparams, vparamss, tpt, rhs) if tree.symbol.isMethodWithExtension => treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, withInvalidOwner(transform(rhs))) @@ -294,9 +312,6 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case TypeApply(sel @ Select(qual, name), args) => mayNeedProtectedAccessor(sel, args, true) - case sel @ Select(qual, name) => - mayNeedProtectedAccessor(sel, List(EmptyTree), true) - case Assign(lhs @ Select(qual, name), rhs) => if (lhs.symbol.isVariable && lhs.symbol.isJavaDefined && @@ -311,10 +326,12 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case Apply(fn, args) => assert(fn.tpe != null, tree) treeCopy.Apply(tree, transform(fn), transformArgs(fn.tpe.params, args)) + case Function(vparams, body) => withInvalidOwner { treeCopy.Function(tree, vparams, transform(body)) } + case _ => super.transform(tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7eb53ca7de..6ef45dd56e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -92,8 +92,8 @@ trait Typers extends Modes with Adaptations with Tags { // when true: // - we may virtualize matches (if -Xexperimental and there's a suitable __match in scope) // - we synthesize PartialFunction implementations for `x => x match {...}` and `match {...}` when the expected type is PartialFunction - // this is disabled by: -Xoldpatmat, scaladoc or interactive compilation - @inline private def newPatternMatching = !settings.XoldPatmat.value && !forScaladoc && !forInteractive // && (phase.id < currentRun.uncurryPhase.id) + // this is disabled by: -Xoldpatmat or interactive compilation (we run it for scaladoc due to SI-5933) + @inline private def newPatternMatching = !settings.XoldPatmat.value && !forInteractive //&& !forScaladoc && (phase.id < currentRun.uncurryPhase.id) abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors { import context0.unit @@ -1551,10 +1551,15 @@ trait Typers extends Modes with Adaptations with Tags { */ def validateParentClasses(parents: List[Tree], selfType: Type) { val pending = ListBuffer[AbsTypeError]() - def validateParentClass(parent: Tree, superclazz: Symbol) { + @inline def validateDynamicParent(parent: Symbol) = + if (parent == DynamicClass) checkFeature(parent.pos, DynamicsFeature) + + def validateParentClass(parent: Tree, superclazz: Symbol) = if (!parent.isErrorTyped) { val psym = parent.tpe.typeSymbol.initialize + checkStablePrefixClassType(parent) + if (psym != superclazz) { if (psym.isTrait) { val ps = psym.info.parents @@ -1564,6 +1569,7 @@ trait Typers extends Modes with Adaptations with Tags { pending += ParentNotATraitMixinError(parent, psym) } } + if (psym.isFinal) pending += ParentFinalInheritanceError(parent, psym) @@ -1586,12 +1592,17 @@ trait Typers extends Modes with Adaptations with Tags { pending += ParentSelfTypeConformanceError(parent, selfType) if (settings.explaintypes.value) explainTypes(selfType, parent.tpe.typeOfThis) } + if (parents exists (p => p != parent && p.tpe.typeSymbol == psym && !psym.isError)) pending += ParentInheritedTwiceError(parent, psym) + + validateDynamicParent(psym) } + + if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) { + val superclazz = parents.head.tpe.typeSymbol + for (p <- parents) validateParentClass(p, superclazz) } - if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) - for (p <- parents) validateParentClass(p, parents.head.tpe.typeSymbol) /* if (settings.Xshowcls.value != "" && @@ -1817,7 +1828,7 @@ trait Typers extends Modes with Adaptations with Tags { checkNonCyclic(vdef, tpt1) if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable) - VolatileValueError(vdef) + VolatileValueError(vdef) val rhs1 = if (vdef.rhs.isEmpty) { @@ -2630,7 +2641,6 @@ trait Typers extends Modes with Adaptations with Tags { def includesTargetPos(tree: Tree) = tree.pos.isRange && context.unit.exists && (tree.pos includes context.unit.targetPos) val localTarget = stats exists includesTargetPos - val statsErrors = scala.collection.mutable.LinkedHashSet[AbsTypeError]() def typedStat(stat: Tree): Tree = { if (context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(stat)) OnlyDeclarationsError(stat) @@ -2649,7 +2659,6 @@ trait Typers extends Modes with Adaptations with Tags { stat } else { val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) { - context.flushBuffer() this } else newTyper(context.make(stat, exprOwner)) // XXX this creates a spurious dead code warning if an exception is thrown @@ -2666,7 +2675,6 @@ trait Typers extends Modes with Adaptations with Tags { "a pure expression does nothing in statement position; " + "you may be omitting necessary parentheses" ) - statsErrors ++= localTyper.context.errBuffer result } } @@ -2749,12 +2757,7 @@ trait Typers extends Modes with Adaptations with Tags { } } - val stats1 = withSavedContext(context) { - val result = stats mapConserve typedStat - context.flushBuffer() - result - } - context.updateBuffer(statsErrors) + val stats1 = stats mapConserve typedStat if (phase.erasedTypes) stats1 else { checkNoDoubleDefs(stats1) @@ -3038,7 +3041,19 @@ trait Typers extends Modes with Adaptations with Tags { // target of a call. Since this information is no longer available from // typedArg, it is recorded here. checkDead.updateExpr(fun) - val args1 = typedArgs(args, forArgMode(fun, mode), paramTypes, formals) + + 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 @@ -4958,7 +4973,7 @@ trait Typers extends Modes with Adaptations with Tags { var catches1 = typedCases(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 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) @@ -5025,10 +5040,11 @@ trait Typers extends Modes with Adaptations with Tags { if (isPatternMode) { val uncheckedTypeExtractor = extractorForUncheckedType(tpt.pos, tptTyped.tpe) - val ownType = inferTypedPattern(tptTyped, tptTyped.tpe, pt, canRemedy = uncheckedTypeExtractor.nonEmpty) - // println(s"Typed($expr, ${tpt.tpe}) : $pt --> $ownType (${isFullyDefined(ownType)}, ${makeFullyDefined(ownType)})") + // make fully defined to avoid bounded wildcard types that may be in pt from calling dropExistential (SI-2038) - treeTyped setType (if (isFullyDefined(ownType)) ownType else makeFullyDefined(ownType)) //ownType + 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 diff --git a/src/library/scala/collection/IterableLike.scala b/src/library/scala/collection/IterableLike.scala index 2e9599058f..ac6d754f9e 100644 --- a/src/library/scala/collection/IterableLike.scala +++ b/src/library/scala/collection/IterableLike.scala @@ -171,7 +171,7 @@ self => * fewer elements than size. */ def sliding(size: Int): Iterator[Repr] = sliding(size, 1) - + /** Groups elements in fixed size blocks by passing a "sliding window" * over them (as opposed to partitioning them, as is done in grouped.) * @see [[scala.collection.Iterator]], method `sliding` @@ -293,6 +293,7 @@ self => override /*TraversableLike*/ def view = new IterableView[A, Repr] { protected lazy val underlying = self.repr + override def isEmpty = self.isEmpty override def iterator = self.iterator } diff --git a/src/library/scala/collection/SeqLike.scala b/src/library/scala/collection/SeqLike.scala index 416aa916b4..81db2d583a 100644 --- a/src/library/scala/collection/SeqLike.scala +++ b/src/library/scala/collection/SeqLike.scala @@ -627,6 +627,7 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[ override def view = new SeqView[A, Repr] { protected lazy val underlying = self.repr + override def isEmpty = self.isEmpty override def iterator = self.iterator override def length = self.length override def apply(idx: Int) = self.apply(idx) diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 641dd095da..0345f05355 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -86,7 +86,7 @@ trait TraversableLike[+A, +Repr] extends Any def repr: Repr = this.asInstanceOf[Repr] final def isTraversableAgain: Boolean = true - + /** The underlying collection seen as an instance of `$Coll`. * By default this is implemented as the current collection object itself, * but this can be overridden. @@ -174,7 +174,7 @@ trait TraversableLike[+A, +Repr] extends Any * * @usecase def ++:[B](that: TraversableOnce[B]): $Coll[B] * @inheritdoc - * + * * Example: * {{{ * scala> val x = List(1) @@ -655,6 +655,7 @@ trait TraversableLike[+A, +Repr] extends Any def view = new TraversableView[A, Repr] { protected lazy val underlying = self.repr override def foreach[U](f: A => U) = self foreach f + override def isEmpty = self.isEmpty } /** Creates a non-strict view of a slice of this $coll. diff --git a/src/library/scala/collection/TraversableViewLike.scala b/src/library/scala/collection/TraversableViewLike.scala index bf4f8205d6..7fbcf1374b 100644 --- a/src/library/scala/collection/TraversableViewLike.scala +++ b/src/library/scala/collection/TraversableViewLike.scala @@ -59,7 +59,7 @@ trait ViewMkString[+A] { * $viewInfo * * All views for traversable collections are defined by creating a new `foreach` method. - * + * * @author Martin Odersky * @version 2.8 * @since 2.8 @@ -162,7 +162,7 @@ trait TraversableViewLike[+A, // if (b.isInstanceOf[NoBuilder[_]]) newFlatMapped(f).asInstanceOf[That] // else super.flatMap[B, That](f)(bf) } - override def flatten[B](implicit asTraversable: A => /*<:<!!!*/ GenTraversableOnce[B]) = + override def flatten[B](implicit asTraversable: A => /*<:<!!!*/ GenTraversableOnce[B]) = newFlatMapped(asTraversable) private[this] implicit def asThis(xs: Transformed[A]): This = xs.asInstanceOf[This] @@ -193,6 +193,15 @@ trait TraversableViewLike[+A, override def span(p: A => Boolean): (This, This) = (newTakenWhile(p), newDroppedWhile(p)) override def splitAt(n: Int): (This, This) = (newTaken(n), newDropped(n)) + // Without this, isEmpty tests go back to the Traversable default, which + // involves starting a foreach, which can force the first element of the + // view. This is just a backstop - it's overridden at all the "def view" + // instantiation points in the collections where the Coll type is known. + override def isEmpty = underlying match { + case x: GenTraversableOnce[_] => x.isEmpty + case _ => super.isEmpty + } + override def scanLeft[B, That](z: B)(op: (B, A) => B)(implicit bf: CanBuildFrom[This, B, That]): That = newForced(thisSeq.scanLeft(z)(op)).asInstanceOf[That] diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala index 2cf9985523..0b297aeb45 100644 --- a/src/library/scala/collection/immutable/HashMap.scala +++ b/src/library/scala/collection/immutable/HashMap.scala @@ -72,19 +72,8 @@ class HashMap[A, +B] extends AbstractMap[A, B] } private[collection] def computeHash(key: A) = improve(elemHashCode(key)) - - protected type MergeFunction[A1, B1] = ((A1, B1), (A1, B1)) => (A1, B1); - - import HashMap.Merger - protected def liftMerger[A1, B1](mergef: MergeFunction[A1, B1]): Merger[A1, B1] = if (mergef == null) null else new Merger[A1, B1] { - self => - def apply(kv1: (A1, B1), kv2: (A1, B1)): (A1, B1) = mergef(kv1, kv2) - val invert: Merger[A1, B1] = new Merger[A1, B1] { - def apply(kv1: (A1, B1), kv2: (A1, B1)): (A1, B1) = mergef(kv2, kv1) - def invert: Merger[A1, B1] = self - } - } + import HashMap.{Merger, MergeFunction, liftMerger} private[collection] def get0(key: A, hash: Int, level: Int): Option[B] = None @@ -130,11 +119,26 @@ class HashMap[A, +B] extends AbstractMap[A, B] */ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { - private[immutable] abstract class Merger[A, B] { + private abstract class Merger[A, B] { def apply(kv1: (A, B), kv2: (A, B)): (A, B) def invert: Merger[A, B] } - + + private type MergeFunction[A1, B1] = ((A1, B1), (A1, B1)) => (A1, B1) + + private def liftMerger[A1, B1](mergef: MergeFunction[A1, B1]): Merger[A1, B1] = + if (mergef == null) defaultMerger.asInstanceOf[Merger[A1, B1]] else liftMerger0(mergef) + + private[this] val defaultMerger : Merger[Any, Any] = liftMerger0((a,b) => a) + + private[this] def liftMerger0[A1, B1](mergef: MergeFunction[A1, B1]): Merger[A1, B1] = new Merger[A1, B1] { + self => + def apply(kv1: (A1, B1), kv2: (A1, B1)): (A1, B1) = mergef(kv1, kv2) + val invert: Merger[A1, B1] = new Merger[A1, B1] { + def apply(kv1: (A1, B1), kv2: (A1, B1)): (A1, B1) = mergef(kv2, kv1) + def invert: Merger[A1, B1] = self + } + } /** $mapCanBuildFromInfo */ implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), HashMap[A, B]] = new MapCanBuildFrom[A, B] diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index c60fdc3bf1..ef0173337c 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -151,10 +151,12 @@ object HashSet extends ImmutableSetFactory[HashSet] { override def removed0(key: A, hash: Int, level: Int): HashSet[A] = if (hash == this.hash) { val ks1 = ks - key - if (!ks1.isEmpty) - new HashSetCollision1(hash, ks1) - else + if(ks1.isEmpty) HashSet.empty[A] + else if(ks1.tail.isEmpty) + new HashSet1(ks1.head, hash) + else + new HashSetCollision1(hash, ks1) } else this override def iterator: Iterator[A] = ks.iterator @@ -236,7 +238,12 @@ object HashSet extends ImmutableSetFactory[HashSet] { Array.copy(elems, 0, elemsNew, 0, offset) Array.copy(elems, offset + 1, elemsNew, offset, elems.length - offset - 1) val sizeNew = size - sub.size - new HashTrieSet(bitmapNew, elemsNew, sizeNew) + // if we have only one child, which is not a HashTrieSet but a self-contained set like + // HashSet1 or HashSetCollision1, return the child instead + if (elemsNew.length == 1 && !elemsNew(0).isInstanceOf[HashTrieSet[_]]) + elemsNew(0) + else + new HashTrieSet(bitmapNew, elemsNew, sizeNew) } else HashSet.empty[A] } else { diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala index ce3abaacb7..4dd0d62fc0 100644 --- a/src/library/scala/collection/immutable/ListSet.scala +++ b/src/library/scala/collection/immutable/ListSet.scala @@ -116,8 +116,8 @@ class ListSet[A] extends AbstractSet[A] def hasNext = that.nonEmpty def next: A = if (hasNext) { - val res = that.elem - that = that.next + val res = that.head + that = that.tail res } else Iterator.empty.next @@ -126,18 +126,18 @@ class ListSet[A] extends AbstractSet[A] /** * @throws Predef.NoSuchElementException */ - protected def elem: A = throw new NoSuchElementException("Set has no elements"); + override def head: A = throw new NoSuchElementException("Set has no elements"); /** * @throws Predef.NoSuchElementException */ - protected def next: ListSet[A] = throw new NoSuchElementException("Next of an empty set"); + override def tail: ListSet[A] = throw new NoSuchElementException("Next of an empty set"); override def stringPrefix = "ListSet" /** Represents an entry in the `ListSet`. */ - protected class Node(override protected val elem: A) extends ListSet[A] with Serializable { + protected class Node(override val head: A) extends ListSet[A] with Serializable { override private[ListSet] def unchecked_outer = self /** Returns the number of elements in this set. @@ -162,7 +162,7 @@ class ListSet[A] extends AbstractSet[A] */ override def contains(e: A) = containsInternal(this, e) @tailrec private def containsInternal(n: ListSet[A], e: A): Boolean = - !n.isEmpty && (n.elem == e || containsInternal(n.unchecked_outer, e)) + !n.isEmpty && (n.head == e || containsInternal(n.unchecked_outer, e)) /** This method creates a new set with an additional element. */ @@ -170,10 +170,10 @@ class ListSet[A] extends AbstractSet[A] /** `-` can be used to remove a single element from a set. */ - override def -(e: A): ListSet[A] = if (e == elem) self else { - val tail = self - e; new tail.Node(elem) + override def -(e: A): ListSet[A] = if (e == head) self else { + val tail = self - e; new tail.Node(head) } - override protected def next: ListSet[A] = self + override def tail: ListSet[A] = self } } diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index 9f5f98ddf4..1bf1e20694 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -916,6 +916,7 @@ self => override def view = new StreamView[A, Stream[A]] { protected lazy val underlying = self.repr + override def isEmpty = self.isEmpty override def iterator = self.iterator override def length = self.length override def apply(idx: Int) = self.apply(idx) diff --git a/src/library/scala/collection/mutable/IndexedSeqLike.scala b/src/library/scala/collection/mutable/IndexedSeqLike.scala index 2ff7ac8272..5d4b4de7b2 100644 --- a/src/library/scala/collection/mutable/IndexedSeqLike.scala +++ b/src/library/scala/collection/mutable/IndexedSeqLike.scala @@ -53,6 +53,7 @@ trait IndexedSeqLike[A, +Repr] extends scala.collection.IndexedSeqLike[A, Repr] */ override def view = new IndexedSeqView[A, Repr] { protected lazy val underlying = self.repr + override def isEmpty = self.isEmpty override def iterator = self.iterator override def length = self.length override def apply(idx: Int) = self.apply(idx) diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index 4feff34751..5d8610a7b7 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -848,6 +848,7 @@ self: ParIterableLike[T, Repr, Sequential] => override def seq = self.seq.view def splitter = self.splitter def size = splitter.remaining + override def isEmpty = size == 0 } override def toArray[U >: T: ClassTag]: Array[U] = { diff --git a/src/library/scala/collection/parallel/ParSeqLike.scala b/src/library/scala/collection/parallel/ParSeqLike.scala index be5ab03ba7..27e8eeb174 100644 --- a/src/library/scala/collection/parallel/ParSeqLike.scala +++ b/src/library/scala/collection/parallel/ParSeqLike.scala @@ -44,7 +44,7 @@ trait ParSeqLike[+T, +Repr <: ParSeq[T], +Sequential <: Seq[T] with SeqLike[T, S extends scala.collection.GenSeqLike[T, Repr] with ParIterableLike[T, Repr, Sequential] { self => - + type SuperParIterator = IterableSplitter[T] /** A more refined version of the iterator found in the `ParallelIterable` trait, @@ -330,6 +330,7 @@ self => def apply(idx: Int) = self(idx) override def seq = self.seq.view def splitter = self.splitter + override def isEmpty = size == 0 } /* tasks */ diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 8081bb32da..1be6050303 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -12,6 +12,7 @@ package scala.concurrent import java.util.concurrent.{ ExecutorService, Executor } import scala.concurrent.util.Duration import scala.annotation.implicitNotFound +import scala.util.Try /** * An `ExecutionContext` is an abstraction over an entity that can execute program logic. @@ -27,6 +28,12 @@ trait ExecutionContext { */ def reportFailure(t: Throwable): Unit + /** Prepares for the execution of a task. Returns the prepared + * execution context. A valid implementation of `prepare` is one + * that simply returns `this`. + */ + def prepare(): ExecutionContext = this + } /** diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index d24fdbf005..bc0b437a33 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -117,7 +117,7 @@ trait Future[+T] extends Awaitable[T] { * $callbackInContext */ def onSuccess[U](pf: PartialFunction[T, U])(implicit executor: ExecutionContext): Unit = onComplete { - case Right(v) if pf isDefinedAt v => pf(v) + case Success(v) if pf isDefinedAt v => pf(v) case _ => }(executor) @@ -135,7 +135,7 @@ trait Future[+T] extends Awaitable[T] { * $callbackInContext */ def onFailure[U](callback: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit = onComplete { - case Left(t) if NonFatal(t) && callback.isDefinedAt(t) => callback(t) + case Failure(t) if NonFatal(t) && callback.isDefinedAt(t) => callback(t) case _ => }(executor) @@ -148,7 +148,7 @@ trait Future[+T] extends Awaitable[T] { * $multipleCallbacks * $callbackInContext */ - def onComplete[U](func: Either[Throwable, T] => U)(implicit executor: ExecutionContext): Unit + def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit /* Miscellaneous */ @@ -169,7 +169,7 @@ trait Future[+T] extends Awaitable[T] { * if it contains a valid result, or `Some(Failure(error))` if it contains * an exception. */ - def value: Option[Either[Throwable, T]] + def value: Option[Try[T]] /* Projections */ @@ -190,8 +190,8 @@ trait Future[+T] extends Awaitable[T] { val p = Promise[Throwable]() onComplete { - case Left(t) => p success t - case Right(v) => p failure (new NoSuchElementException("Future.failed not completed with a throwable.")) + case Failure(t) => p success t + case Success(v) => p failure (new NoSuchElementException("Future.failed not completed with a throwable.")) } p.future @@ -205,7 +205,7 @@ trait Future[+T] extends Awaitable[T] { * Will not be called if the future fails. */ def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = onComplete { - case Right(r) => f(r) + case Success(r) => f(r) case _ => // do nothing }(executor) @@ -227,8 +227,8 @@ trait Future[+T] extends Awaitable[T] { case result => try { result match { - case Left(t) => p failure f(t) - case Right(r) => p success s(r) + case Failure(t) => p failure f(t) + case Success(r) => p success s(r) } } catch { case NonFatal(t) => p failure t @@ -251,8 +251,8 @@ trait Future[+T] extends Awaitable[T] { case result => try { result match { - case Right(r) => p success f(r) - case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, S]] + case Success(r) => p success f(r) + case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] } } catch { case NonFatal(t) => p failure t @@ -273,12 +273,12 @@ trait Future[+T] extends Awaitable[T] { val p = Promise[S]() onComplete { - case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, S]] - case Right(v) => + case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] + case Success(v) => try { f(v).onComplete({ - case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, S]] - case Right(v) => p success v + case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] + case Success(v) => p success v })(internalExecutor) } catch { case NonFatal(t) => p failure t @@ -308,11 +308,11 @@ trait Future[+T] extends Awaitable[T] { val p = Promise[T]() onComplete { - case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, T]] - case Right(v) => + case f: Failure[_] => p complete f.asInstanceOf[Failure[T]] + case Success(v) => try { if (pred(v)) p success v - else p failure new NoSuchElementException("Future.filter predicate is not satisfied by: " + v) + else p failure new NoSuchElementException("Future.filter predicate is not satisfied") } catch { case NonFatal(t) => p failure t } @@ -357,8 +357,8 @@ trait Future[+T] extends Awaitable[T] { val p = Promise[S]() onComplete { - case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, S]] - case Right(v) => + case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] + case Success(v) => try { if (pf.isDefinedAt(v)) p success pf(v) else p failure new NoSuchElementException("Future.collect partial function is not defined at: " + v) @@ -377,22 +377,15 @@ trait Future[+T] extends Awaitable[T] { * Example: * * {{{ - * future (6 / 0) recover { case e: ArithmeticException ⇒ 0 } // result: 0 - * future (6 / 0) recover { case e: NotFoundException ⇒ 0 } // result: exception - * future (6 / 2) recover { case e: ArithmeticException ⇒ 0 } // result: 3 + * future (6 / 0) recover { case e: ArithmeticException => 0 } // result: 0 + * future (6 / 0) recover { case e: NotFoundException => 0 } // result: exception + * future (6 / 2) recover { case e: ArithmeticException => 0 } // result: 3 * }}} */ def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = { val p = Promise[U]() - onComplete { - case Left(t) if pf isDefinedAt t => - try { p success pf(t) } - catch { - case NonFatal(t) => p failure t - } - case otherwise => p complete otherwise - }(executor) + onComplete { case tr => p.complete(tr recover pf) }(executor) p.future } @@ -414,7 +407,7 @@ trait Future[+T] extends Awaitable[T] { val p = Promise[U]() onComplete { - case Left(t) if pf isDefinedAt t => + case Failure(t) if pf isDefinedAt t => try { p completeWith pf(t) } catch { @@ -438,8 +431,8 @@ trait Future[+T] extends Awaitable[T] { val p = Promise[(T, U)]() this onComplete { - case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, (T, U)]] - case Right(r) => + case f: Failure[_] => p complete f.asInstanceOf[Failure[(T, U)]] + case Success(r) => that onSuccess { case r2 => p success ((r, r2)) } @@ -468,8 +461,8 @@ trait Future[+T] extends Awaitable[T] { def fallbackTo[U >: T](that: Future[U]): Future[U] = { val p = Promise[U]() onComplete { - case r @ Right(_) ⇒ p complete r - case _ ⇒ p completeWith that + case s @ Success(_) => p complete s + case _ => p completeWith that } p.future } @@ -485,12 +478,12 @@ trait Future[+T] extends Awaitable[T] { val p = Promise[S]() onComplete { - case l: Left[_, _] => p complete l.asInstanceOf[Left[Throwable, S]] - case Right(t) => + case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] + case Success(t) => p complete (try { - Right(boxedType(tag.runtimeClass).cast(t).asInstanceOf[S]) + Success(boxedType(tag.runtimeClass).cast(t).asInstanceOf[S]) } catch { - case e: ClassCastException => Left(e) + case e: ClassCastException => Failure(e) }) } @@ -520,7 +513,7 @@ trait Future[+T] extends Awaitable[T] { * } * }}} */ - def andThen[U](pf: PartialFunction[Either[Throwable, T], U])(implicit executor: ExecutionContext): Future[T] = { + def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() onComplete { @@ -545,7 +538,7 @@ trait Future[+T] extends Awaitable[T] { */ def either[U >: T](that: Future[U]): Future[U] = { val p = Promise[U]() - val completePromise: PartialFunction[Either[Throwable, U], _] = { case result => p tryComplete result } + val completePromise: PartialFunction[Try[U], _] = { case result => p tryComplete result } this onComplete completePromise that onComplete completePromise @@ -615,7 +608,7 @@ object Future { def firstCompletedOf[T](futures: TraversableOnce[Future[T]])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() - val completeFirst: Either[Throwable, T] => Unit = p tryComplete _ + val completeFirst: Try[T] => Unit = p tryComplete _ futures.foreach(_ onComplete completeFirst) p.future @@ -629,14 +622,14 @@ object Future { else { val result = Promise[Option[T]]() val ref = new AtomicInteger(futures.size) - val search: Either[Throwable, T] => Unit = v => try { + val search: Try[T] => Unit = v => try { v match { - case Right(r) => if (predicate(r)) result tryComplete Right(Some(r)) - case _ => + case Success(r) => if (predicate(r)) result tryComplete Success(Some(r)) + case _ => } } finally { if (ref.decrementAndGet == 0) { - result tryComplete Right(None) + result tryComplete Success(None) } } diff --git a/src/library/scala/concurrent/Promise.scala b/src/library/scala/concurrent/Promise.scala index 5d1b2c00b6..b873939c15 100644 --- a/src/library/scala/concurrent/Promise.scala +++ b/src/library/scala/concurrent/Promise.scala @@ -8,6 +8,8 @@ package scala.concurrent +import scala.util.{ Try, Success, Failure } + /** Promise is an object which can be completed with a value or failed * with an exception. * @@ -49,7 +51,7 @@ trait Promise[T] { * * $promiseCompletion */ - def complete(result: Either[Throwable, T]): this.type = + def complete(result: Try[T]): this.type = if (tryComplete(result)) this else throw new IllegalStateException("Promise already completed.") /** Tries to complete the promise with either a value or the exception. @@ -58,7 +60,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryComplete(result: Either[Throwable, T]): Boolean + def tryComplete(result: Try[T]): Boolean /** Completes this promise with the specified future, once that future is completed. * @@ -84,7 +86,7 @@ trait Promise[T] { * * $promiseCompletion */ - def success(v: T): this.type = complete(Right(v)) + def success(v: T): this.type = complete(Success(v)) /** Tries to complete the promise with a value. * @@ -92,7 +94,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def trySuccess(value: T): Boolean = tryComplete(Right(value)) + def trySuccess(value: T): Boolean = tryComplete(Success(value)) /** Completes the promise with an exception. * @@ -102,7 +104,7 @@ trait Promise[T] { * * $promiseCompletion */ - def failure(t: Throwable): this.type = complete(Left(t)) + def failure(t: Throwable): this.type = complete(Failure(t)) /** Tries to complete the promise with an exception. * @@ -110,7 +112,7 @@ trait Promise[T] { * * @return If the promise has already been completed returns `false`, or `true` otherwise. */ - def tryFailure(t: Throwable): Boolean = tryComplete(Left(t)) + def tryFailure(t: Throwable): Boolean = tryComplete(Failure(t)) } @@ -129,14 +131,14 @@ object Promise { * @tparam T the type of the value in the promise * @return the newly created `Promise` object */ - def failed[T](exception: Throwable): Promise[T] = new impl.Promise.KeptPromise[T](Left(exception)) + def failed[T](exception: Throwable): Promise[T] = new impl.Promise.KeptPromise[T](Failure(exception)) /** Creates an already completed Promise with the specified result. * * @tparam T the type of the value in the promise * @return the newly created `Promise` object */ - def successful[T](result: T): Promise[T] = new impl.Promise.KeptPromise[T](Right(result)) + def successful[T](result: T): Promise[T] = new impl.Promise.KeptPromise[T](Success(result)) } diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index 098008e958..d92691901f 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -12,7 +12,7 @@ package scala.concurrent.impl import scala.concurrent.ExecutionContext import scala.util.control.NonFatal - +import scala.util.{Try, Success, Failure} private[concurrent] object Future { @@ -21,7 +21,7 @@ private[concurrent] object Future { override def run() = { promise complete { - try Right(body) catch { case NonFatal(e) => Left(e) } + try Success(body) catch { case NonFatal(e) => Failure(e) } } } } diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index c2df9ac296..b19bed004b 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -15,42 +15,46 @@ import scala.concurrent.{ ExecutionContext, CanAwait, OnCompleteRunnable, Timeou import scala.concurrent.util.Duration import scala.annotation.tailrec import scala.util.control.NonFatal - +import scala.util.{ Try, Success, Failure } private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] { def future: this.type = this } -private class CallbackRunnable[T](val executor: ExecutionContext, val onComplete: (Either[Throwable, T]) => Any) extends Runnable with OnCompleteRunnable { +/* Precondition: `executor` is prepared, i.e., `executor` has been returned from invocation of `prepare` on some other `ExecutionContext`. + */ +private class CallbackRunnable[T](val executor: ExecutionContext, val onComplete: Try[T] => Any) extends Runnable with OnCompleteRunnable { // must be filled in before running it - var value: Either[Throwable, T] = null + var value: Try[T] = null override def run() = { require(value ne null) // must set value to non-null before running! try onComplete(value) catch { case NonFatal(e) => executor reportFailure e } } - def executeWithValue(v: Either[Throwable, T]): Unit = { + def executeWithValue(v: Try[T]): Unit = { require(value eq null) // can't complete it twice value = v + // Note that we cannot prepare the ExecutionContext at this point, since we might + // already be running on a different thread! executor.execute(this) } } private[concurrent] object Promise { - private def resolveEither[T](source: Either[Throwable, T]): Either[Throwable, T] = source match { - case Left(t) => resolver(t) - case _ => source + private def resolveTry[T](source: Try[T]): Try[T] = source match { + case Failure(t) => resolver(t) + case _ => source } - private def resolver[T](throwable: Throwable): Either[Throwable, T] = throwable match { - case t: scala.runtime.NonLocalReturnControl[_] => Right(t.value.asInstanceOf[T]) - case t: scala.util.control.ControlThrowable => Left(new ExecutionException("Boxed ControlThrowable", t)) - case t: InterruptedException => Left(new ExecutionException("Boxed InterruptedException", t)) - case e: Error => Left(new ExecutionException("Boxed Error", e)) - case t => Left(t) + private def resolver[T](throwable: Throwable): Try[T] = throwable match { + case t: scala.runtime.NonLocalReturnControl[_] => Success(t.value.asInstanceOf[T]) + case t: scala.util.control.ControlThrowable => Failure(new ExecutionException("Boxed ControlThrowable", t)) + case t: InterruptedException => Failure(new ExecutionException("Boxed InterruptedException", t)) + case e: Error => Failure(new ExecutionException("Boxed Error", e)) + case t => Failure(t) } /** Default promise implementation. @@ -88,25 +92,25 @@ private[concurrent] object Promise { @throws(classOf[Exception]) def result(atMost: Duration)(implicit permit: CanAwait): T = ready(atMost).value.get match { - case Left(e) => throw e - case Right(r) => r + case Failure(e) => throw e + case Success(r) => r } - def value: Option[Either[Throwable, T]] = getState match { - case c: Either[_, _] => Some(c.asInstanceOf[Either[Throwable, T]]) - case _ => None + def value: Option[Try[T]] = getState match { + case c: Try[_] => Some(c.asInstanceOf[Try[T]]) + case _ => None } override def isCompleted(): Boolean = getState match { // Cheaper than boxing result into Option due to "def value" - case _: Either[_, _] => true - case _ => false + case _: Try[_] => true + case _ => false } - def tryComplete(value: Either[Throwable, T]): Boolean = { - val resolved = resolveEither(value) + def tryComplete(value: Try[T]): Boolean = { + val resolved = resolveTry(value) (try { @tailrec - def tryComplete(v: Either[Throwable, T]): List[CallbackRunnable[T]] = { + def tryComplete(v: Try[T]): List[CallbackRunnable[T]] = { getState match { case raw: List[_] => val cur = raw.asInstanceOf[List[CallbackRunnable[T]]] @@ -124,13 +128,14 @@ private[concurrent] object Promise { } } - def onComplete[U](func: Either[Throwable, T] => U)(implicit executor: ExecutionContext): Unit = { - val runnable = new CallbackRunnable[T](executor, func) + def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit = { + val preparedEC = executor.prepare + val runnable = new CallbackRunnable[T](preparedEC, func) @tailrec //Tries to add the callback, if already completed, it dispatches the callback to be executed def dispatchOrAddCallback(): Unit = getState match { - case r: Either[_, _] => runnable.executeWithValue(r.asInstanceOf[Either[Throwable, T]]) + case r: Try[_] => runnable.executeWithValue(r.asInstanceOf[Try[T]]) case listeners: List[_] => if (updateState(listeners, runnable :: listeners)) () else dispatchOrAddCallback() } dispatchOrAddCallback() @@ -141,25 +146,23 @@ private[concurrent] object Promise { * * Useful in Future-composition when a value to contribute is already available. */ - final class KeptPromise[T](suppliedValue: Either[Throwable, T]) extends Promise[T] { + final class KeptPromise[T](suppliedValue: Try[T]) extends Promise[T] { - val value = Some(resolveEither(suppliedValue)) + val value = Some(resolveTry(suppliedValue)) override def isCompleted(): Boolean = true - def tryComplete(value: Either[Throwable, T]): Boolean = false + def tryComplete(value: Try[T]): Boolean = false - def onComplete[U](func: Either[Throwable, T] => U)(implicit executor: ExecutionContext): Unit = { + def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit = { val completedAs = value.get - (new CallbackRunnable(executor, func)).executeWithValue(completedAs) + val preparedEC = executor.prepare + (new CallbackRunnable(preparedEC, func)).executeWithValue(completedAs) } def ready(atMost: Duration)(implicit permit: CanAwait): this.type = this - def result(atMost: Duration)(implicit permit: CanAwait): T = value.get match { - case Left(e) => throw e - case Right(r) => r - } + def result(atMost: Duration)(implicit permit: CanAwait): T = value.get.get } } diff --git a/src/library/scala/reflect/base/FlagSets.scala b/src/library/scala/reflect/base/FlagSets.scala index 43de9970c0..96cdbe894c 100644 --- a/src/library/scala/reflect/base/FlagSets.scala +++ b/src/library/scala/reflect/base/FlagSets.scala @@ -13,11 +13,4 @@ trait FlagSets { self: Universe => /** The empty set of flags */ val NoFlags: FlagSet - - /** The base API all flag bearers support */ - trait HasFlagsBase { - def flags: FlagSet - def hasFlag(flags: FlagSet): Boolean - } } - diff --git a/src/library/scala/reflect/base/Trees.scala b/src/library/scala/reflect/base/Trees.scala index 7fa3c90e7d..70993fd77f 100644 --- a/src/library/scala/reflect/base/Trees.scala +++ b/src/library/scala/reflect/base/Trees.scala @@ -1359,7 +1359,9 @@ trait Trees { self: Universe => implicit val ModifiersTag: ClassTag[Modifiers] /** ... */ - abstract class ModifiersBase extends HasFlagsBase { + abstract class ModifiersBase { + def flags: FlagSet // default: NoFlags + def hasFlag(flag: FlagSet): Boolean def privateWithin: Name // default: EmptyTypeName def annotations: List[Tree] // default: List() def mapAnnotations(f: List[Tree] => List[Tree]): Modifiers = diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index f85bac0b84..f381a18b0c 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -54,6 +54,7 @@ import language.implicitConversions * * `Try` comes to the Scala standard library after years of use as an integral part of Twitter's stack. * + * @author based on Twitter's original implementation in com.twitter.util. * @since 2.10 */ sealed abstract class Try[+T] { @@ -102,7 +103,7 @@ sealed abstract class Try[+T] { * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. * This is like `flatMap` for the exception. */ - def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] + def recoverWith[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] /** * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. @@ -116,17 +117,6 @@ sealed abstract class Try[+T] { def toOption = if (isSuccess) Some(get) else None /** - * Returns an empty `Seq` (usually a `List`) if this is a `Failure` or a `Seq` containing the value if this is a `Success`. - */ - def toSeq = if (isSuccess) Seq(get) else Seq() - - /** - * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`. - * Alias for `flatMap`. - */ - def andThen[U](f: T => Try[U]): Try[U] = flatMap(f) - - /** * Transforms a nested `Try`, ie, a `Try` of type `Try[Try[T]]`, * into an un-nested `Try`, ie, a `Try` of type `Try[T]`. */ @@ -141,7 +131,7 @@ sealed abstract class Try[+T] { /** Completes this `Try` by applying the function `f` to this if this is of type `Failure`, or conversely, by applying * `s` if this is a `Success`. */ - def transform[U](f: Throwable => Try[U], s: T => Try[U]): Try[U] = this match { + def transform[U](s: T => Try[U], f: Throwable => Try[U]): Try[U] = this match { case Success(v) => s(v) case Failure(e) => f(e) } @@ -150,20 +140,6 @@ sealed abstract class Try[+T] { object Try { - implicit def try2either[T](tr: Try[T]): Either[Throwable, T] = { - tr match { - case Success(v) => Right(v) - case Failure(t) => Left(t) - } - } - - implicit def either2try[T](ei: Either[Throwable, T]): Try[T] = { - ei match { - case Right(v) => Success(v) - case Left(t) => Failure(t) - } - } - def apply[T](r: => T): Try[T] = { try { Success(r) } catch { case NonFatal(e) => Failure(e) @@ -175,7 +151,7 @@ object Try { final case class Failure[+T](val exception: Throwable) extends Try[T] { def isFailure: Boolean = true def isSuccess: Boolean = false - def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] = + def recoverWith[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] = if (f.isDefinedAt(exception)) f(exception) else this def get: T = throw exception def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](exception) @@ -201,12 +177,12 @@ final case class Failure[+T](val exception: Throwable) extends Try[T] { final case class Success[+T](value: T) extends Try[T] { def isFailure: Boolean = false def isSuccess: Boolean = true - def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] = Success(value) + def recoverWith[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] = Success(value) def get = value def flatMap[U](f: T => Try[U]): Try[U] = try f(value) catch { - case e: Throwable => Failure(e) + case NonFatal(e) => Failure(e) } def flatten[U](implicit ev: T <:< Try[U]): Try[U] = value def foreach[U](f: T => U): Unit = f(value) diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala index 604e53e64c..0f2806214f 100644 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -9,6 +9,7 @@ package scala.tools.partest package nest import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError, io } +import scala.tools.nsc.io.{ File => SFile } import scala.tools.nsc.interactive.RangePositions import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } import scala.tools.nsc.util.{ ClassPath, FakePos } @@ -94,7 +95,17 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { val logFile = basename(log.getName) val flagsFileName = "%s.flags" format (logFile.substring(0, logFile.lastIndexOf("-"))) val argString = (io.File(log).parent / flagsFileName) ifFile (x => updatePluginPath(x.slurp())) getOrElse "" - val allOpts = fileManager.SCALAC_OPTS.toList ::: argString.split(' ').toList.filter(_.length > 0) + + // slurp local flags (e.g., "A_1.flags") + val fstFile = SFile(files(0)) + def isInGroup(num: Int) = fstFile.stripExtension endsWith ("_" + num) + val inGroup = (1 to 9) flatMap (group => if (isInGroup(group)) List(group) else List()) + val localFlagsList = if (inGroup.nonEmpty) { + val localArgString = (fstFile.parent / (fstFile.stripExtension + ".flags")) ifFile (x => updatePluginPath(x.slurp())) getOrElse "" + localArgString.split(' ').toList.filter(_.length > 0) + } else List() + + val allOpts = fileManager.SCALAC_OPTS.toList ::: argString.split(' ').toList.filter(_.length > 0) ::: localFlagsList val args = allOpts.toList NestUI.verbose("scalac options: "+allOpts) diff --git a/src/reflect/scala/reflect/api/FlagSets.scala b/src/reflect/scala/reflect/api/FlagSets.scala index 6d105c9d20..36836e84a9 100644 --- a/src/reflect/scala/reflect/api/FlagSets.scala +++ b/src/reflect/scala/reflect/api/FlagSets.scala @@ -9,7 +9,6 @@ trait FlagSets { self: Universe => trait FlagOps extends Any { def | (right: FlagSet): FlagSet - def hasFlag(flags: FlagSet): Boolean } implicit def addFlagOps(left: FlagSet): FlagOps @@ -18,84 +17,86 @@ trait FlagSets { self: Universe => type FlagValues >: Null <: FlagValuesApi + // Q: I have a pretty flag. Can I put it here? + // A: Only if there's a tree that cannot be built without it. + // If you want to put a flag here so that it can be tested against, + // introduce an `isXXX` method in one of the `api.Symbols` classes instead. + trait FlagValuesApi { - /** Flag indicating that symbol or tree represents a trait */ + /** Flag indicating that tree represents a trait */ val TRAIT: FlagSet - /** Flag indicating that symbol or tree represents a module or its internal module class */ - val MODULE: FlagSet + /** Flag indicating that a tree is an interface (i.e. a trait which defines only abstract methods) */ + val INTERFACE: FlagSet - /** Flag indicating that symbol or tree represents a mutable variable */ + /** Flag indicating that tree represents a mutable variable */ val MUTABLE: FlagSet - /** Flag indicating that symbol or tree represents a package or its internal package class */ - val PACKAGE: FlagSet - - /** Flag indicating that symbol or tree represents a method */ - val METHOD: FlagSet - - /** Flag indicating that symbol or tree represents a macro definition. */ + /** Flag indicating that tree represents a macro definition. */ val MACRO: FlagSet - /** Flag indicating that symbol or tree represents an abstract type, method, or value */ + /** Flag indicating that tree represents an abstract type, method, or value */ val DEFERRED: FlagSet - /** Flag indicating that symbol or tree represents an abstract class */ + /** Flag indicating that tree represents an abstract class */ val ABSTRACT: FlagSet - /** Flag indicating that symbol or tree has `final` modifier set */ + /** Flag indicating that tree has `final` modifier set */ val FINAL: FlagSet - /** Flag indicating that symbol or tree has `sealed` modifier set */ + /** Flag indicating that tree has `sealed` modifier set */ val SEALED: FlagSet - /** Flag indicating that symbol or tree has `implicit` modifier set */ + /** Flag indicating that tree has `implicit` modifier set */ val IMPLICIT: FlagSet - /** Flag indicating that symbol or tree has `lazy` modifier set */ + /** Flag indicating that tree has `lazy` modifier set */ val LAZY: FlagSet - /** Flag indicating that symbol or tree has `override` modifier set */ + /** Flag indicating that tree has `override` modifier set */ val OVERRIDE: FlagSet - /** Flag indicating that symbol or tree has `private` modifier set */ + /** Flag indicating that tree has `private` modifier set */ val PRIVATE: FlagSet - /** Flag indicating that symbol or tree has `protected` modifier set */ + /** Flag indicating that tree has `protected` modifier set */ val PROTECTED: FlagSet - /** Flag indicating that symbol or tree has `case` modifier set */ + /** Flag indicating that tree represents a member local to current class + * (i.e. private[this] or protected[this]. + * This requires having either PRIVATE or PROTECTED set as well. + */ + val LOCAL: FlagSet + + /** Flag indicating that tree has `case` modifier set */ val CASE: FlagSet - /** Flag indicating that symbol or tree has `abstract` and `override` modifiers set */ + /** Flag indicating that tree has `abstract` and `override` modifiers set */ val ABSOVERRIDE: FlagSet - /** Flag indicating that symbol or tree represents a by-name parameter */ + /** Flag indicating that tree represents a by-name parameter */ val BYNAMEPARAM: FlagSet - /** Flag indicating that symbol or tree represents a class or parameter. + /** Flag indicating that tree represents a class or parameter. * Both type and value parameters carry the flag. */ val PARAM: FlagSet - /** Flag indicating that symbol or tree represents a field of a class - * that was generated from a parameter of that class */ - val PARAMACCESSOR: FlagSet - - /** Flag indicating that symbol or tree represents a field of a case class - * that corresponds to a parameter in the first parameter list of the - * primary constructor of that class */ - val CASEACCESSOR: FlagSet - - /** Flag indicating that symbol or tree represents a contravariant + /** Flag indicating that tree represents a covariant * type parameter (marked with `+`). */ val COVARIANT: FlagSet - /** Flag indicating that symbol or tree represents a contravariant + /** Flag indicating that tree represents a contravariant * type parameter (marked with `-`). */ val CONTRAVARIANT: FlagSet - /** Flag indicating that parameter has a default value */ + /** Flag indicating that tree represents a parameter that has a default value */ val DEFAULTPARAM: FlagSet + + /** Flag indicating that tree represents an early definition */ + val PRESUPER: FlagSet + + /** Flag indicating that tree represents a variable or a member initialized to the default value */ + val DEFAULTINIT: FlagSet } } diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 448382973a..fda76c7b95 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -13,7 +13,7 @@ trait Symbols extends base.Symbols { self: Universe => override type FreeTypeSymbol >: Null <: TypeSymbol with FreeTypeSymbolApi /** The API of symbols */ - trait SymbolApi extends SymbolBase with HasFlagsBase { this: Symbol => + trait SymbolApi extends SymbolBase { this: Symbol => /** The position of this symbol */ @@ -110,10 +110,10 @@ trait Symbols extends base.Symbols { self: Universe => * * The java access levels translate as follows: * - * java private: hasFlag(PRIVATE) && (privateWithin == NoSymbol) - * java package: !hasFlag(PRIVATE | PROTECTED) && (privateWithin == enclosingPackage) - * java protected: hasFlag(PROTECTED) && (privateWithin == enclosingPackage) - * java public: !hasFlag(PRIVATE | PROTECTED) && (privateWithin == NoSymbol) + * java private: isPrivate && (privateWithin == NoSymbol) + * java package: !isPrivate && !isProtected && (privateWithin == enclosingPackage) + * java protected: isProtected && (privateWithin == enclosingPackage) + * java public: !isPrivate && !isProtected && (privateWithin == NoSymbol) */ def privateWithin: Symbol diff --git a/src/reflect/scala/reflect/internal/FlagSets.scala b/src/reflect/scala/reflect/internal/FlagSets.scala index 6e77741355..b03d01c944 100644 --- a/src/reflect/scala/reflect/internal/FlagSets.scala +++ b/src/reflect/scala/reflect/internal/FlagSets.scala @@ -13,7 +13,6 @@ trait FlagSets extends api.FlagSets { self: SymbolTable => private class FlagOpsImpl(left: Long) extends FlagOps { def | (right: Long): Long = left | right - def hasFlag(right: Long): Boolean = (left & right) != 0 } val NoFlags: FlagSet = 0L @@ -22,10 +21,8 @@ trait FlagSets extends api.FlagSets { self: SymbolTable => object Flag extends FlagValues { val TRAIT : FlagSet = Flags.TRAIT - val MODULE : FlagSet = Flags.MODULE + val INTERFACE : FlagSet = Flags.INTERFACE val MUTABLE : FlagSet = Flags.MUTABLE - val PACKAGE : FlagSet = Flags.PACKAGE - val METHOD : FlagSet = Flags.METHOD val MACRO : FlagSet = Flags.MACRO val DEFERRED : FlagSet = Flags.DEFERRED val ABSTRACT : FlagSet = Flags.ABSTRACT @@ -36,15 +33,15 @@ trait FlagSets extends api.FlagSets { self: SymbolTable => val OVERRIDE : FlagSet = Flags.OVERRIDE val PRIVATE : FlagSet = Flags.PRIVATE val PROTECTED : FlagSet = Flags.PROTECTED + val LOCAL : FlagSet = Flags.LOCAL val CASE : FlagSet = Flags.CASE val ABSOVERRIDE : FlagSet = Flags.ABSOVERRIDE val BYNAMEPARAM : FlagSet = Flags.BYNAMEPARAM val PARAM : FlagSet = Flags.PARAM - val PARAMACCESSOR : FlagSet = Flags.PARAMACCESSOR - val CASEACCESSOR : FlagSet = Flags.CASEACCESSOR val COVARIANT : FlagSet = Flags.COVARIANT val CONTRAVARIANT : FlagSet = Flags.CONTRAVARIANT val DEFAULTPARAM : FlagSet = Flags.DEFAULTPARAM - val INTERFACE : FlagSet = Flags.INTERFACE + val PRESUPER : FlagSet = Flags.PRESUPER + val DEFAULTINIT : FlagSet = Flags.DEFAULTINIT } } diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index 2e2e63a4b4..bde7f7ac51 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -247,7 +247,7 @@ trait Mirrors extends api.Mirrors { // is very beneficial for a handful of bootstrap symbols to have // first class identities sealed trait WellKnownSymbol extends Symbol { - this initFlags TopLevelCreationFlags + this initFlags (TopLevelCreationFlags | STATIC) } // Features common to RootClass and RootPackage, the roots of all // type and term symbols respectively. @@ -276,7 +276,6 @@ trait Mirrors extends api.Mirrors { override def isRoot = true override def isEffectiveRoot = true - override def isStatic = true override def isNestedClass = false } // The empty package, which holds all top level types without given packages. diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index e3e2063b05..0c86e4fba0 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -670,7 +670,8 @@ trait Printers extends api.Printers { self: SymbolTable => if (flags == NoFlags) nme.NoFlags.toString else { val s_flags = new collection.mutable.ListBuffer[String] - for (i <- 0 to 63 if (flags hasFlag (1L << i))) + def hasFlag(left: Long, right: Long): Boolean = (left & right) != 0 + for (i <- 0 to 63 if hasFlag(flags, 1L << i)) s_flags += flagToString(1L << i).replace("<", "").replace(">", "").toUpperCase s_flags mkString " | " } diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index c21391e305..f761b856cb 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -43,7 +43,9 @@ abstract class SymbolTable extends macros.Universe lazy val treeBuild = gen def log(msg: => AnyRef): Unit - def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) + def warning(msg: String): Unit = Console.err.println(msg) + def globalError(msg: String): Unit = abort(msg) + def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) @deprecated("Give us a reason", "2.10.0") def abort(): Nothing = abort("unknown error") diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d811815bdd..e8a011d4f9 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -80,8 +80,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => def toType: Type = tpe def toTypeIn(site: Type): Type = site.memberType(this) def toTypeConstructor: Type = typeConstructor - def setFlags(flags: FlagSet): this.type = setInternalFlags(flags) - def setInternalFlags(flag: Long): this.type = { setFlag(flag); this } def setTypeSignature(tpe: Type): this.type = { setInfo(tpe); this } def getAnnotations: List[AnnotationInfo] = { initialize; annotations } def setAnnotations(annots: AnnotationInfo*): this.type = { setAnnotations(annots.toList); this } @@ -392,6 +390,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => case x: TermName => newErrorValue(x) } + /** Creates a placeholder symbol for when a name is encountered during + * unpickling for which there is no corresponding classfile. This defers + * failure to the point when that name is used for something, which is + * often to the point of never. + */ + def newStubSymbol(name: Name): Symbol = name match { + case n: TypeName => new StubClassSymbol(this, n) + case _ => new StubTermSymbol(this, name.toTermName) + } + @deprecated("Use the other signature", "2.10.0") def newClass(pos: Position, name: TypeName): Symbol = newClass(name, pos) @deprecated("Use the other signature", "2.10.0") @@ -884,7 +892,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ owner attribute -------------------------------------------------------------- def owner: Symbol = { - Statistics.incCounter(ownerCount) + if (Statistics.hotEnabled) Statistics.incCounter(ownerCount) rawowner } @@ -2285,7 +2293,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[this] var _rawname: TermName = initName def rawname = _rawname def name = { - Statistics.incCounter(nameCount) + if (Statistics.hotEnabled) Statistics.incCounter(nameCount) _rawname } def name_=(name: Name) { @@ -2427,12 +2435,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => flatOwnerInfo.decl(name.toTypeName).suchThat(sym => sym.isClass && (sym isCoDefinedWith this)) override def owner = { - Statistics.incCounter(ownerCount) + if (Statistics.hotEnabled) Statistics.incCounter(ownerCount) if (!isMethod && needsFlatClasses) rawowner.owner else rawowner } override def name: TermName = { - Statistics.incCounter(nameCount) + if (Statistics.hotEnabled) Statistics.incCounter(nameCount) if (!isMethod && needsFlatClasses) { if (flatname eq null) flatname = nme.flattenedName(rawowner.name, rawname) @@ -2526,7 +2534,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def rawname = _rawname def name = { - Statistics.incCounter(nameCount) + if (Statistics.hotEnabled) Statistics.incCounter(nameCount) _rawname } final def asNameType(n: Name) = n.toTypeName @@ -2655,7 +2663,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * info for T in Test1 should be >: Nothing <: Test3[_] */ - Statistics.incCounter(typeSymbolCount) + if (Statistics.hotEnabled) Statistics.incCounter(typeSymbolCount) } implicit val TypeSymbolTag = ClassTag[TypeSymbol](classOf[TypeSymbol]) @@ -2834,7 +2842,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } override def owner: Symbol = { - Statistics.incCounter(ownerCount) + if (Statistics.hotEnabled) Statistics.incCounter(ownerCount) if (needsFlatClasses) rawowner.owner else rawowner } @@ -2878,7 +2886,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def children = childSet override def addChild(sym: Symbol) { childSet = childSet + sym } - Statistics.incCounter(classSymbolCount) + if (Statistics.hotEnabled) Statistics.incCounter(classSymbolCount) } implicit val ClassSymbolTag = ClassTag[ClassSymbol](classOf[ClassSymbol]) @@ -2972,6 +2980,37 @@ trait Symbols extends api.Symbols { self: SymbolTable => || info.parents.exists(_.typeSymbol hasTransOwner sym) ) } + trait StubSymbol extends Symbol { + protected def stubWarning = { + val from = if (associatedFile == null) "" else s" - referenced from ${associatedFile.canonicalPath}" + s"$kindString $nameString$locationString$from (a classfile may be missing)" + } + private def fail[T](alt: T): T = { + // Avoid issuing lots of redundant errors + if (!hasFlag(IS_ERROR)) { + globalError(s"bad symbolic reference to " + stubWarning) + if (settings.debug.value) + (new Throwable).printStackTrace + + this setFlag IS_ERROR + } + alt + } + // This one doesn't call fail because SpecializeTypes winds up causing + // isMonomorphicType to be called, which calls this, which would fail us + // in all the scenarios we're trying to keep from failing. + override def originalInfo = NoType + override def associatedFile = owner.associatedFile + override def info = fail(NoType) + override def rawInfo = fail(NoType) + override def companionSymbol = fail(NoSymbol) + + locally { + debugwarn("creating stub symbol for " + stubWarning) + } + } + class StubClassSymbol(owner0: Symbol, name0: TypeName) extends ClassSymbol(owner0, owner0.pos, name0) with StubSymbol + class StubTermSymbol(owner0: Symbol, name0: TermName) extends TermSymbol(owner0, owner0.pos, name0) with StubSymbol trait FreeSymbol extends Symbol { def origin: String diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index e92cfba1c5..51dc2567f8 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -236,7 +236,7 @@ abstract class TreeInfo { case _ => tree } - + /** Is tree a self or super constructor call? */ def isSelfOrSuperConstrCall(tree: Tree) = { // stripNamedApply for SI-3584: adaptToImplicitMethod in Typers creates a special context diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 220869e4d2..94d51b7455 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -160,7 +160,7 @@ trait Trees extends api.Trees { self: SymbolTable => new ThisSubstituter(clazz, to) transform this def hasSymbolWhich(f: Symbol => Boolean) = - hasSymbol && symbol != null && f(symbol) + (symbol ne null) && (symbol ne NoSymbol) && f(symbol) def isErroneous = (tpe ne null) && tpe.isErroneous def isTyped = (tpe ne null) && !tpe.isErroneous diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 7df34a14e2..5709ddf128 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2202,7 +2202,15 @@ trait Types extends api.Types { self: SymbolTable => override protected def normalizeImpl = if (typeParamsMatchArgs) betaReduce.normalize else if (isHigherKinded) super.normalizeImpl - else ErrorType + else { + // if we are overriding a type alias in an erroneous way, don't just + // return an ErrorType since that will result in useless error msg. + // Instead let's try to recover from it and rely on refcheck reporting the correct error, + // if that fails fallback to the old behaviour. + val overriddenSym = sym.nextOverriddenSymbol + if (overriddenSym != NoSymbol) pre.memberType(overriddenSym).normalize + else ErrorType + } // isHKSubType0 introduces synthetic type params so that // betaReduce can first apply sym.info to typeArgs before calling diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index cbe9c1d729..3d95cb9c1d 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -230,9 +230,11 @@ abstract class UnPickler /*extends reflect.generic.UnPickler*/ { fromName(nme.expandedName(name.toTermName, owner)) orElse { // (3) Try as a nested object symbol. nestedObjectSymbol orElse { - // (4) Otherwise, fail. - //System.err.println("missing "+name+" in "+owner+"/"+owner.id+" "+owner.info.decls) - adjust(errorMissingRequirement(name, owner)) + // (4) Call the mirror's "missing" hook. + adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse { + // (5) Create a stub symbol to defer hard failure a little longer. + owner.newStubSymbol(name) + } } } } diff --git a/src/reflect/scala/reflect/macros/Universe.scala b/src/reflect/scala/reflect/macros/Universe.scala index 4074dd9e93..8d9711dedd 100644 --- a/src/reflect/scala/reflect/macros/Universe.scala +++ b/src/reflect/scala/reflect/macros/Universe.scala @@ -22,17 +22,15 @@ abstract class Universe extends scala.reflect.api.Universe { /** The extended API of symbols that's supported in macro context universes */ - trait SymbolContextApi extends SymbolApi with AttachableApi { this: Symbol => + trait SymbolContextApi extends SymbolApi with AttachableApi { self: Symbol => - def setFlags(flags: FlagSet): this.type + def setTypeSignature(tpe: Type): Symbol - def setTypeSignature(tpe: Type): this.type + def setAnnotations(annots: AnnotationInfo*): Symbol - def setAnnotations(annots: AnnotationInfo*): this.type + def setName(name: Name): Symbol - def setName(name: Name): this.type - - def setPrivateWithin(sym: Symbol): this.type + def setPrivateWithin(sym: Symbol): Symbol } // Tree extensions --------------------------------------------------------------- @@ -41,20 +39,20 @@ abstract class Universe extends scala.reflect.api.Universe { /** The extended API of trees that's supported in macro context universes */ - trait TreeContextApi extends TreeApi with AttachableApi { this: Tree => + trait TreeContextApi extends TreeApi with AttachableApi { self: Tree => /** ... */ def pos_=(pos: Position): Unit /** ... */ - def setPos(newpos: Position): this.type + def setPos(newpos: Position): Tree /** ... */ def tpe_=(t: Type): Unit /** Set tpe to give `tp` and return this. */ - def setType(tp: Type): this.type + def setType(tp: Type): Tree /** Like `setType`, but if this is a previously empty TypeTree that * fact is remembered so that resetAllAttrs will snap back. @@ -73,13 +71,13 @@ abstract class Universe extends scala.reflect.api.Universe { * and therefore should be abandoned if the current line of type * inquiry doesn't work out. */ - def defineType(tp: Type): this.type + def defineType(tp: Type): Tree /** ... */ def symbol_=(sym: Symbol): Unit /** ... */ - def setSymbol(sym: Symbol): this.type + def setSymbol(sym: Symbol): Tree } override type SymTree >: Null <: Tree with SymTreeContextApi diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 2bf57d61cc..4ce2cda04a 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -18,7 +18,7 @@ import collection.mutable.{ HashMap, ListBuffer } import internal.Flags._ //import scala.tools.nsc.util.ScalaClassLoader //import scala.tools.nsc.util.ScalaClassLoader._ -import ReflectionUtils.{singletonInstance} +import ReflectionUtils.{staticSingletonInstance, innerSingletonInstance} import language.existentials import scala.runtime.{ScalaRunTime, BoxesRunTime} import scala.reflect.internal.util.Collections._ @@ -403,8 +403,11 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym def erasure = symbol.moduleClass.asClass def isStatic = true def instance = { - if (!symbol.owner.isPackageClass) throw new Error("inner and nested modules are not supported yet") - singletonInstance(classLoader, symbol.fullName) + if (symbol.owner.isPackageClass) + staticSingletonInstance(classLoader, symbol.fullName) + else + if (outer == null) staticSingletonInstance(classToJava(symbol.moduleClass.asClass)) + else innerSingletonInstance(outer, symbol.name) } def companion: Option[ClassMirror] = symbol.companionClass match { case cls: ClassSymbol => Some(new JavaClassMirror(outer, cls)) @@ -1089,12 +1092,25 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym noClass else if (clazz.owner.isPackageClass) javaClass(clazz.javaClassName) - else if (clazz.owner.isClass) - classToJava(clazz.owner.asClass) - .getDeclaredClasses - .find(_.getSimpleName == clazz.name.toString) - .getOrElse(noClass) - else + else if (clazz.owner.isClass) { + val childOfClass = !clazz.owner.isModuleClass + val childOfTopLevel = clazz.owner.owner.isPackageClass + val childOfTopLevelObject = clazz.owner.isModuleClass && childOfTopLevel + + // suggested in https://issues.scala-lang.org/browse/SI-4023?focusedCommentId=54759#comment-54759 + var ownerClazz = classToJava(clazz.owner.asClass) + if (childOfTopLevelObject) ownerClazz = Class.forName(ownerClazz.getName stripSuffix "$", true, ownerClazz.getClassLoader) + val ownerChildren = ownerClazz.getDeclaredClasses + + var fullNameOfJavaClass = ownerClazz.getName + if (childOfClass || childOfTopLevel) fullNameOfJavaClass += "$" + fullNameOfJavaClass += clazz.name + if (clazz.isModuleClass) fullNameOfJavaClass += "$" + + // println(s"ownerChildren = ${ownerChildren.toList}") + // println(s"fullNameOfJavaClass = $fullNameOfJavaClass") + ownerChildren.find(_.getName == fullNameOfJavaClass).getOrElse(noClass) + } else noClass } diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala index 2c7be6e317..e87c6b339b 100644 --- a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala +++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala @@ -6,7 +6,7 @@ package scala.reflect.runtime import java.lang.{Class => jClass} -import java.lang.reflect.{ InvocationTargetException, UndeclaredThrowableException } +import java.lang.reflect.{ Method, InvocationTargetException, UndeclaredThrowableException } /** A few java-reflection oriented utility functions useful during reflection bootstrapping. */ @@ -63,25 +63,25 @@ object ReflectionUtils { } } - def singletonInstance(cl: ClassLoader, className: String): AnyRef = { + def staticSingletonInstance(cl: ClassLoader, className: String): AnyRef = { val name = if (className endsWith "$") className else className + "$" val clazz = java.lang.Class.forName(name, true, cl) - val singleton = clazz getField "MODULE$" get null - singleton + staticSingletonInstance(clazz) } - // Retrieves the MODULE$ field for the given class name. - def singletonInstanceOpt(cl: ClassLoader, className: String): Option[AnyRef] = - try Some(singletonInstance(cl, className)) - catch { case _: ClassNotFoundException => None } + def staticSingletonInstance(clazz: Class[_]): AnyRef = clazz getField "MODULE$" get null - def invokeFactory(cl: ClassLoader, className: String, methodName: String, args: AnyRef*): AnyRef = { - val singleton = singletonInstance(cl, className) - val method = singleton.getClass.getMethod(methodName, classOf[ClassLoader]) - method.invoke(singleton, args: _*) - } + def innerSingletonInstance(outer: AnyRef, className: String): AnyRef = { + val accessorName = if (className endsWith "$") className.substring(0, className.length - 1) else className + def singletonAccessor(clazz: Class[_]): Option[Method] = + if (clazz == null) None + else { + val declaredAccessor = clazz.getDeclaredMethods.filter(_.getName == accessorName).headOption + declaredAccessor orElse singletonAccessor(clazz.getSuperclass) + } - def invokeFactoryOpt(cl: ClassLoader, className: String, methodName: String, args: AnyRef*): Option[AnyRef] = - try Some(invokeFactory(cl, className, methodName, args: _*)) - catch { case _: ClassNotFoundException => None } + val accessor = singletonAccessor(outer.getClass) getOrElse { throw new NoSuchMethodException(s"${outer.getClass.getName}.$accessorName") } + accessor setAccessible true + accessor invoke outer + } } diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index 30e1a722bf..31bb8c4e44 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -6,6 +6,7 @@ import scala.concurrent.util.duration._ import scala.concurrent.util.Duration.Inf import scala.collection._ import scala.runtime.NonLocalReturnControl +import scala.util.{Try,Success,Failure} @@ -197,7 +198,7 @@ object FutureTests extends MinimalScalaTest { } andThen { case _ => q.add(2) } andThen { - case Right(0) => q.add(Int.MaxValue) + case Success(0) => q.add(Int.MaxValue) } andThen { case _ => q.add(3); } @@ -260,13 +261,13 @@ object FutureTests extends MinimalScalaTest { } val futures = (0 to 9) map { - idx => async(idx, idx * 200) + idx => async(idx, idx * 20) } val folded = Future.fold(futures)(0)(_ + _) Await.result(folded, timeout) mustBe (45) val futuresit = (0 to 9) map { - idx => async(idx, idx * 200) + idx => async(idx, idx * 20) } val foldedit = Future.fold(futures)(0)(_ + _) Await.result(foldedit, timeout) mustBe (45) @@ -279,7 +280,7 @@ object FutureTests extends MinimalScalaTest { add } def futures = (0 to 9) map { - idx => async(idx, idx * 200) + idx => async(idx, idx * 20) } val folded = futures.foldLeft(Future(0)) { case (fr, fa) => for (r <- fr; a <- fa) yield (r + a) @@ -295,7 +296,7 @@ object FutureTests extends MinimalScalaTest { add } def futures = (0 to 9) map { - idx => async(idx, idx * 100) + idx => async(idx, idx * 10) } val folded = Future.fold(futures)(0)(_ + _) intercept[IllegalArgumentException] { @@ -326,7 +327,7 @@ object FutureTests extends MinimalScalaTest { "shouldReduceResults" in { def async(idx: Int) = future { - Thread.sleep(idx * 200) + Thread.sleep(idx * 20) idx } val timeout = 10000 millis @@ -348,7 +349,7 @@ object FutureTests extends MinimalScalaTest { } val timeout = 10000 millis def futures = (1 to 10) map { - idx => async(idx, idx * 100) + idx => async(idx, idx * 10) } val failed = Future.reduce(futures)(_ + _) intercept[IllegalArgumentException] { @@ -472,7 +473,7 @@ object FutureTests extends MinimalScalaTest { p1.future.isCompleted mustBe (false) f4.isCompleted mustBe (false) - p1 complete Right("Hello") + p1 complete Success("Hello") Await.ready(latch(7), TestLatch.DefaultTimeout) @@ -509,7 +510,7 @@ object FutureTests extends MinimalScalaTest { } "should not throw when Await.ready" in { - val expected = try Right(5 / 0) catch { case a: ArithmeticException => Left(a) } + val expected = try Success(5 / 0) catch { case a: ArithmeticException => Failure(a) } val f = future(5).map(_ / 0) Await.ready(f, defaultTimeout).value.get.toString mustBe expected.toString } diff --git a/test/files/jvm/future-spec/PromiseTests.scala b/test/files/jvm/future-spec/PromiseTests.scala index d15bb31f36..d9aaa1d5ed 100644 --- a/test/files/jvm/future-spec/PromiseTests.scala +++ b/test/files/jvm/future-spec/PromiseTests.scala @@ -6,7 +6,7 @@ import scala.concurrent.util.duration._ import scala.concurrent.util.Duration.Inf import scala.collection._ import scala.runtime.NonLocalReturnControl - +import scala.util.{Try,Success,Failure} object PromiseTests extends MinimalScalaTest { @@ -48,27 +48,27 @@ object PromiseTests extends MinimalScalaTest { "A successful Promise" should { val result = "test value" - val promise = Promise[String]().complete(Right(result)) + val promise = Promise[String]().complete(Success(result)) promise.isCompleted mustBe (true) futureWithResult(_(promise.future, result)) } "A failed Promise" should { val message = "Expected Exception" - val promise = Promise[String]().complete(Left(new RuntimeException(message))) + val promise = Promise[String]().complete(Failure(new RuntimeException(message))) promise.isCompleted mustBe (true) futureWithException[RuntimeException](_(promise.future, message)) } "An interrupted Promise" should { val message = "Boxed InterruptedException" - val future = Promise[String]().complete(Left(new InterruptedException(message))).future + val future = Promise[String]().complete(Failure(new InterruptedException(message))).future futureWithException[ExecutionException](_(future, message)) } "A NonLocalReturnControl failed Promise" should { val result = "test value" - val future = Promise[String]().complete(Left(new NonLocalReturnControl[String]("test", result))).future + val future = Promise[String]().complete(Failure(new NonLocalReturnControl[String]("test", result))).future futureWithResult(_(future, result)) } @@ -76,7 +76,7 @@ object PromiseTests extends MinimalScalaTest { "be completed" in { f((future, _) => future.isCompleted mustBe (true)) } - "contain a value" in { f((future, result) => future.value mustBe (Some(Right(result)))) } + "contain a value" in { f((future, result) => future.value mustBe (Some(Success(result)))) } "return when ready with 'Await.ready'" in { f((future, result) => Await.ready(future, defaultTimeout).isCompleted mustBe (true)) } @@ -159,7 +159,7 @@ object PromiseTests extends MinimalScalaTest { "contain a value" in { f((future, message) => { - future.value.get.left.get.getMessage mustBe (message) + future.value.get.failed.get.getMessage mustBe (message) }) } diff --git a/test/files/jvm/future-spec/TryTests.scala b/test/files/jvm/future-spec/TryTests.scala new file mode 100644 index 0000000000..82ca12276f --- /dev/null +++ b/test/files/jvm/future-spec/TryTests.scala @@ -0,0 +1,118 @@ +// This is a port of the com.twitter.util Try spec. +// -- +// It lives in the future-spec directory simply because it requires a specs-like +// DSL which has already been minimally implemented for the future spec tests. + +import scala.util.{Try,Success,Failure} + +object TryTests extends MinimalScalaTest { + class MyException extends Exception + val e = new Exception("this is an exception") + + "Try()" should { + "catch exceptions and lift into the Try type" in { + Try[Int](1) mustEqual Success(1) + Try[Int] { throw e } mustEqual Failure(e) + } + } + + "Try" should { + "recoverWith" in { + val myException = new MyException + Success(1) recoverWith { case _ => Success(2) } mustEqual Success(1) + Failure(e) recoverWith { case _ => Success(2) } mustEqual Success(2) + Failure(e) recoverWith { case _ => Failure(e) } mustEqual Failure(e) + } + + "getOrElse" in { + Success(1) getOrElse 2 mustEqual 1 + Failure(e) getOrElse 2 mustEqual 2 + } + + "orElse" in { + Success(1) orElse Success(2) mustEqual Success(1) + Failure(e) orElse Success(2) mustEqual Success(2) + } + + "map" in { + "when there is no exception" in { + Success(1) map(1+) mustEqual Success(2) + Failure[Int](e) map(1+) mustEqual Failure(e) + } + + "when there is an exception" in { + Success(1) map(_ => throw e) mustEqual Failure(e) + + val e2 = new Exception + Failure[Int](e) map(_ => throw e2) mustEqual Failure(e) + } + } + + "flatMap" in { + "when there is no exception" in { + Success(1) flatMap(x => Success(1 + x)) mustEqual Success(2) + Failure[Int](e) flatMap(x => Success(1 + x)) mustEqual Failure(e) + } + + "when there is an exception" in { + Success(1).flatMap[Int](_ => throw e) mustEqual Failure(e) + + val e2 = new Exception + Failure[Int](e).flatMap[Int](_ => throw e2) mustEqual Failure(e) + } + } + + "flatten" in { + "is a Success(Success)" in { + Success(Success(1)).flatten mustEqual Success(1) + } + + "is a Success(Failure)" in { + val e = new Exception + Success(Failure(e)).flatten mustEqual Failure(e) + } + + "is a Throw" in { + val e = new Exception + Failure[Try[Int]](e).flatten mustEqual Failure(e) + } + } + + "for" in { + "with no Failure values" in { + val result = for { + i <- Success(1) + j <- Success(1) + } yield (i + j) + result mustEqual Success(2) + } + + "with Failure values" in { + "throws before" in { + val result = for { + i <- Failure[Int](e) + j <- Success(1) + } yield (i + j) + result mustEqual Failure(e) + } + + "throws after" in { + val result = for { + i <- Success(1) + j <- Failure[Int](e) + } yield (i + j) + result mustEqual Failure(e) + } + + "returns the FIRST Failure" in { + val e2 = new Exception + val result = for { + i <- Failure[Int](e) + j <- Failure[Int](e2) + } yield (i + j) + result mustEqual Failure(e) + } + } + } + } +}
\ No newline at end of file diff --git a/test/files/jvm/future-spec/main.scala b/test/files/jvm/future-spec/main.scala index 45b59d5d20..57183d8cea 100644 --- a/test/files/jvm/future-spec/main.scala +++ b/test/files/jvm/future-spec/main.scala @@ -12,6 +12,7 @@ object Test { def main(args: Array[String]) { FutureTests.check() PromiseTests.check() + TryTests.check() } } @@ -47,7 +48,7 @@ trait MinimalScalaTest extends Output { snippet bufferPrintln("[OK] Test passed.") } catch { - case e => + case e: Throwable => bufferPrintln("[FAILED] " + e) bufferPrintln(e.getStackTrace().mkString("\n")) throwables += e @@ -59,6 +60,7 @@ trait MinimalScalaTest extends Output { implicit def objectops(obj: Any) = new { def mustBe(other: Any) = assert(obj == other, obj + " is not " + other) + def mustEqual(other: Any) = mustBe(other) } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 43d4c9dc71..ffb5608fd2 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -601,10 +601,10 @@ trait FutureProjections extends TestBase { throw cause } f.failed onComplete { - case Right(t) => + case Success(t) => assert(t == cause) done() - case Left(t) => + case Failure(t) => assert(false) } } @@ -626,9 +626,9 @@ trait FutureProjections extends TestBase { done => val f = future { 0 } f.failed onComplete { - case Right(t) => + case Success(t) => assert(false) - case Left(t) => + case Failure(t) => assert(t.isInstanceOf[NoSuchElementException]) done() } @@ -803,81 +803,6 @@ trait Exceptions extends TestBase { } -// trait TryEitherExtractor extends TestBase { - -// import scala.util.{Try, Success, Failure} - -// def testSuccessMatch(): Unit = once { -// done => -// val thisIsASuccess = Success(42) -// thisIsASuccess match { -// case Success(v) => -// done() -// assert(v == 42) -// case Failure(e) => -// done() -// assert(false) -// case other => -// done() -// assert(false) -// } -// } - -// def testRightMatch(): Unit = once { -// done => -// val thisIsNotASuccess: Right[Throwable, Int] = Right(43) -// thisIsNotASuccess match { -// case Success(v) => -// done() -// assert(v == 43) -// case Failure(e) => -// done() -// assert(false) -// case other => -// done() -// assert(false) -// } -// } - -// def testFailureMatch(): Unit = once { -// done => -// val thisIsAFailure = Failure(new Exception("I'm an exception")) -// thisIsAFailure match { -// case Success(v) => -// done() -// assert(false) -// case Failure(e) => -// done() -// assert(e.getMessage == "I'm an exception") -// case other => -// done() -// assert(false) -// } -// } - -// def testLeftMatch(): Unit = once { -// done => -// val thisIsNotAFailure: Left[Throwable, Int] = Left(new Exception("I'm an exception")) -// thisIsNotAFailure match { -// case Success(v) => -// done() -// assert(false) -// case Failure(e) => -// done() -// assert(e.getMessage == "I'm an exception") -// case other => -// done() -// assert(false) -// } - -// } - -// testSuccessMatch() -// testRightMatch() -// testFailureMatch() -// testLeftMatch() -// } - trait CustomExecutionContext extends TestBase { import scala.concurrent.{ ExecutionContext, Awaitable } @@ -975,13 +900,13 @@ trait CustomExecutionContext extends TestBase { } flatMap { x => Promise.successful(x + 1).future.map(addOne).map(addOne) } onComplete { - case Left(t) => + case Failure(t) => try { throw new AssertionError("error in test: " + t.getMessage, t) } finally { done() } - case Right(x) => + case Success(x) => assertEC() assert(x == 14) done() @@ -1001,6 +926,66 @@ trait CustomExecutionContext extends TestBase { testCallbackChainCustomEC() } +trait ExecutionContextPrepare extends TestBase { + val theLocal = new ThreadLocal[String] { + override protected def initialValue(): String = "" + } + + class PreparingExecutionContext extends ExecutionContext { + def delegate = ExecutionContext.global + + override def execute(runnable: Runnable): Unit = + delegate.execute(runnable) + + override def prepare(): ExecutionContext = { + // save object stored in ThreadLocal storage + val localData = theLocal.get + new PreparingExecutionContext { + override def execute(runnable: Runnable): Unit = { + val wrapper = new Runnable { + override def run(): Unit = { + // now we're on the new thread + // put localData into theLocal + theLocal.set(localData) + runnable.run() + } + } + delegate.execute(wrapper) + } + } + } + + override def reportFailure(t: Throwable): Unit = + delegate.reportFailure(t) + } + + implicit val ec = new PreparingExecutionContext + + def testOnComplete(): Unit = once { + done => + theLocal.set("secret") + val fut = future { 42 } + fut onComplete { + case _ => + assert(theLocal.get == "secret") + done() + } + } + + def testMap(): Unit = once { + done => + theLocal.set("secret2") + val fut = future { 42 } + fut map { x => + assert(theLocal.get == "secret2") + done() + } + } + + testOnComplete() + testMap() +} + object Test extends App with FutureCallbacks @@ -1009,12 +994,9 @@ with FutureProjections with Promises with BlockContexts with Exceptions -// with TryEitherExtractor with CustomExecutionContext +with ExecutionContextPrepare { System.exit(0) } - - - diff --git a/test/files/jvm/try-type-tests.scala b/test/files/jvm/try-type-tests.scala index eecbb0ae57..17811f64c5 100644 --- a/test/files/jvm/try-type-tests.scala +++ b/test/files/jvm/try-type-tests.scala @@ -59,12 +59,12 @@ trait TryStandard { def testRescueSuccess(): Unit = { val t = Success(1) - t.rescue{ case x => assert(false); Try() } + t.recoverWith{ case x => assert(false); Try() } } def testRescueFailure(): Unit = { val t = Failure(new Exception("foo")) - val n = t.rescue{ case x => Try(1) } + val n = t.recoverWith{ case x => Try(1) } assert(n.get == 1) } @@ -103,6 +103,20 @@ trait TryStandard { } } + def testSuccessTransform(): Unit = { + val s = Success(1) + val succ = (x: Int) => Success(x * 10) + val fail = (x: Throwable) => Success(0) + assert(s.transform(succ, fail).get == 10) + } + + def testFailureTransform(): Unit = { + val f = Failure(new Exception("foo")) + val succ = (x: Int) => Success(x * 10) + val fail = (x: Throwable) => Success(0) + assert(f.transform(succ, fail).get == 0) + } + testForeachSuccess() testForeachFailure() testFlatMapSuccess() @@ -119,132 +133,12 @@ trait TryStandard { testFlattenSuccess() testFailedSuccess() testFailedFailure() -} - -// tests that implicit conversions from Try to Either behave as expected -trait TryImplicitConversionTry2Either { - - def testTry2RightMap(): Unit = { - val t = Success(1) - val n = t.right.map(x => x * 100) - assert(n == Right(100)) - } - - def testTry2LeftMap(): Unit = { - val e = new Exception("foo") - val t = Failure(e) - val n = t.left.map(x => x) - assert(n == Left(e)) - } - - def testTry2FoldSuccess(): Unit = { - val t = Success(1) - val n = t.fold(x => assert(false), y => y * 200) - assert(n == 200) - } - - def testTry2FoldFailure(): Unit = { - val e = new Exception("foo") - val t = Failure(e) - val n = t.fold(x => x, y => assert(false)) - assert(n == e) - } - - def testTry2SwapSuccess(): Unit = { - val t = Success(1) - val n = t.swap - assert(n == Left(1)) - } - - def testTry2SwapFailure(): Unit = { - val e = new Exception("foo") - val t = Failure(e) - val n = t.swap - assert(n == Right(e)) - } - - // def testTry2MergeSucccess(): Unit = { - // val t: Try[Int] = Success(1) - // val n = (t: Either[Any, Any]).t.merge // connecting two implicit conversions - // assert(n == 1) - // } - - // def testTry2MergeFailure(): Unit = { - // val e = new Exception("foo") - // val t = Failure(e) - // val n = (t: Either[Any, Any]).merge // connecting two implicit conversions - // assert(n == e) - // } - - testTry2RightMap() - testTry2LeftMap() - testTry2FoldSuccess() - testTry2FoldFailure() - testTry2SwapSuccess() - testTry2SwapFailure() - // testTry2MergeSucccess() - // testTry2MergeFailure() -} - -// tests that implicit conversions from Either to Try behave as expected -trait TryImplicitConversionEither2Try { - - def testRight2FilterSuccessTrue(): Unit = { - def expectsTry[U <% Try[Int]](rght: U): Try[Int] = { - val n = rght.filter(x => x > 0) // this should be converted to a Try - n - } - val r = Right(1) - val n = expectsTry(r) - assert(n == Success(1)) - } - - def testRight2FilterSuccessFalse(): Unit = { - def expectsTry[U <% Try[Int]](rght: U): Try[Int] = { - val n = rght.filter(x => x < 0) // this should be converted to a Try - n - } - val r = Right(1) - val n = expectsTry(r) - n match { - case Failure(e: NoSuchElementException) => assert(true) - case _ => assert(false) - } - } - - def testLeft2FilterFailure(): Unit = { - def expectsTry[U <% Try[Int]](rght: U): Try[Int] = { - val n = rght.filter(x => x > 0) // this should be converted to a Try - n - } - val r = Left(new Exception("foo")) - val n = expectsTry(r) - n match { - case Failure(e: Exception) => assert(true) - case _ => assert(false) - } - } - - def testRight2GetSuccess(): Unit = { - def expectsTry[U <% Try[Int]](rght: U): Int = { - val n = rght.get // this should be converted to a Try - n - } - val r = Right(1) - val n = expectsTry(r) - assert(n == 1) - } - - testRight2FilterSuccessTrue() - testRight2FilterSuccessFalse() - testLeft2FilterFailure() - testRight2GetSuccess() + testSuccessTransform() + testFailureTransform() } object Test extends App -with TryStandard -with TryImplicitConversionTry2Either -with TryImplicitConversionEither2Try { +with TryStandard { System.exit(0) }
\ No newline at end of file diff --git a/test/files/neg/catch-all.check b/test/files/neg/catch-all.check index d59e826f03..aaf51480c3 100644 --- a/test/files/neg/catch-all.check +++ b/test/files/neg/catch-all.check @@ -1,10 +1,10 @@ -catch-all.scala:2: warning: This catches all Throwables. If this is really intended, use `case _: Throwable` to clear this warning. +catch-all.scala:2: warning: This catches all Throwables. If this is really intended, use `case _ : Throwable` to clear this warning. try { "warn" } catch { case _ => } ^ -catch-all.scala:4: warning: This catches all Throwables. If this is really intended, use `case x: Throwable` to clear this warning. +catch-all.scala:4: warning: This catches all Throwables. If this is really intended, use `case x : Throwable` to clear this warning. try { "warn" } catch { case x => } ^ -catch-all.scala:6: warning: This catches all Throwables. If this is really intended, use `case x: Throwable` to clear this warning. +catch-all.scala:6: warning: This catches all Throwables. If this is really intended, use `case x : Throwable` to clear this warning. try { "warn" } catch { case _: RuntimeException => ; case x => } ^ error: No warnings can be incurred under -Xfatal-warnings. diff --git a/test/files/neg/macro-invalidsig-context-bounds.check b/test/files/neg/macro-invalidsig-context-bounds.check index 894eabc442..6c9482e537 100644 --- a/test/files/neg/macro-invalidsig-context-bounds.check +++ b/test/files/neg/macro-invalidsig-context-bounds.check @@ -1,4 +1,4 @@ -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
+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 diff --git a/test/files/neg/macro-invalidsig-implicit-params.check b/test/files/neg/macro-invalidsig-implicit-params.check index 029b8a4634..98b3167b7a 100644 --- a/test/files/neg/macro-invalidsig-implicit-params.check +++ b/test/files/neg/macro-invalidsig-implicit-params.check @@ -1,4 +1,4 @@ -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: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 diff --git a/test/files/neg/newpat_unreachable.check b/test/files/neg/newpat_unreachable.check new file mode 100644 index 0000000000..a928e3853a --- /dev/null +++ b/test/files/neg/newpat_unreachable.check @@ -0,0 +1,29 @@ +newpat_unreachable.scala:6: warning: patterns after a variable pattern cannot match (SLS 8.1.1) +If you intended to match against parameter b of method contrivedExample, you must use backticks, like: case `b` => + case b => println("matched b") + ^ +newpat_unreachable.scala:7: warning: unreachable code due to variable pattern 'b' on line 6 +If you intended to match against parameter c of method contrivedExample, you must use backticks, like: case `c` => + case c => println("matched c") + ^ +newpat_unreachable.scala:8: warning: unreachable code due to variable pattern 'b' on line 6 +If you intended to match against value d in class A, you must use backticks, like: case `d` => + case d => println("matched d") + ^ +newpat_unreachable.scala:9: warning: unreachable code due to variable pattern 'b' on line 6 + case _ => println("matched neither") + ^ +newpat_unreachable.scala:22: warning: patterns after a variable pattern cannot match (SLS 8.1.1) +If you intended to match against parameter b of method g, you must use backticks, like: case `b` => + case b => 1 + ^ +newpat_unreachable.scala:23: warning: unreachable code due to variable pattern 'b' on line 22 +If you intended to match against parameter c of method h, you must use backticks, like: case `c` => + case c => 2 + ^ +newpat_unreachable.scala:24: warning: unreachable code due to variable pattern 'b' on line 22 + case _ => 3 + ^ +error: No warnings can be incurred under -Xfatal-warnings. +7 warnings found +one error found diff --git a/test/files/neg/newpat_unreachable.flags b/test/files/neg/newpat_unreachable.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/newpat_unreachable.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/newpat_unreachable.scala b/test/files/neg/newpat_unreachable.scala new file mode 100644 index 0000000000..c9cc85cec6 --- /dev/null +++ b/test/files/neg/newpat_unreachable.scala @@ -0,0 +1,29 @@ +object Test { + class A { + val d = 55 + + def contrivedExample[A, B, C](a: A, b: B, c: C): Unit = a match { + case b => println("matched b") + case c => println("matched c") + case d => println("matched d") + case _ => println("matched neither") + } + + def correctExample[A, B, C](a: A, b: B, c: C): Unit = a match { + case `b` => println("matched b") + case `c` => println("matched c") + case `d` => println("matched d") + case _ => println("matched neither") + } + + def f[A](a: A) = { + def g[B](b: B) = { + def h[C](c: C) = a match { + case b => 1 + case c => 2 + case _ => 3 + } + } + } + } +} diff --git a/test/files/neg/nonlocal-warning.check b/test/files/neg/nonlocal-warning.check index efb3efaaa2..5202df655a 100644 --- a/test/files/neg/nonlocal-warning.check +++ b/test/files/neg/nonlocal-warning.check @@ -1,4 +1,4 @@ -nonlocal-warning.scala:4: warning: This catches all Throwables. If this is really intended, use `case x: Throwable` to clear this warning. +nonlocal-warning.scala:4: warning: This catches all Throwables. If this is really intended, use `case x : Throwable` to clear this warning. catch { case x => 11 } ^ nonlocal-warning.scala:2: warning: catch block may intercept non-local return from method foo diff --git a/test/files/neg/pat_unreachable.check b/test/files/neg/pat_unreachable.check index 4e1463d591..c5706b7fad 100644 --- a/test/files/neg/pat_unreachable.check +++ b/test/files/neg/pat_unreachable.check @@ -4,4 +4,10 @@ pat_unreachable.scala:5: error: unreachable code pat_unreachable.scala:9: error: unreachable code case Seq(x, y) => List(x, y) ^ -two errors found +pat_unreachable.scala:23: error: unreachable code + case c => println("matched c") + ^ +pat_unreachable.scala:24: error: unreachable code + case _ => println("matched neither") + ^ +four errors found diff --git a/test/files/neg/pat_unreachable.scala b/test/files/neg/pat_unreachable.scala index fc0fd41920..1f402e5212 100644 --- a/test/files/neg/pat_unreachable.scala +++ b/test/files/neg/pat_unreachable.scala @@ -8,7 +8,7 @@ object Test extends App { case Seq(x, y, _*) => x::y::Nil case Seq(x, y) => List(x, y) } - + def not_unreachable(xs:Seq[Char]) = xs match { case Seq(x, y, _*) => x::y::Nil case Seq(x) => List(x) @@ -17,4 +17,10 @@ object Test extends App { case Seq(x, y) => x::y::Nil case Seq(x, y, z, _*) => List(x,y) } + + def contrivedExample[A, B, C](a: A, b: B, c: C): Unit = a match { + case b => println("matched b") + case c => println("matched c") + case _ => println("matched neither") + } } diff --git a/test/files/neg/t4425.check b/test/files/neg/t4425.check index a6a1a1fad4..0f2fe6f2d1 100644 --- a/test/files/neg/t4425.check +++ b/test/files/neg/t4425.check @@ -1,5 +1,4 @@ -t4425.scala:3: error: error during expansion of this match (this is a scalac bug). -The underlying error was: value _1 is not a member of object Foo.X +t4425.scala:3: error: isInstanceOf cannot test if value types are references. 42 match { case _ X _ => () } - ^ + ^ one error found diff --git a/test/files/neg/t4425.flags b/test/files/neg/t4425.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/neg/t4425.flags @@ -0,0 +1 @@ +-optimize
\ No newline at end of file diff --git a/test/files/neg/t5148.check b/test/files/neg/t5148.check index 96eb1fd364..6edfdf2b1e 100644 --- a/test/files/neg/t5148.check +++ b/test/files/neg/t5148.check @@ -1,2 +1,3 @@ -error: bad reference while unpickling Imports.class: term memberHandlers not found in scala.tools.nsc.interpreter.IMain -one error found +error: bad symbolic reference to value global in class IMain - referenced from t5148.scala (a classfile may be missing) +error: bad symbolic reference to value memberHandlers in class IMain - referenced from t5148.scala (a classfile may be missing) +two errors found diff --git a/test/files/neg/t5687.check b/test/files/neg/t5687.check new file mode 100644 index 0000000000..5096077ee5 --- /dev/null +++ b/test/files/neg/t5687.check @@ -0,0 +1,8 @@ +t5687.scala:4: error: type arguments [T] do not conform to class Template's type parameter bounds [T <: AnyRef] + type Repr[T]<:Template[T] + ^ +t5687.scala:20: error: overriding type Repr in class Template with bounds[T] <: Template[T]; + type Repr has incompatible type + type Repr = CurveTemplate[T] + ^ +two errors found diff --git a/test/files/neg/t5687.scala b/test/files/neg/t5687.scala new file mode 100644 index 0000000000..90a9ae265c --- /dev/null +++ b/test/files/neg/t5687.scala @@ -0,0 +1,55 @@ +abstract class Template[T <: AnyRef](private val t: T) { + +// type Repr[A<:AnyRef]<:Template[T] + type Repr[T]<:Template[T] + + def access1(timeout: Int): Repr[T] = this.asInstanceOf[Repr[T]] + def access2: Repr[T] = this.asInstanceOf[Repr[T]] + val access3: Repr[T] = this.asInstanceOf[Repr[T]] + def access4(v: Repr[T]): Repr[T] = this.asInstanceOf[Repr[T]] + def access5(x: X): Repr[T] = this.asInstanceOf[Repr[T]] + def access5(x: Y): Repr[T] = this.asInstanceOf[Repr[T]] + + def withReadModifiers(readModifiers:Int): Repr[T] = this.asInstanceOf[Repr[T]] +} + +class Curve + +class CurveTemplate [T <: Curve](t: T) extends Template(t) { +// type Repr[A<: AnyRef] = CurveTemplate[T] + type Repr = CurveTemplate[T] +} + +class Base +class X extends Base +class Y extends Base + + +object Example { + def test1() { + new CurveTemplate(new Curve).access1(10) + + new CurveTemplate(new Curve).access2 + + new CurveTemplate(new Curve).access3 + + new CurveTemplate(new Curve).access4(null) + + new CurveTemplate(new Curve).access5(new X) + + () + + } + + def test2() { + new CurveTemplate(new Curve).access1(10).withReadModifiers(1) + + new CurveTemplate(new Curve).access2.withReadModifiers(1) + + new CurveTemplate(new Curve).access3.withReadModifiers(1) + + new CurveTemplate(new Curve).access4(null).withReadModifiers(1) + + new CurveTemplate(new Curve).access5(new X).withReadModifiers(1) + } +} diff --git a/test/files/neg/t6040.check b/test/files/neg/t6040.check new file mode 100644 index 0000000000..f44a81c471 --- /dev/null +++ b/test/files/neg/t6040.check @@ -0,0 +1,7 @@ +error: extension of type scala.Dynamic needs to be enabled +by making the implicit value language.dynamics visible. +This can be achieved by adding the import clause 'import language.dynamics' +or by setting the compiler option -language:dynamics. +See the Scala docs for value scala.language.dynamics for a discussion +why the feature needs to be explicitly enabled. +one error found diff --git a/test/files/neg/t6040.scala b/test/files/neg/t6040.scala new file mode 100644 index 0000000000..b8f7dab7a4 --- /dev/null +++ b/test/files/neg/t6040.scala @@ -0,0 +1 @@ +class X extends Dynamic
\ No newline at end of file diff --git a/test/files/neg/t6048.check b/test/files/neg/t6048.check index 319e3fa620..5e11d24fde 100644 --- a/test/files/neg/t6048.check +++ b/test/files/neg/t6048.check @@ -4,9 +4,12 @@ t6048.scala:3: warning: unreachable code t6048.scala:8: warning: unreachable code case _ if false => x // unreachable ^ -t6048.scala:14: warning: unreachable code +t6048.scala:13: warning: patterns after a variable pattern cannot match (SLS 8.1.1) + case _ => x + ^ +t6048.scala:14: warning: unreachable code due to variable pattern on line 13 case 5 if true => x // unreachable ^ error: No warnings can be incurred under -Xfatal-warnings. -three warnings found +four warnings found one error found diff --git a/test/files/neg/t6214.check b/test/files/neg/t6214.check new file mode 100644 index 0000000000..6349a3e71c --- /dev/null +++ b/test/files/neg/t6214.check @@ -0,0 +1,4 @@ +t6214.scala:5: error: missing parameter type + m { s => case class Foo() } + ^ +one error found diff --git a/test/files/neg/t6214.scala b/test/files/neg/t6214.scala new file mode 100644 index 0000000000..734acda35e --- /dev/null +++ b/test/files/neg/t6214.scala @@ -0,0 +1,7 @@ +object Test { + def m(f: String => Unit) = 0 + def m(f: Int => Unit) = 0 + def foo { + m { s => case class Foo() } + } +} diff --git a/test/files/pos/inline-access-levels.flags b/test/files/pos/inline-access-levels.flags new file mode 100644 index 0000000000..882f40f050 --- /dev/null +++ b/test/files/pos/inline-access-levels.flags @@ -0,0 +1 @@ +-optimise -Xfatal-warnings -Yinline-warnings diff --git a/test/files/pos/inline-access-levels/A_1.scala b/test/files/pos/inline-access-levels/A_1.scala new file mode 100644 index 0000000000..479fe0fc71 --- /dev/null +++ b/test/files/pos/inline-access-levels/A_1.scala @@ -0,0 +1,10 @@ +package test + +object A { + + private var x: Int = 0 + + @inline def actOnX(f: Int => Int) = { + x = f(x) + } +} diff --git a/test/files/pos/inline-access-levels/Test_2.scala b/test/files/pos/inline-access-levels/Test_2.scala new file mode 100644 index 0000000000..12c9eb540f --- /dev/null +++ b/test/files/pos/inline-access-levels/Test_2.scala @@ -0,0 +1,11 @@ +package test + +object Test { + + def main(args: Array[String]) { + + A.actOnX(_ + 1) + + } + +} diff --git a/test/files/pos/t5756.scala b/test/files/pos/t5756.scala new file mode 100644 index 0000000000..45960fa8bd --- /dev/null +++ b/test/files/pos/t5756.scala @@ -0,0 +1,6 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + def tagme[T: TypeTag](x: T) = typeTag[T] + val foo = tagme{object Bar; Bar} +}
\ No newline at end of file diff --git a/test/files/pos/t6022b.scala b/test/files/pos/t6022b.scala new file mode 100644 index 0000000000..6ceb928162 --- /dev/null +++ b/test/files/pos/t6022b.scala @@ -0,0 +1,20 @@ +trait A +trait B +trait C +trait AB extends B with A + +// two types are mutually exclusive if there is no equality symbol whose constant implies both +object Test extends App { + def foo(x: Any) = x match { + case _ : C => println("C") + case _ : AB => println("AB") + case _ : (A with B) => println("AB'") + case _ : B => println("B") + case _ : A => println("A") + } + + foo(new A {}) + foo(new B {}) + foo(new AB{}) + foo(new C {}) +} diff --git a/test/files/pos/t6040.scala b/test/files/pos/t6040.scala new file mode 100644 index 0000000000..9c00ecd8e1 --- /dev/null +++ b/test/files/pos/t6040.scala @@ -0,0 +1,3 @@ +import language.dynamics + +class X extends Dynamic
\ No newline at end of file diff --git a/test/files/pos/t6145.scala b/test/files/pos/t6145.scala new file mode 100644 index 0000000000..28334d4420 --- /dev/null +++ b/test/files/pos/t6145.scala @@ -0,0 +1,11 @@ +object Test { + // the existential causes a cast and the cast makes searchClass not be in tail position + // can we get rid of the useless cast? + @annotation.tailrec + final def searchClass: Class[_] = { + "packageName" match { + case _ => + searchClass + } + } +}
\ No newline at end of file diff --git a/test/files/pos/t6184.scala b/test/files/pos/t6184.scala new file mode 100644 index 0000000000..83a1306aca --- /dev/null +++ b/test/files/pos/t6184.scala @@ -0,0 +1,7 @@ +trait Foo[TroubleSome] { + type T <: Foo[TroubleSome] + + this match { + case e: Foo[_]#T => ??? + } +}
\ No newline at end of file diff --git a/test/files/pos/t6201.scala b/test/files/pos/t6201.scala new file mode 100644 index 0000000000..366c1f26eb --- /dev/null +++ b/test/files/pos/t6201.scala @@ -0,0 +1,13 @@ +class Test { + class Foo1 { + def must(x: scala.xml.Elem) = () + } + + class Foo2 { + def must(x: Int) = () + } + implicit def toFoo1(s: scala.xml.Elem) = new Foo1() + implicit def toFoo2(s: scala.xml.Elem) = new Foo2() + + def is: Unit = { (<a>{"a"}</a>).must(<a>{"b"}</a>) } +}
\ No newline at end of file diff --git a/test/files/pos/t6204-a.scala b/test/files/pos/t6204-a.scala new file mode 100644 index 0000000000..bd8d5c437e --- /dev/null +++ b/test/files/pos/t6204-a.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ + +object Bish { + def m { + object Bash { + typeOf[Option[_]] + } + } +}
\ No newline at end of file diff --git a/test/files/pos/t6204-b.scala b/test/files/pos/t6204-b.scala new file mode 100644 index 0000000000..86094d1a19 --- /dev/null +++ b/test/files/pos/t6204-b.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ + +object Bosh { + def Besh { + new { + val t = typeOf[Option[_]] + val x = t + } + } +}
\ No newline at end of file diff --git a/test/files/pos/t6205.scala b/test/files/pos/t6205.scala new file mode 100644 index 0000000000..02d924fe85 --- /dev/null +++ b/test/files/pos/t6205.scala @@ -0,0 +1,18 @@ +// original code by reporter +class A[T] +class Test1 { + def x(backing: Map[A[_], Any]) = + for( (k: A[kt], v) <- backing) + yield (k: A[kt]) +} + +// this tests same thing as above, but independent of library classes, +// earlier expansions eliminated as well as variance (everything's invariant) +case class Holder[A](a: A) +class Mapped[A] { def map[T](f: Holder[A] => T): Iterable[T] = ??? } +class Test2 { + def works(backing: Mapped[A[_]]): Iterable[A[_]] + = backing.map(x => + x match {case Holder(k: A[kt]) => (k: A[kt])} + ) +}
\ No newline at end of file diff --git a/test/files/run/applydynamic_sip.flags b/test/files/run/applydynamic_sip.flags new file mode 100644 index 0000000000..1141f97507 --- /dev/null +++ b/test/files/run/applydynamic_sip.flags @@ -0,0 +1 @@ +-language:dynamics diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check index 25e1b2a4dd..a4c9f215e8 100644 --- a/test/files/run/inline-ex-handlers.check +++ b/test/files/run/inline-ex-handlers.check @@ -13,6 +13,10 @@ < 92 JUMP 2 < < 2: +383c382 +< locals: value args, variable result, value ex6, value x4, value x5, value message, value x +--- +> locals: value args, variable result, value ex6, value x4, value x5, value x 385c384 < blocks: [1,2,3,4,5,8,11,13,14,16] --- @@ -34,11 +38,21 @@ < 101 JUMP 4 < < 4: -515c520 +438,441d442 +< 106 LOAD_LOCAL(value x5) +< 106 CALL_METHOD MyException.message (dynamic) +< 106 STORE_LOCAL(value message) +< 106 SCOPE_ENTER value message +443c444,445 +< 106 LOAD_LOCAL(value message) +--- +> ? LOAD_LOCAL(value x5) +> ? CALL_METHOD MyException.message (dynamic) +515c517 < blocks: [1,2,3,4,6,7,8,9,10] --- > blocks: [1,2,3,4,6,7,8,9,10,11,12,13] -544c549,554 +544c546,551 < 306 THROW(MyException) --- > ? JUMP 11 @@ -47,7 +61,7 @@ > ? LOAD_LOCAL(variable monitor4) > 305 MONITOR_EXIT > ? JUMP 12 -550c560,566 +550c557,563 < ? THROW(Throwable) --- > ? JUMP 12 @@ -57,7 +71,7 @@ > 304 MONITOR_EXIT > ? STORE_LOCAL(value t) > ? JUMP 13 -556c572,585 +556c569,582 < ? THROW(Throwable) --- > ? STORE_LOCAL(value t) @@ -74,19 +88,19 @@ > 310 CALL_PRIMITIVE(EndConcat) > 310 CALL_METHOD scala.Predef.println (dynamic) > 310 JUMP 2 -580c609 +580c606 < catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6 --- > catch (Throwable) in ArrayBuffer(7, 8, 9, 10, 11) starting at: 6 -583c612 +583c609 < catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10) starting at: 3 --- > catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10, 11, 12) starting at: 3 -615c644 +615c641 < blocks: [1,2,3,4,5,6,7,9,10] --- > blocks: [1,2,3,4,5,6,7,9,10,11,12] -639c668,674 +639c665,671 < 78 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) @@ -96,7 +110,7 @@ > 81 LOAD_LOCAL(value e) > ? STORE_LOCAL(variable exc1) > ? JUMP 12 -668c703,717 +668c700,714 < 81 THROW(Exception) --- > ? STORE_LOCAL(variable exc1) @@ -114,15 +128,19 @@ > 84 STORE_LOCAL(variable result) > 84 LOAD_LOCAL(variable exc1) > 84 THROW(Throwable) -690c739 +690c736 < catch (<none>) in ArrayBuffer(4, 6, 7, 9) starting at: 3 --- > catch (<none>) in ArrayBuffer(4, 6, 7, 9, 11) starting at: 3 -716c765 +714c760 +< locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x +--- +> locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x +716c762 < blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31] --- > blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31,32,33,34] -740c789,796 +740c786,793 < 172 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -133,12 +151,27 @@ > 170 STORE_LOCAL(value x4) > 170 SCOPE_ENTER value x4 > 170 JUMP 18 -798c854,855 +787,790d839 +< 175 LOAD_LOCAL(value x5) +< 175 CALL_METHOD MyException.message (dynamic) +< 175 STORE_LOCAL(value message) +< 175 SCOPE_ENTER value message +792c841,842 +< 176 LOAD_LOCAL(value message) +--- +> ? LOAD_LOCAL(value x5) +> ? CALL_METHOD MyException.message (dynamic) +796c846,847 +< 177 LOAD_LOCAL(value message) +--- +> ? LOAD_LOCAL(value x5) +> ? CALL_METHOD MyException.message (dynamic) +798c849,850 < 177 THROW(MyException) --- > ? STORE_LOCAL(value ex6) > ? JUMP 33 -802c859,866 +802c854,861 < 170 THROW(Throwable) --- > ? STORE_LOCAL(value ex6) @@ -149,17 +182,32 @@ > 169 STORE_LOCAL(value x4) > 169 SCOPE_ENTER value x4 > 169 JUMP 5 -837c901,902 +826,829d884 +< 180 LOAD_LOCAL(value x5) +< 180 CALL_METHOD MyException.message (dynamic) +< 180 STORE_LOCAL(value message) +< 180 SCOPE_ENTER value message +831c886,887 +< 181 LOAD_LOCAL(value message) +--- +> ? LOAD_LOCAL(value x5) +> ? CALL_METHOD MyException.message (dynamic) +835c891,892 +< 182 LOAD_LOCAL(value message) +--- +> ? LOAD_LOCAL(value x5) +> ? CALL_METHOD MyException.message (dynamic) +837c894,895 < 182 THROW(MyException) --- > ? STORE_LOCAL(variable exc2) > ? JUMP 34 -841c906,907 +841c899,900 < 169 THROW(Throwable) --- > ? STORE_LOCAL(variable exc2) > ? JUMP 34 -842a909,921 +842a902,914 > 34: > 184 LOAD_MODULE object Predef > 184 CONSTANT("finally") @@ -173,19 +221,23 @@ > 185 LOAD_LOCAL(variable exc2) > 185 THROW(Throwable) > -863c942 +863c935 < catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30) starting at: 4 --- > catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30, 32) starting at: 4 -866c945 +866c938 < catch (<none>) in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30) starting at: 3 --- > catch (<none>) in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30, 32, 33) starting at: 3 -892c971 +890c962 +< locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x +--- +> locals: value args, variable result, value e, value ex6, value x4, value x5, value x +892c964 < blocks: [1,2,3,6,7,8,11,14,16,17,19] --- > blocks: [1,2,3,6,7,8,11,14,16,17,19,20] -916c995,1002 +916c988,995 < 124 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -196,15 +248,29 @@ > 122 STORE_LOCAL(value x4) > 122 SCOPE_ENTER value x4 > 122 JUMP 7 -979c1065 +945,948d1023 +< 127 LOAD_LOCAL(value x5) +< 127 CALL_METHOD MyException.message (dynamic) +< 127 STORE_LOCAL(value message) +< 127 SCOPE_ENTER value message +950c1025,1026 +< 127 LOAD_LOCAL(value message) +--- +> ? LOAD_LOCAL(value x5) +> ? CALL_METHOD MyException.message (dynamic) +979c1055 < catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19) starting at: 3 --- > catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19, 20) starting at: 3 -1005c1091 +1003c1079 +< locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e +--- +> locals: value args, variable result, value ex6, value x4, value x5, value x, value e +1005c1081 < blocks: [1,2,3,4,5,8,11,15,16,17,19] --- > blocks: [1,2,3,5,8,11,15,16,17,19,20] -1029c1115,1124 +1029c1105,1114 < 148 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -217,15 +283,25 @@ > 154 LOAD_LOCAL(value x4) > 154 IS_INSTANCE REF(class MyException) > 154 CZJUMP (BOOL)NE ? 5 : 11 -1050,1052d1144 +1050,1052d1134 < 145 JUMP 4 < < 4: -1288c1380 +1066,1069d1147 +< 154 LOAD_LOCAL(value x5) +< 154 CALL_METHOD MyException.message (dynamic) +< 154 STORE_LOCAL(value message) +< 154 SCOPE_ENTER value message +1071c1149,1150 +< 154 LOAD_LOCAL(value message) +--- +> ? LOAD_LOCAL(value x5) +> ? CALL_METHOD MyException.message (dynamic) +1288c1367 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1312c1404,1411 +1312c1391,1398 < 38 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) @@ -236,16 +312,20 @@ > 42 CONSTANT("IllegalArgumentException") > 42 CALL_METHOD scala.Predef.println (dynamic) > 42 JUMP 2 -1361c1460 +1359c1445 +< locals: value args, variable result, value ex6, value x4, value x5, value message, value x +--- +> locals: value args, variable result, value ex6, value x4, value x5, value x +1361c1447 < blocks: [1,2,3,4,5,8,11,13,14,16,17,19] --- > blocks: [1,2,3,5,8,11,13,14,16,17,19,20] -1385c1484,1485 +1385c1471,1472 < 203 THROW(MyException) --- > ? STORE_LOCAL(value ex6) > ? JUMP 20 -1405c1505,1514 +1405c1492,1501 < 209 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -258,15 +338,25 @@ > 212 LOAD_LOCAL(value x4) > 212 IS_INSTANCE REF(class MyException) > 212 CZJUMP (BOOL)NE ? 5 : 11 -1418,1420d1526 +1418,1420d1513 < 200 JUMP 4 < < 4: -1483c1589 +1434,1437d1526 +< 212 LOAD_LOCAL(value x5) +< 212 CALL_METHOD MyException.message (dynamic) +< 212 STORE_LOCAL(value message) +< 212 SCOPE_ENTER value message +1439c1528,1529 +< 213 LOAD_LOCAL(value message) +--- +> ? LOAD_LOCAL(value x5) +> ? CALL_METHOD MyException.message (dynamic) +1483c1573 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1507c1613,1620 +1507c1597,1604 < 58 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) @@ -277,11 +367,11 @@ > 62 CONSTANT("RuntimeException") > 62 CALL_METHOD scala.Predef.println (dynamic) > 62 JUMP 2 -1556c1669 +1556c1653 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1576c1689,1694 +1576c1673,1678 < 229 THROW(MyException) --- > ? JUMP 5 @@ -290,19 +380,19 @@ > ? LOAD_LOCAL(variable monitor1) > 228 MONITOR_EXIT > 228 THROW(Throwable) -1582c1700 +1582c1684 < ? THROW(Throwable) --- > 228 THROW(Throwable) -1610c1728 +1610c1712 < locals: value args, variable result, variable monitor2, variable monitorResult1 --- > locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 -1612c1730 +1612c1714 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1635c1753,1761 +1635c1737,1745 < 245 THROW(MyException) --- > ? STORE_LOCAL(value exception$1) @@ -314,7 +404,7 @@ > ? LOAD_LOCAL(variable monitor2) > 244 MONITOR_EXIT > 244 THROW(Throwable) -1641c1767 +1641c1751 < ? THROW(Throwable) --- > 244 THROW(Throwable) diff --git a/test/files/run/reflection-enclosed-basic.check b/test/files/run/reflection-enclosed-basic.check new file mode 100644 index 0000000000..41f6a72f1c --- /dev/null +++ b/test/files/run/reflection-enclosed-basic.check @@ -0,0 +1,18 @@ +class B1
+B1
+1
+class B2
+B2
+2
+object B3
+B3
+3
+object B4
+B4
+4
+object B5
+B5
+5
+object B6
+B6
+6
diff --git a/test/files/run/reflection-enclosed-basic.scala b/test/files/run/reflection-enclosed-basic.scala new file mode 100644 index 0000000000..1dcb6c2a27 --- /dev/null +++ b/test/files/run/reflection-enclosed-basic.scala @@ -0,0 +1,46 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.reflect.{classTag, ClassTag} + +class B1 { override def toString = "B1"; def foo = 1 } +private class B2 { override def toString = "B2"; def foo = 2 } +object B3 { override def toString = "B3"; def foo = 3 } +private object B4 { override def toString = "B4"; def foo = 4 } +object B5 extends B1 { override def toString = "B5"; override def foo = 5 } +private object B6 extends B2 { override def toString = "B6"; override def foo = 6 } + +object Test extends App { + def testMethodInvocation(instance: Any) = { + val instanceMirror = cm.reflect(instance) + val method = instanceMirror.symbol.typeSignature.declaration(newTermName("foo")).asMethod + val methodMirror = instanceMirror.reflectMethod(method) + println(methodMirror()) + } + + def testNestedClass(name: String) = { + val sym = cm.staticClass(name) + println(sym) + val ctor = sym.typeSignature.declaration(newTermName("<init>")).asMethod + val ctorMirror = cm.reflectClass(sym).reflectConstructor(ctor) + val instance = ctorMirror() + println(instance) + testMethodInvocation(instance) + } + + testNestedClass("B1") + testNestedClass("B2") + + def testNestedModule(name: String) = { + val sym = cm.staticModule(name) + println(sym) + val moduleMirror = cm.reflectModule(sym) + val instance = moduleMirror.instance + println(instance) + testMethodInvocation(instance) + } + + testNestedModule("B3") + testNestedModule("B4") + testNestedModule("B5") + testNestedModule("B6") +} diff --git a/test/files/run/reflection-enclosed-inner-basic.check b/test/files/run/reflection-enclosed-inner-basic.check new file mode 100644 index 0000000000..984fb1ff12 --- /dev/null +++ b/test/files/run/reflection-enclosed-inner-basic.check @@ -0,0 +1,20 @@ +class B
+List(constructor B, class B1, class B2, object B3, object B4, object B5, object B6)
+class B1
+B1
+1
+class B2
+B2
+2
+object B3
+B3
+3
+object B4
+B4
+4
+object B5
+B5
+5
+object B6
+B6
+6
diff --git a/test/files/run/reflection-enclosed-inner-basic.scala b/test/files/run/reflection-enclosed-inner-basic.scala new file mode 100644 index 0000000000..2b2c701993 --- /dev/null +++ b/test/files/run/reflection-enclosed-inner-basic.scala @@ -0,0 +1,52 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.reflect.{classTag, ClassTag} + +class B { + class B1 { override def toString = "B1"; def foo = 1 } + private class B2 { override def toString = "B2"; def foo = 2 } + object B3 { override def toString = "B3"; def foo = 3 } + private object B4 { override def toString = "B4"; def foo = 4 } + object B5 extends B1 { override def toString = "B5"; override def foo = 5 } + private object B6 extends B2 { override def toString = "B6"; override def foo = 6 } +} + +object Test extends App { + val b = cm.classSymbol(classTag[B].runtimeClass) + println(b) + println(b.typeSignature.declarations.toList) + + def testMethodInvocation(instance: Any) = { + val instanceMirror = cm.reflect(instance) + val method = instanceMirror.symbol.typeSignature.declaration(newTermName("foo")).asMethod + val methodMirror = instanceMirror.reflectMethod(method) + println(methodMirror()) + } + + def testInnerClass(name: String) = { + val sym = b.typeSignature.declaration(newTypeName(name)).asClass + println(sym) + val ctor = sym.typeSignature.declaration(newTermName("<init>")).asMethod + val ctorMirror = cm.reflect(new B).reflectClass(sym).reflectConstructor(ctor) + val instance = ctorMirror() + println(instance) + testMethodInvocation(instance) + } + + testInnerClass("B1") + testInnerClass("B2") + + def testInnerModule(name: String) = { + val sym = b.typeSignature.declaration(newTermName(name)).asModule + println(sym) + val moduleMirror = cm.reflect(new B).reflectModule(sym) + val instance = moduleMirror.instance + println(instance) + testMethodInvocation(instance) + } + + testInnerModule("B3") + testInnerModule("B4") + testInnerModule("B5") + testInnerModule("B6") +} diff --git a/test/files/run/reflection-enclosed-inner-inner-basic.check b/test/files/run/reflection-enclosed-inner-inner-basic.check new file mode 100644 index 0000000000..8987f31b18 --- /dev/null +++ b/test/files/run/reflection-enclosed-inner-inner-basic.check @@ -0,0 +1,20 @@ +class BB
+List(constructor BB, class B1, class B2, object B3, object B4, object B5, object B6)
+class B1
+B1
+1
+class B2
+B2
+2
+object B3
+B3
+3
+object B4
+B4
+4
+object B5
+B5
+5
+object B6
+B6
+6
diff --git a/test/files/run/reflection-enclosed-inner-inner-basic.scala b/test/files/run/reflection-enclosed-inner-inner-basic.scala new file mode 100644 index 0000000000..1b9e19d37d --- /dev/null +++ b/test/files/run/reflection-enclosed-inner-inner-basic.scala @@ -0,0 +1,58 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.reflect.{classTag, ClassTag} + +class B { + class BB { + class B1 { override def toString = "B1"; def foo = 1 } + private class B2 { override def toString = "B2"; def foo = 2 } + object B3 { override def toString = "B3"; def foo = 3 } + private object B4 { override def toString = "B4"; def foo = 4 } + object B5 extends B1 { override def toString = "B5"; override def foo = 5 } + private object B6 extends B2 { override def toString = "B6"; override def foo = 6 } + } +} + +object Test extends App { + val b = cm.classSymbol(classTag[B#BB].runtimeClass) + println(b) + println(b.typeSignature.declarations.toList) + + def testMethodInvocation(instance: Any) = { + val instanceMirror = cm.reflect(instance) + val method = instanceMirror.symbol.typeSignature.declaration(newTermName("foo")).asMethod + val methodMirror = instanceMirror.reflectMethod(method) + println(methodMirror()) + } + + def testInnerClass(name: String) = { + val sym = b.typeSignature.declaration(newTypeName(name)).asClass + println(sym) + val ctor = sym.typeSignature.declaration(newTermName("<init>")).asMethod + val outer1 = new B + val outer2 = new outer1.BB + val ctorMirror = cm.reflect(outer2).reflectClass(sym).reflectConstructor(ctor) + val instance = ctorMirror() + println(instance) + testMethodInvocation(instance) + } + + testInnerClass("B1") + testInnerClass("B2") + + def testInnerModule(name: String) = { + val sym = b.typeSignature.declaration(newTermName(name)).asModule + println(sym) + val outer1 = new B + val outer2 = new outer1.BB + val moduleMirror = cm.reflect(outer2).reflectModule(sym) + val instance = moduleMirror.instance + println(instance) + testMethodInvocation(instance) + } + + testInnerModule("B3") + testInnerModule("B4") + testInnerModule("B5") + testInnerModule("B6") +} diff --git a/test/files/run/reflection-enclosed-inner-nested-basic.check b/test/files/run/reflection-enclosed-inner-nested-basic.check new file mode 100644 index 0000000000..0f5176a6e7 --- /dev/null +++ b/test/files/run/reflection-enclosed-inner-nested-basic.check @@ -0,0 +1,20 @@ +object BB +List(constructor BB, class B1, class B2, object B3, object B4, object B5, object B6) +class B1 +B1 +1 +class B2 +B2 +2 +object B3 +B3 +3 +object B4 +B4 +4 +object B5 +B5 +5 +object B6 +B6 +6 diff --git a/test/files/run/reflection-enclosed-inner-nested-basic.scala b/test/files/run/reflection-enclosed-inner-nested-basic.scala new file mode 100644 index 0000000000..2800ee2548 --- /dev/null +++ b/test/files/run/reflection-enclosed-inner-nested-basic.scala @@ -0,0 +1,55 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.reflect.{classTag, ClassTag} + +class B { + object BB { + class B1 { override def toString = "B1"; def foo = 1 } + private class B2 { override def toString = "B2"; def foo = 2 } + object B3 { override def toString = "B3"; def foo = 3 } + private object B4 { override def toString = "B4"; def foo = 4 } + object B5 extends B1 { override def toString = "B5"; override def foo = 5 } + private object B6 extends B2 { override def toString = "B6"; override def foo = 6 } + } +} + +object Test extends App { + val outer1 = new B() + val b = cm.moduleSymbol(classTag[outer1.BB.type].runtimeClass) + println(b) + println(b.typeSignature.declarations.toList) + + def testMethodInvocation(instance: Any) = { + val instanceMirror = cm.reflect(instance) + val method = instanceMirror.symbol.typeSignature.declaration(newTermName("foo")).asMethod + val methodMirror = instanceMirror.reflectMethod(method) + println(methodMirror()) + } + + def testNestedClass(name: String) = { + val sym = b.typeSignature.declaration(newTypeName(name)).asClass + println(sym) + val ctor = sym.typeSignature.declaration(newTermName("<init>")).asMethod + val ctorMirror = cm.reflect(outer1.BB).reflectClass(sym).reflectConstructor(ctor) + val instance = ctorMirror() + println(instance) + testMethodInvocation(instance) + } + + testNestedClass("B1") + testNestedClass("B2") + + def testNestedModule(name: String) = { + val sym = b.typeSignature.declaration(newTermName(name)).asModule + println(sym) + val moduleMirror = cm.reflect(outer1.BB).reflectModule(sym) + val instance = moduleMirror.instance + println(instance) + testMethodInvocation(instance) + } + + testNestedModule("B3") + testNestedModule("B4") + testNestedModule("B5") + testNestedModule("B6") +} diff --git a/test/files/run/reflection-enclosed-nested-basic.check b/test/files/run/reflection-enclosed-nested-basic.check new file mode 100644 index 0000000000..b0e61149f8 --- /dev/null +++ b/test/files/run/reflection-enclosed-nested-basic.check @@ -0,0 +1,20 @@ +object B +List(constructor B, class B1, class B2, object B3, object B4, object B5, object B6) +class B1 +B1 +1 +class B2 +B2 +2 +object B3 +B3 +3 +object B4 +B4 +4 +object B5 +B5 +5 +object B6 +B6 +6 diff --git a/test/files/run/reflection-enclosed-nested-basic.scala b/test/files/run/reflection-enclosed-nested-basic.scala new file mode 100644 index 0000000000..8b740c2da2 --- /dev/null +++ b/test/files/run/reflection-enclosed-nested-basic.scala @@ -0,0 +1,52 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.reflect.{classTag, ClassTag} + +object B { + class B1 { override def toString = "B1"; def foo = 1 } + private class B2 { override def toString = "B2"; def foo = 2 } + object B3 { override def toString = "B3"; def foo = 3 } + private object B4 { override def toString = "B4"; def foo = 4 } + object B5 extends B1 { override def toString = "B5"; override def foo = 5 } + private object B6 extends B2 { override def toString = "B6"; override def foo = 6 } +} + +object Test extends App { + val b = cm.moduleSymbol(classTag[B.type].runtimeClass) + println(b) + println(b.typeSignature.declarations.toList) + + def testMethodInvocation(instance: Any) = { + val instanceMirror = cm.reflect(instance) + val method = instanceMirror.symbol.typeSignature.declaration(newTermName("foo")).asMethod + val methodMirror = instanceMirror.reflectMethod(method) + println(methodMirror()) + } + + def testNestedClass(name: String) = { + val sym = b.typeSignature.declaration(newTypeName(name)).asClass + println(sym) + val ctor = sym.typeSignature.declaration(newTermName("<init>")).asMethod + val ctorMirror = cm.reflectClass(sym).reflectConstructor(ctor) + val instance = ctorMirror() + println(instance) + testMethodInvocation(instance) + } + + testNestedClass("B1") + testNestedClass("B2") + + def testNestedModule(name: String) = { + val sym = b.typeSignature.declaration(newTermName(name)).asModule + println(sym) + val moduleMirror = cm.reflectModule(sym) + val instance = moduleMirror.instance + println(instance) + testMethodInvocation(instance) + } + + testNestedModule("B3") + testNestedModule("B4") + testNestedModule("B5") + testNestedModule("B6") +} diff --git a/test/files/run/reflection-enclosed-nested-inner-basic.check b/test/files/run/reflection-enclosed-nested-inner-basic.check new file mode 100644 index 0000000000..add7a81c0a --- /dev/null +++ b/test/files/run/reflection-enclosed-nested-inner-basic.check @@ -0,0 +1,20 @@ +class BB +List(constructor BB, class B1, class B2, object B3, object B4, object B5, object B6) +class B1 +B1 +1 +class B2 +B2 +2 +object B3 +B3 +3 +object B4 +B4 +4 +object B5 +B5 +5 +object B6 +B6 +6 diff --git a/test/files/run/reflection-enclosed-nested-inner-basic.scala b/test/files/run/reflection-enclosed-nested-inner-basic.scala new file mode 100644 index 0000000000..7466733d37 --- /dev/null +++ b/test/files/run/reflection-enclosed-nested-inner-basic.scala @@ -0,0 +1,54 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.reflect.{classTag, ClassTag} + +object B { + class BB { + class B1 { override def toString = "B1"; def foo = 1 } + private class B2 { override def toString = "B2"; def foo = 2 } + object B3 { override def toString = "B3"; def foo = 3 } + private object B4 { override def toString = "B4"; def foo = 4 } + object B5 extends B1 { override def toString = "B5"; override def foo = 5 } + private object B6 extends B2 { override def toString = "B6"; override def foo = 6 } + } +} + +object Test extends App { + val b = cm.classSymbol(classTag[B.BB].runtimeClass) + println(b) + println(b.typeSignature.declarations.toList) + + def testMethodInvocation(instance: Any) = { + val instanceMirror = cm.reflect(instance) + val method = instanceMirror.symbol.typeSignature.declaration(newTermName("foo")).asMethod + val methodMirror = instanceMirror.reflectMethod(method) + println(methodMirror()) + } + + def testInnerClass(name: String) = { + val sym = b.typeSignature.declaration(newTypeName(name)).asClass + println(sym) + val ctor = sym.typeSignature.declaration(newTermName("<init>")).asMethod + val ctorMirror = cm.reflect(new B.BB).reflectClass(sym).reflectConstructor(ctor) + val instance = ctorMirror() + println(instance) + testMethodInvocation(instance) + } + + testInnerClass("B1") + testInnerClass("B2") + + def testInnerModule(name: String) = { + val sym = b.typeSignature.declaration(newTermName(name)).asModule + println(sym) + val moduleMirror = cm.reflect(new B.BB).reflectModule(sym) + val instance = moduleMirror.instance + println(instance) + testMethodInvocation(instance) + } + + testInnerModule("B3") + testInnerModule("B4") + testInnerModule("B5") + testInnerModule("B6") +} diff --git a/test/files/run/reflection-enclosed-nested-nested-basic.check b/test/files/run/reflection-enclosed-nested-nested-basic.check new file mode 100644 index 0000000000..0f5176a6e7 --- /dev/null +++ b/test/files/run/reflection-enclosed-nested-nested-basic.check @@ -0,0 +1,20 @@ +object BB +List(constructor BB, class B1, class B2, object B3, object B4, object B5, object B6) +class B1 +B1 +1 +class B2 +B2 +2 +object B3 +B3 +3 +object B4 +B4 +4 +object B5 +B5 +5 +object B6 +B6 +6 diff --git a/test/files/run/reflection-enclosed-nested-nested-basic.scala b/test/files/run/reflection-enclosed-nested-nested-basic.scala new file mode 100644 index 0000000000..8335ea482a --- /dev/null +++ b/test/files/run/reflection-enclosed-nested-nested-basic.scala @@ -0,0 +1,54 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.reflect.{classTag, ClassTag} + +object B { + object BB { + class B1 { override def toString = "B1"; def foo = 1 } + private class B2 { override def toString = "B2"; def foo = 2 } + object B3 { override def toString = "B3"; def foo = 3 } + private object B4 { override def toString = "B4"; def foo = 4 } + object B5 extends B1 { override def toString = "B5"; override def foo = 5 } + private object B6 extends B2 { override def toString = "B6"; override def foo = 6 } + } +} + +object Test extends App { + val b = cm.moduleSymbol(classTag[B.BB.type].runtimeClass) + println(b) + println(b.typeSignature.declarations.toList) + + def testMethodInvocation(instance: Any) = { + val instanceMirror = cm.reflect(instance) + val method = instanceMirror.symbol.typeSignature.declaration(newTermName("foo")).asMethod + val methodMirror = instanceMirror.reflectMethod(method) + println(methodMirror()) + } + + def testNestedClass(name: String) = { + val sym = b.typeSignature.declaration(newTypeName(name)).asClass + println(sym) + val ctor = sym.typeSignature.declaration(newTermName("<init>")).asMethod + val ctorMirror = cm.reflectClass(sym).reflectConstructor(ctor) + val instance = ctorMirror() + println(instance) + testMethodInvocation(instance) + } + + testNestedClass("B1") + testNestedClass("B2") + + def testNestedModule(name: String) = { + val sym = b.typeSignature.declaration(newTermName(name)).asModule + println(sym) + val moduleMirror = cm.reflectModule(sym) + val instance = moduleMirror.instance + println(instance) + testMethodInvocation(instance) + } + + testNestedModule("B3") + testNestedModule("B4") + testNestedModule("B5") + testNestedModule("B6") +} diff --git a/test/files/run/reflection-modulemirror-inner-good.check b/test/files/run/reflection-modulemirror-inner-good.check index 0bf38a73d1..fe658e7087 100644 --- a/test/files/run/reflection-modulemirror-inner-good.check +++ b/test/files/run/reflection-modulemirror-inner-good.check @@ -1,2 +1 @@ -inner and nested modules are not supported yet
-()
+R
diff --git a/test/files/run/reflection-modulemirror-nested-good.check b/test/files/run/reflection-modulemirror-nested-good.check index 0bf38a73d1..fe658e7087 100644 --- a/test/files/run/reflection-modulemirror-nested-good.check +++ b/test/files/run/reflection-modulemirror-nested-good.check @@ -1,2 +1 @@ -inner and nested modules are not supported yet
-()
+R
diff --git a/test/files/run/reflection-repl-classes.check b/test/files/run/reflection-repl-classes.check new file mode 100644 index 0000000000..1c7f86c90c --- /dev/null +++ b/test/files/run/reflection-repl-classes.check @@ -0,0 +1,35 @@ +Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> class A
+defined class A
+
+scala>
+
+scala> class B {
+ def foo(x: A) = 1
+}
+defined class B
+
+scala>
+
+scala> object defs {
+ val cm = reflect.runtime.currentMirror
+ val u = cm.universe
+ val im = cm.reflect(new B)
+ val method = im.symbol.typeSignature.member(u.newTermName("foo")).asMethod
+ val mm = im.reflectMethod(method)
+}
+defined module defs
+
+scala> import defs._
+import defs._
+
+scala>
+
+scala> mm(new A)
+res0: Any = 1
+
+scala>
diff --git a/test/files/run/reflection-repl-classes.scala b/test/files/run/reflection-repl-classes.scala new file mode 100644 index 0000000000..80e332cde3 --- /dev/null +++ b/test/files/run/reflection-repl-classes.scala @@ -0,0 +1,22 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ + |class A + | + |class B { + | def foo(x: A) = 1 + |} + | + |object defs { + | val cm = reflect.runtime.currentMirror + | val u = cm.universe + | val im = cm.reflect(new B) + | val method = im.symbol.typeSignature.member(u.newTermName("foo")).asMethod + | val mm = im.reflectMethod(method) + |} + |import defs._ + | + |mm(new A) + |""".stripMargin +} diff --git a/test/files/run/reflection-repl.check b/test/files/run/reflection-repl-elementary.check index 341dd10ab0..341dd10ab0 100644 --- a/test/files/run/reflection-repl.check +++ b/test/files/run/reflection-repl-elementary.check diff --git a/test/files/run/reflection-repl.scala b/test/files/run/reflection-repl-elementary.scala index 72b65a1a70..72b65a1a70 100644 --- a/test/files/run/reflection-repl.scala +++ b/test/files/run/reflection-repl-elementary.scala diff --git a/test/files/run/reflection-sanitychecks.check b/test/files/run/reflection-sanitychecks.check index a1df486b51..821457a999 100644 --- a/test/files/run/reflection-sanitychecks.check +++ b/test/files/run/reflection-sanitychecks.check @@ -6,7 +6,7 @@ method #2: 14 constructor #1: scala.ScalaReflectionException: expected a constructor of class D, you provided method bar constructor #2: scala.ScalaReflectionException: expected a constructor of class D, you provided constructor C class: CC -object: java.lang.Error: inner and nested modules are not supported yet +object: CO =========members of D in a mirror of D========= field #1: 21 @@ -16,7 +16,7 @@ method #2: 14 constructor #1: scala.ScalaReflectionException: expected a constructor of class D, you provided method bar constructor #2: an instance of class D class: CC -object: java.lang.Error: inner and nested modules are not supported yet +object: CO =========members of E in a mirror of D========= field #1: scala.ScalaReflectionException: expected a member of class D, you provided value E.foo diff --git a/test/files/run/t4536.flags b/test/files/run/t4536.flags new file mode 100644 index 0000000000..1141f97507 --- /dev/null +++ b/test/files/run/t4536.flags @@ -0,0 +1 @@ +-language:dynamics diff --git a/test/files/run/t5040.flags b/test/files/run/t5040.flags new file mode 100644 index 0000000000..1141f97507 --- /dev/null +++ b/test/files/run/t5040.flags @@ -0,0 +1 @@ +-language:dynamics diff --git a/test/files/run/t6197.check b/test/files/run/t6197.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/t6197.check diff --git a/test/files/run/t6197.scala b/test/files/run/t6197.scala new file mode 100644 index 0000000000..5ab4b002d7 --- /dev/null +++ b/test/files/run/t6197.scala @@ -0,0 +1,21 @@ +import scala.collection.immutable._ + +object Test extends App { + + // test that a HashTrieSet with one leaf element is not created! + val x = HashSet.empty + 1 + 2 + if(x.getClass.getSimpleName != "HashTrieSet") + println("A hash set containing two non-colliding values should be a HashTrieSet") + + val y = x - 1 + if(y.getClass.getSimpleName != "HashSet1") + println("A hash set containing one element should always use HashSet1") + + // it is pretty hard to test that the case where a HashTrieSet has one element which + // is itself of type HashTrieS t. That is because the improve hash function makes it very difficult + // to find keys that will have hashes that are close together. + // + // However, it is also not necessary. Removing the ability of a HashTrieSet to have + // one child of type HashTrieSet completely breaks the HashSet, so that many other + // tests fail +} diff --git a/test/files/run/t6198.check b/test/files/run/t6198.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/t6198.check diff --git a/test/files/run/t6198.scala b/test/files/run/t6198.scala new file mode 100644 index 0000000000..5aa8f1c1cf --- /dev/null +++ b/test/files/run/t6198.scala @@ -0,0 +1,24 @@ +import scala.collection.immutable._ + +object Test extends App { + // test that ListSet.tail does not use a builder + // we can't test for O(1) behavior, so the best we can do is to + // check that ls.tail always returns the same instance + val ls = ListSet.empty[Int] + 1 + 2 + + if(ls.tail ne ls.tail) + println("ListSet.tail should not use a builder!") + + // 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 = HashSet.empty + Collision(0) + Collision(1) + if(x.getClass.getSimpleName != "HashSetCollision1") + println("HashSet of size >1 with collisions should use HashSetCollision") + + // remove the collision again by removing all but one element + val y = x - Collision(0) + if(y.getClass.getSimpleName != "HashSet1") + println("HashSet of size 1 should use HashSet1" + y.getClass) +} diff --git a/test/files/run/t6223.check b/test/files/run/t6223.check new file mode 100644 index 0000000000..90ec019407 --- /dev/null +++ b/test/files/run/t6223.check @@ -0,0 +1,4 @@ +bar +bar$mcI$sp +bar$mIc$sp +bar$mIcI$sp diff --git a/test/files/run/t6223.scala b/test/files/run/t6223.scala new file mode 100644 index 0000000000..4ab7c832e6 --- /dev/null +++ b/test/files/run/t6223.scala @@ -0,0 +1,11 @@ +class Foo[@specialized(Int) A](a:A) { + def bar[@specialized(Int) B](f:A => B) = new Foo(f(a)) +} + +object Test { + def main(args:Array[String]) { + val f = new Foo(333) + val ms = f.getClass().getDeclaredMethods() + ms.foreach(m => println(m.getName)) + } +} diff --git a/test/scaladoc/run/SI-5933.check b/test/scaladoc/run/SI-5933.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-5933.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-5933.scala b/test/scaladoc/run/SI-5933.scala new file mode 100644 index 0000000000..087116fa71 --- /dev/null +++ b/test/scaladoc/run/SI-5933.scala @@ -0,0 +1,43 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + // Test code + override def code = """ + // This example should compile without errors, and the pattern match should be correctly displayed + + import language.higherKinds + + abstract class Base[M[_, _]] { + def foo[A, B]: M[(A, B), Any] + } + + class Derived extends Base[PartialFunction] { + def foo[A, B] /*: PartialFunction[(A, B) => Any]*/ = { case (a, b) => (a: A, b: B) } + } + + object Test { + lazy val lx = { println("hello"); 3 } + def test1(x: Int = lx) = ??? + def test2(x: Int = lx match { case 0 => 1; case 3 => 4 }) = ??? + } + """ + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + val Test = rootPackage._object("Test") + val test1 = Test._method("test1") + val test2 = Test._method("test2") + + def assertEqual(s1: String, s2: String) = assert(s1 == s2, s1 + " == " + s2) + + assertEqual(test1.valueParams(0)(0).defaultValue.get.expression, "lx") + assertEqual(test2.valueParams(0)(0).defaultValue.get.expression, "lx match { case 0 => 1; case 3 => 4 }") + } +}
\ No newline at end of file |