diff options
31 files changed, 1114 insertions, 114 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index d122a1a207..38a6170637 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -133,7 +133,7 @@ self => val global: Global import global._ - case class OpInfo(lhs: Tree, operator: TermName, offset: Offset) { + case class OpInfo(lhs: Tree, operator: TermName, targs: List[Tree], offset: Offset) { def precedence = Precedence(operator.toString) } @@ -787,10 +787,13 @@ self => private def opHead = opstack.head private def headPrecedence = opHead.precedence private def popOpInfo(): OpInfo = try opHead finally opstack = opstack.tail - private def pushOpInfo(top: Tree) { - val opinfo = OpInfo(top, in.name, in.offset) - opstack ::= opinfo + private def pushOpInfo(top: Tree): Unit = { + val name = in.name + val offset = in.offset ident() + val targs = if (in.token == LBRACKET) exprTypeArgs() else Nil + val opinfo = OpInfo(top, name, targs, offset) + opstack ::= opinfo } def checkHeadAssoc(leftAssoc: Boolean) = checkAssoc(opHead.offset, opHead.operator, leftAssoc) @@ -800,6 +803,9 @@ self => ) def finishPostfixOp(start: Int, base: List[OpInfo], opinfo: OpInfo): Tree = { + if (opinfo.targs.nonEmpty) + syntaxError(opinfo.offset, "type application is not allowed for postfix operators") + val od = stripParens(reduceExprStack(base, opinfo.lhs)) makePostfixSelect(start, opinfo.offset, od, opinfo.operator) } @@ -809,7 +815,7 @@ self => val operatorPos: Position = Position.range(rhs.pos.source, offset, offset, offset + operator.length) val pos = lhs.pos union rhs.pos union operatorPos withPoint offset - atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos)) + atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos, opinfo.targs)) } def reduceExprStack(base: List[OpInfo], top: Tree): Tree = reduceStack(isExpr = true, base, top) @@ -1892,9 +1898,9 @@ self => def isDelimiter = in.token == RPAREN || in.token == RBRACE def isCommaOrDelimiter = isComma || isDelimiter val (isUnderscore, isStar) = opstack match { - case OpInfo(Ident(nme.WILDCARD), nme.STAR, _) :: _ => (true, true) - case OpInfo(_, nme.STAR, _) :: _ => (false, true) - case _ => (false, false) + case OpInfo(Ident(nme.WILDCARD), nme.STAR, _, _) :: _ => (true, true) + case OpInfo(_, nme.STAR, _, _) :: _ => (false, true) + case _ => (false, false) } def isSeqPatternClose = isUnderscore && isStar && isSequenceOK && isDelimiter val preamble = "bad simple pattern:" diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index d88470bd5e..cfee988efc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -55,7 +55,13 @@ abstract class TreeBuilder { ValDef(Modifiers(PRIVATE), name, tpt, EmptyTree) /** Create tree representing (unencoded) binary operation expression or pattern. */ - def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { + def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = { + require(isExpr || targs.isEmpty, s"Incompatible args to makeBinop: !isExpr but targs=$targs") + + def mkSelection(t: Tree) = { + def sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode)) + if (targs.isEmpty) sel else atPos(left.pos)(TypeApply(sel, targs)) + } def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args val arguments = right match { case Parens(args) => mkNamed(args) @@ -63,12 +69,12 @@ abstract class TreeBuilder { } if (isExpr) { if (treeInfo.isLeftAssoc(op)) { - Apply(atPos(opPos union left.pos) { Select(stripParens(left), op.encode) }, arguments) + Apply(mkSelection(left), arguments) } else { val x = freshTermName() Block( List(ValDef(Modifiers(SYNTHETIC | ARTIFACT), x, TypeTree(), stripParens(left))), - Apply(atPos(opPos union right.pos) { Select(stripParens(right), op.encode) }, List(Ident(x)))) + Apply(mkSelection(right), List(Ident(x)))) } } else { Apply(Ident(op.encode), stripParens(left) :: arguments) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 4f9f4c9e31..c8845344e9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -88,7 +88,9 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def genThrow(expr: Tree): BType = { val thrownKind = tpeTK(expr) - assert(exemplars.get(thrownKind).isSubtypeOf(ThrowableReference)) + // `throw null` is valid although scala.Null (as defined in src/libray-aux) isn't a subtype of Throwable. + // Similarly for scala.Nothing (again, as defined in src/libray-aux). + assert(thrownKind.isNullType || thrownKind.isNothingType || exemplars.get(thrownKind).isSubtypeOf(ThrowableReference)) genLoad(expr, thrownKind) lineNumber(expr) emit(asm.Opcodes.ATHROW) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level. @@ -824,7 +826,6 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { /* Generate code that loads args into label parameters. */ def genLoadLabelArguments(args: List[Tree], lblDef: LabelDef, gotoPos: Position) { - assert(args forall { a => !a.hasSymbolField || a.hasSymbolWhich( s => !s.isLabel) }, s"SI-6089 at: $gotoPos") // SI-6089 val aps = { val params: List[Symbol] = lblDef.params.map(_.symbol) @@ -857,12 +858,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { } def genLoadModule(tree: Tree): BType = { - // Working around SI-5604. Rather than failing the compile when we see a package here, check if there's a package object. val module = ( if (!tree.symbol.isPackageClass) tree.symbol else tree.symbol.info.member(nme.PACKAGE) match { - case NoSymbol => abort(s"Cannot use package as value: $tree") ; NoSymbol - case s => devWarning("Bug: found package class where package object expected. Converting.") ; s.moduleClass + case NoSymbol => abort(s"SI-5604: Cannot use package as value: $tree") + case s => abort(s"SI-5604: found package class where package object expected: $tree") } ) lineNumber(tree) @@ -871,7 +871,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { } def genLoadModule(module: Symbol) { - if (claszSymbol == module.moduleClass && jMethodName != "readResolve") { + def inStaticMethod = methSymbol != null && methSymbol.isStaticMember + if (claszSymbol == module.moduleClass && jMethodName != "readResolve" && !inStaticMethod) { mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) } else { val mbt = symInfoTK(module) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala index 9dcf263f4f..cc3265c5f9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala @@ -661,6 +661,14 @@ abstract class BCodeGlue extends SubComponent { val CT_NOTHING = brefType("scala/Nothing") // TODO needed? val CT_NULL = brefType("scala/Null") // TODO needed? + val srBooleanRef = brefType("scala/runtime/BooleanRef") + val srByteRef = brefType("scala/runtime/ByteRef") + val srCharRef = brefType("scala/runtime/CharRef") + val srIntRef = brefType("scala/runtime/IntRef") + val srLongRef = brefType("scala/runtime/LongRef") + val srFloatRef = brefType("scala/runtime/FloatRef") + val srDoubleRef = brefType("scala/runtime/DoubleRef") + /* Map from type kinds to the Java reference types. * Useful when pushing class literals onto the operand stack (ldc instruction taking a class literal). * @see Predef.classOf diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 64ed094a47..6b1bb5b220 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -440,13 +440,11 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { if (sym0 == definitions.NullClass) return RT_NULL; if (sym0 == definitions.NothingClass) return RT_NOTHING; - // Working around SI-5604. Rather than failing the compile when we see - // a package here, check if there's a package object. val sym = ( if (!sym0.isPackageClass) sym0 else sym0.info.member(nme.PACKAGE) match { - case NoSymbol => abort(s"Cannot use package as value: ${sym0.fullName}") - case s => devWarning("Bug: found package class where package object expected. Converting.") ; s.moduleClass + case NoSymbol => abort(s"SI-5604: Cannot use package as value: ${sym0.fullName}") + case s => abort(s"SI-5604: found package class where package object expected: $s") } ) @@ -561,7 +559,9 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { memberCTK } - exemplar(csym) // side effect city + + exemplar(csym).directMemberClasses = result + result } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index 529295389c..c3492b79a9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -46,6 +46,7 @@ abstract class BCodeIdiomatic extends BCodeGlue { val ObjectReference = brefType("java/lang/Object") val AnyRefReference = ObjectReference + val objArrayReference = arrayOf(ObjectReference) val JAVA_LANG_OBJECT = ObjectReference val JAVA_LANG_STRING = brefType("java/lang/String") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index c921d11d00..360ce58ecc 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -460,7 +460,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { } def lineNumber(tree: Tree) { if (!emitLines || !tree.pos.isDefined) return; - val nr = tree.pos.line + val nr = tree.pos.finalPosition.line if (nr != lastEmittedLineNr) { lastEmittedLineNr = nr lastInsn match { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala index 27bcbb82d4..9ddb7a3ce8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala @@ -386,10 +386,6 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { /* Does this tree have a try-catch block? */ def mayCleanStack(tree: Tree): Boolean = tree exists { t => t.isInstanceOf[Try] } - abstract class Cleanup(val value: AnyRef) { } - case class MonitorRelease(v: Symbol) extends Cleanup(v) { } - case class Finalizer(f: Tree) extends Cleanup (f) { } - trait EHClause case class NamelessEH(typeToDrop: BType, caseBody: Tree) extends EHClause case class BoundEH (patSymbol: Symbol, caseBody: Tree) extends EHClause diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala index dd2d63ad17..1eca69936a 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala @@ -24,11 +24,15 @@ abstract class BCodeTypes extends BCodeIdiomatic { // when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface) val isCompilingStdLib = !(settings.sourcepath.isDefault) + val srBoxedUnit = brefType("scala/runtime/BoxedUnit") + // special names var StringReference : BType = null var ThrowableReference : BType = null var jlCloneableReference : BType = null // java/lang/Cloneable + var jlNPEReference : BType = null // java/lang/NullPointerException var jioSerializableReference : BType = null // java/io/Serializable + var scalaSerializableReference : BType = null // scala/Serializable var classCastExceptionReference : BType = null // java/lang/ClassCastException /* A map from scala primitive type-symbols to BTypes */ @@ -52,6 +56,8 @@ abstract class BCodeTypes extends BCodeIdiomatic { /* The Object => String overload. */ var String_valueOf: Symbol = null + var ArrayInterfaces: Set[Tracked] = null + // scala.FunctionX and scala.runtim.AbstractFunctionX val FunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1) val AbstractFunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1) @@ -128,15 +134,17 @@ abstract class BCodeTypes extends BCodeIdiomatic { ) } - exemplar(JavaCloneableClass).c - exemplar(JavaSerializableClass).c - exemplar(SerializableClass).c + exemplar(JavaCloneableClass) + exemplar(JavaSerializableClass) + exemplar(SerializableClass) StringReference = exemplar(StringClass).c StringBuilderReference = exemplar(StringBuilderClass).c ThrowableReference = exemplar(ThrowableClass).c jlCloneableReference = exemplar(JavaCloneableClass).c + jlNPEReference = exemplar(NullPointerExceptionClass).c jioSerializableReference = exemplar(JavaSerializableClass).c + scalaSerializableReference = exemplar(SerializableClass).c classCastExceptionReference = exemplar(ClassCastExceptionClass).c /* @@ -203,6 +211,23 @@ abstract class BCodeTypes extends BCodeIdiomatic { * All methods of this class can-multi-thread */ case class Tracked(c: BType, flags: Int, sc: Tracked, ifaces: Array[Tracked], innersChain: Array[InnerClassEntry]) { + + // not a case-field because we initialize it only for JVM classes we emit. + private var _directMemberClasses: List[BType] = null + + def directMemberClasses: List[BType] = { + assert(_directMemberClasses != null, s"getter directMemberClasses() invoked too early for $c") + _directMemberClasses + } + + def directMemberClasses_=(bs: List[BType]) { + if (_directMemberClasses != null) { + // TODO we enter here when both mirror class and plain class are emitted for the same ModuleClassSymbol. + assert(_directMemberClasses == bs.sortBy(_.off)) + } + _directMemberClasses = bs.sortBy(_.off) + } + /* `isCompilingStdLib` saves the day when compiling: * (1) scala.Nothing (the test `c.isNonSpecial` fails for it) * (2) scala.Boolean (it has null superClass and is not an interface) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6ec364bcb6..307f42c0bc 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -73,48 +73,55 @@ trait ScalaSettings extends AbsScalaSettings val nospecialization = BooleanSetting ("-no-specialization", "Ignore @specialize annotations.") val language = MultiStringSetting("-language", "feature", "Enable one or more language features.") + /* + * The previous "-source" option is intended to be used mainly + * though this helper. + */ + lazy val isScala211: Boolean = (source.value >= ScalaVersion("2.11.0")) + /** * -X "Advanced" settings */ - val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options.") - val checkInit = BooleanSetting ("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") - val developer = BooleanSetting ("-Xdev", "Indicates user is a developer - issue warnings about anything which seems amiss") - val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions or assumptions.") - val elidebelow = IntSetting ("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument", + val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options.") + val checkInit = BooleanSetting ("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") + val developer = BooleanSetting ("-Xdev", "Indicates user is a developer - issue warnings about anything which seems amiss") + val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions or assumptions.") + val elidebelow = IntSetting ("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument", elidable.MINIMUM, None, elidable.byName get _) - val noForwarders = BooleanSetting ("-Xno-forwarders", "Do not generate static forwarders in mirror classes.") - val genPhaseGraph = StringSetting ("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot.", "") - val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more detail on why some implicits are not applicable.") - val logImplicitConv = BooleanSetting ("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.") - val logReflectiveCalls = BooleanSetting("-Xlog-reflective-calls", "Print a message when a reflective method call is generated") - val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") - val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") - val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) - val Xmigration = ScalaVersionSetting("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", AnyScalaVersion) - val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") - val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.") - val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") - val plugin = MultiStringSetting("-Xplugin", "paths", "Load a plugin from each classpath.") - val disable = MultiStringSetting("-Xplugin-disable", "plugin", "Disable plugins by name.") - val showPlugins = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") - val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort if a named plugin is not loaded.") - val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) - val Xprint = PhasesSetting ("-Xprint", "Print out program after") - val writeICode = PhasesSetting ("-Xprint-icode", "Log internal icode to *.icode files after", "icode") - val Xprintpos = BooleanSetting ("-Xprint-pos", "Print tree positions, as offsets.") - val printtypes = BooleanSetting ("-Xprint-types", "Print tree types (debugging option).") - val prompt = BooleanSetting ("-Xprompt", "Display a prompt after each error (debugging option).") - val resident = BooleanSetting ("-Xresident", "Compiler stays resident: read source filenames from standard input.") - val script = StringSetting ("-Xscript", "object", "Treat the source file as a script and wrap it in a main method.", "") - val mainClass = StringSetting ("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "") - val Xshowcls = StringSetting ("-Xshow-class", "class", "Show internal representation of class.", "") - val Xshowobj = StringSetting ("-Xshow-object", "object", "Show internal representation of object.", "") - val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.") - val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "") - val strictInference = BooleanSetting ("-Xstrict-inference", "Don't infer known-unsound types") + val noForwarders = BooleanSetting ("-Xno-forwarders", "Do not generate static forwarders in mirror classes.") + val genPhaseGraph = StringSetting ("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot.", "") + val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more detail on why some implicits are not applicable.") + val logImplicitConv = BooleanSetting ("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.") + val logReflectiveCalls = BooleanSetting ("-Xlog-reflective-calls", "Print a message when a reflective method call is generated") + val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") + val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") + val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) + val Xmigration = ScalaVersionSetting ("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", AnyScalaVersion) + val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") + val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.") + val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") + val plugin = MultiStringSetting ("-Xplugin", "paths", "Load a plugin from each classpath.") + val disable = MultiStringSetting ("-Xplugin-disable", "plugin", "Disable plugins by name.") + val showPlugins = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") + val require = MultiStringSetting ("-Xplugin-require", "plugin", "Abort if a named plugin is not loaded.") + val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) + val Xprint = PhasesSetting ("-Xprint", "Print out program after") + val writeICode = PhasesSetting ("-Xprint-icode", "Log internal icode to *.icode files after", "icode") + val Xprintpos = BooleanSetting ("-Xprint-pos", "Print tree positions, as offsets.") + val printtypes = BooleanSetting ("-Xprint-types", "Print tree types (debugging option).") + val prompt = BooleanSetting ("-Xprompt", "Display a prompt after each error (debugging option).") + val resident = BooleanSetting ("-Xresident", "Compiler stays resident: read source filenames from standard input.") + val script = StringSetting ("-Xscript", "object", "Treat the source file as a script and wrap it in a main method.", "") + val mainClass = StringSetting ("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "") + val Xshowcls = StringSetting ("-Xshow-class", "class", "Show internal representation of class.", "") + val Xshowobj = StringSetting ("-Xshow-object", "object", "Show internal representation of object.", "") + val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.") + val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "") + val strictInference = BooleanSetting ("-Xstrict-inference", "Don't infer known-unsound types") + val source = ScalaVersionSetting ("-Xsource", "version", "Treat compiler input as Scala source for the specified version, see SI-8126.", ScalaVersion("2.11")) withPostSetHook ( _ => isScala211) val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") - val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") + val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") /** Compatibility stubs for options whose value name did * not previously match the option name. diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 19fba639e3..06a1e21e8b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1365,7 +1365,7 @@ trait Implicits { maybeInvalidConversionError("the result type of an implicit conversion must be more specific than AnyRef") result = SearchFailure } - else if (isInvalidConversionSource(pt)) { + else if (settings.isScala211 && isInvalidConversionSource(pt)) { maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion") result = SearchFailure } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 95f2620061..4ba8d56da0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -132,11 +132,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans defaultMethodNames.toList.distinct foreach { name => val methods = clazz.info.findMember(name, 0L, requiredFlags = METHOD, stableOnly = false).alternatives - def hasDefaultParam(tpe: Type): Boolean = tpe match { - case MethodType(params, restpe) => (params exists (_.hasDefault)) || hasDefaultParam(restpe) - case _ => false - } - val haveDefaults = methods filter (sym => hasDefaultParam(sym.info) && !nme.isProtectedAccessorName(sym.name)) + val haveDefaults = methods filter (sym => mexists(sym.info.paramss)(_.hasDefault) && !nme.isProtectedAccessorName(sym.name)) if (haveDefaults.lengthCompare(1) > 0) { val owners = haveDefaults map (_.owner) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6b5afce993..5d0d5392dd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1793,7 +1793,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val impl2 = finishMethodSynthesis(impl1, clazz, context) - if (mdef.symbol == PredefModule) + if (settings.isScala211 && mdef.symbol == PredefModule) ensurePredefParentsAreInSameSourceFile(impl2) // SI-5954. On second compile of a companion class contained in a package object we end up diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 6e6b617e5c..1bd9323752 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -55,7 +55,7 @@ trait Parsers { self: Quasiquotes => def isHole(name: Name): Boolean = holeMap.contains(name) - override implicit def fresh: FreshNameCreator = new FreshNameCreator(nme.QUASIQUOTE_PREFIX) + override implicit lazy val fresh: FreshNameCreator = new FreshNameCreator(nme.QUASIQUOTE_PREFIX) override val treeBuilder = new ParserTreeBuilder { override implicit def fresh: FreshNameCreator = parser.fresh @@ -193,4 +193,4 @@ trait Parsers { self: Quasiquotes => } object FreshName extends FreshNameExtractor(nme.QUASIQUOTE_PREFIX) -}
\ No newline at end of file +} diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 127b22185a..67e9d18da7 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -15,6 +15,7 @@ package immutable import generic._ import scala.collection.parallel.immutable.ParHashSet import scala.collection.GenSet +import scala.annotation.tailrec /** This class implements immutable sets using a hash trie. * @@ -38,7 +39,7 @@ class HashSet[A] extends AbstractSet[A] with CustomParallelizable[A, ParHashSet[A]] with Serializable { - import HashSet.{nullToEmpty, bufferSize} + import HashSet.{nullToEmpty, bufferSize, LeafHashSet} override def companion: GenericCompanion[HashSet] = HashSet @@ -85,8 +86,81 @@ class HashSet[A] extends AbstractSet[A] override def + (elem1: A, elem2: A, elems: A*): HashSet[A] = this + elem1 + elem2 ++ elems + override def union(that: GenSet[A]): HashSet[A] = that match { + case that: HashSet[A] => + val buffer = new Array[HashSet[A]](bufferSize(this.size + that.size)) + nullToEmpty(union0(that, 0, buffer, 0)) + case _ => super.union(that) + } + + override def intersect(that: GenSet[A]): HashSet[A] = that match { + case that: HashSet[A] => + val buffer = new Array[HashSet[A]](bufferSize(this.size min that.size)) + nullToEmpty(intersect0(that, 0, buffer, 0)) + case _ => super.intersect(that) + } + + override def diff(that: GenSet[A]): HashSet[A] = that match { + case that: HashSet[A] => + val buffer = new Array[HashSet[A]](bufferSize(this.size)) + nullToEmpty(diff0(that, 0, buffer, 0)) + case _ => super.diff(that) + } + + /** + * Union with a leaf HashSet at a given level. + * @param that a leaf HashSet + * @param level the depth in the tree. We need this when we have to create a branch node on top of this and that + * @return The union of this and that at the given level. Unless level is zero, the result is not a self-contained + * HashSet but needs to be stored at the correct depth + */ + private[immutable] def union0(that: LeafHashSet[A], level: Int): HashSet[A] = { + // the default implementation is for the empty set, so we just return that + that + } + + /** + * Union with a HashSet at a given level + * @param that a HashSet + * @param level the depth in the tree. We need to keep track of the level to know how deep we are in the tree + * @param buffer a temporary buffer that is used for temporarily storing elements when creating new branch nodes + * @param offset0 the first offset into the buffer in which we are allowed to write + * @return The union of this and that at the given level. Unless level is zero, the result is not a self-contained + * HashSet but needs to be stored at the correct depth + */ + private[immutable] def union0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = { + // the default implementation is for the empty set, so we just return that + that + } + + /** + * Intersection with another hash set at a given level + * @param level the depth in the tree. We need to keep track of the level to know how deep we are in the tree + * @param buffer a temporary buffer that is used for temporarily storing elements when creating new branch nodes + * @param offset0 the first offset into the buffer in which we are allowed to write + * @return The intersection of this and that at the given level. Unless level is zero, the result is not a + * self-contained HashSet but needs to be stored at the correct depth + */ + private[immutable] def intersect0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = { + // the default implementation is for the empty set, so we just return the empty set + null + } + + /** + * Diff with another hash set at a given level + * @param level the depth in the tree. We need to keep track of the level to know how deep we are in the tree + * @param buffer a temporary buffer that is used for temporarily storing elements when creating new branch nodes + * @param offset0 the first offset into the buffer in which we are allowed to write + * @return The diff of this and that at the given level. Unless level is zero, the result is not a + * self-contained HashSet but needs to be stored at the correct depth + */ + private[immutable] def diff0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = { + // the default implementation is for the empty set, so we just return the empty set + null + } + def - (e: A): HashSet[A] = - removed0(e, computeHash(e), 0) + nullToEmpty(removed0(e, computeHash(e), 0)) override def filter(p: A => Boolean) = { val buffer = new Array[HashSet[A]](bufferSize(size)) @@ -165,7 +239,14 @@ object HashSet extends ImmutableSetFactory[HashSet] { } } - class HashSet1[A](private[HashSet] val key: A, private[HashSet] val hash: Int) extends HashSet[A] { + /** + * Common superclass of HashSet1 and HashSetCollision1, which are the two possible leaves of the Trie + */ + private[HashSet] sealed abstract class LeafHashSet[A] extends HashSet[A] { + private[HashSet] def hash:Int + } + + class HashSet1[A](private[HashSet] val key: A, private[HashSet] val hash: Int) extends LeafHashSet[A] { override def size = 1 override def get0(key: A, hash: Int, level: Int): Boolean = @@ -190,8 +271,42 @@ object HashSet extends ImmutableSetFactory[HashSet] { } } + override private[immutable] def union0(that: LeafHashSet[A], level: Int): HashSet[A] = that match { + case that if that.hash != this.hash => + // different hash code, so there is no need to investigate further. + // Just create a branch node containing the two. + makeHashTrieSet(this.hash, this, that.hash, that, level) + case that: HashSet1[A] => + if (this.key == that.key) { + this + } else { + // 32-bit hash collision (rare, but not impossible) + new HashSetCollision1[A](hash, ListSet.empty + this.key + that.key) + } + case that: HashSetCollision1[A] => + val ks1 = that.ks + key + // Could use eq check (faster) if ListSet was guaranteed to return itself + if (ks1.size == that.ks.size) { + that + } else { + new HashSetCollision1[A](hash, ks1) + } + } + + override private[immutable] def union0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int) = { + // switch to the Leaf version of union + // we can exchange the arguments because union is symmetrical + that.union0(this, level) + } + + override private[immutable] def intersect0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = + if (that.get0(key, hash, level)) this else null + + override private[immutable] def diff0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = + if (that.get0(key, hash, level)) null else this + override def removed0(key: A, hash: Int, level: Int): HashSet[A] = - if (hash == this.hash && key == this.key) HashSet.empty[A] else this + if (hash == this.hash && key == this.key) null else this override protected def filter0(p: A => Boolean, negate: Boolean, level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = if (negate ^ p(key)) this else null @@ -200,8 +315,7 @@ object HashSet extends ImmutableSetFactory[HashSet] { override def foreach[U](f: A => U): Unit = f(key) } - private[immutable] class HashSetCollision1[A](private[HashSet] val hash: Int, val ks: ListSet[A]) - extends HashSet[A] { + private[immutable] class HashSetCollision1[A](private[HashSet] val hash: Int, val ks: ListSet[A]) extends LeafHashSet[A] { override def size = ks.size @@ -220,15 +334,116 @@ object HashSet extends ImmutableSetFactory[HashSet] { if (hash == this.hash) new HashSetCollision1(hash, ks + key) else makeHashTrieSet(this.hash, this, hash, new HashSet1(key, hash), level) + override def union0(that: LeafHashSet[A], level: Int): HashSet[A] = that match { + case that if that.hash != this.hash => + // different hash code, so there is no need to investigate further. + // Just create a branch node containing the two. + makeHashTrieSet(this.hash, this, that.hash, that, level) + case that: HashSet1[A] => + val ks1 = ks + that.key + // Could use eq check (faster) if ListSet was guaranteed to return itself + if (ks1.size == ks.size) { + this + } else { + // create a new HashSetCollision with the existing hash + // we don't have to check for size=1 because union is never going to remove elements + new HashSetCollision1[A](hash, ks1) + } + case that: HashSetCollision1[A] => + val ks1 = this.ks ++ that.ks + ks1.size match { + case size if size == this.ks.size => + // could this check be made faster by doing an eq check? + // I am not sure we can rely on ListSet returning itself when all elements are already in the set, + // so it seems unwise to rely on it. + this + case size if size == that.ks.size => + // we have to check this as well, since we don't want to create a new instance if this is a subset of that + that + case _ => + // create a new HashSetCollision with the existing hash + // we don't have to check for size=1 because union is never going to remove elements + new HashSetCollision1[A](hash, ks1) + } + } + + override def union0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = that match { + case that: LeafHashSet[A] => + // switch to the simpler Tree/Leaf implementation + this.union0(that, level) + case that: HashTrieSet[A] => + // switch to the simpler Tree/Leaf implementation + // we can swap this and that because union is symmetrical + that.union0(this, level) + case _ => this + } + + override private[immutable] def intersect0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = { + // filter the keys, taking advantage of the fact that we know their hash code + val ks1 = ks.filter(that.get0(_, hash, level)) + ks1.size match { + case 0 => + // the empty set + null + case size if size == this.size => + // unchanged + // We do this check first since even if the result is of size 1 since + // it is preferable to return the existing set for better structural sharing + this + case size if size == that.size => + // the other set + // We do this check first since even if the result is of size 1 since + // it is preferable to return the existing set for better structural sharing + that + case 1 => + // create a new HashSet1 with the hash we already know + new HashSet1(ks1.head, hash) + case _ => + // create a new HashSetCollison with the hash we already know and the new keys + new HashSetCollision1(hash, ks1) + } + } + + override private[immutable] def diff0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = { + val ks1 = ks.filterNot(that.get0(_, hash, level)) + ks1.size match { + case 0 => + // the empty set + null + case size if size == this.size => + // unchanged + // We do this check first since even if the result is of size 1 since + // it is preferable to return the existing set for better structural sharing + this + case 1 => + // create a new HashSet1 with the hash we already know + new HashSet1(ks1.head, hash) + case _ => + // create a new HashSetCollison with the hash we already know and the new keys + new HashSetCollision1(hash, ks1) + } + } + override def removed0(key: A, hash: Int, level: Int): HashSet[A] = if (hash == this.hash) { val ks1 = ks - key - if(ks1.isEmpty) - HashSet.empty[A] - else if(ks1.tail.isEmpty) - new HashSet1(ks1.head, hash) - else - new HashSetCollision1(hash, ks1) + ks1.size match { + case 0 => + // the empty set + null + case size if size == ks.size => + // We do this check first since even if the result is of size 1 since + // it is preferable to return the existing set for better structural sharing + // we can not rely on ks.- returning the same instance if we subtract an element that is not in it + // so we need to do the size check + this + case 1 => + // create a new HashSet1 with the hash we already know + new HashSet1(ks1.head, hash) + case _ => + // create a new HashSetCollison with the hash we already know and the new keys + new HashSetCollision1(hash, ks1) + } } else this override protected def filter0(p: A => Boolean, negate: Boolean, level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = { @@ -345,6 +560,284 @@ object HashSet extends ImmutableSetFactory[HashSet] { } } + override private[immutable] def union0(that: LeafHashSet[A], level: Int): HashSet[A] = { + val index = (that.hash >>> level) & 0x1f + val mask = (1 << index) + val offset = Integer.bitCount(bitmap & (mask - 1)) + if ((bitmap & mask) != 0) { + val sub = elems(offset) + val sub1 = sub.union0(that, level + 5) + if (sub eq sub1) this + else { + val elems1 = new Array[HashSet[A]](elems.length) + Array.copy(elems, 0, elems1, 0, elems.length) + elems1(offset) = sub1 + new HashTrieSet(bitmap, elems1, size + (sub1.size - sub.size)) + } + } else { + val elems1 = new Array[HashSet[A]](elems.length + 1) + Array.copy(elems, 0, elems1, 0, offset) + elems1(offset) = that + Array.copy(elems, offset, elems1, offset + 1, elems.length - offset) + val bitmap1 = bitmap | mask + new HashTrieSet(bitmap1, elems1, size + that.size) + } + } + + override private[immutable] def union0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = that match { + case that if that eq this => + // shortcut for when that is this + // this happens often for nodes deeper in the tree, especially when that and this share a common "heritage" + // e.g. you have a large set A and do some small operations (adding and removing elements) to it to create B + // then A and B will have the vast majority of nodes in common, and this eq check will allow not even looking + // at these nodes. + this + case that: LeafHashSet[A] => + // when that is a leaf, we can switch to the simpler Tree/Leaf implementation + this.union0(that, level) + case that: HashTrieSet[A] => + val a = this.elems + var abm = this.bitmap + var ai = 0 + + val b = that.elems + var bbm = that.bitmap + var bi = 0 + + // fetch a new temporary array that is guaranteed to be big enough (32 elements) + var offset = offset0 + var rs = 0 + + // loop as long as there are bits left in either abm or bbm + while ((abm | bbm) != 0) { + // lowest remaining bit in abm + val alsb = abm ^ (abm & (abm - 1)) + // lowest remaining bit in bbm + val blsb = bbm ^ (bbm & (bbm - 1)) + if (alsb == blsb) { + val sub1 = a(ai).union0(b(bi), level + 5, buffer, offset) + rs += sub1.size + buffer(offset) = sub1 + offset += 1 + // clear lowest remaining one bit in abm and increase the a index + abm &= ~alsb + ai += 1 + // clear lowest remaining one bit in bbm and increase the b index + bbm &= ~blsb + bi += 1 + } else if (unsignedCompare(alsb - 1, blsb - 1)) { + // alsb is smaller than blsb, or alsb is set and blsb is 0 + // in any case, alsb is guaranteed to be set here! + val sub1 = a(ai) + rs += sub1.size + buffer(offset) = sub1 + offset += 1 + // clear lowest remaining one bit in abm and increase the a index + abm &= ~alsb + ai += 1 + } else { + // blsb is smaller than alsb, or blsb is set and alsb is 0 + // in any case, blsb is guaranteed to be set here! + val sub1 = b(bi) + rs += sub1.size + buffer(offset) = sub1 + offset += 1 + // clear lowest remaining one bit in bbm and increase the b index + bbm &= ~blsb + bi += 1 + } + } + if (rs == this.size) { + // if the result would be identical to this, we might as well return this + this + } else if (rs == that.size) { + // if the result would be identical to that, we might as well return that + that + } else { + // we don't have to check whether the result is a leaf, since union will only make the set larger + // and this is not a leaf to begin with. + val length = offset - offset0 + val elems = new Array[HashSet[A]](length) + System.arraycopy(buffer, offset0, elems, 0, length) + new HashTrieSet(this.bitmap | that.bitmap, elems, rs) + } + case _ => this + } + + override private[immutable] def intersect0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = that match { + case that if that eq this => + // shortcut for when that is this + // this happens often for nodes deeper in the tree, especially when that and this share a common "heritage" + // e.g. you have a large set A and do some small operations (adding and removing elements) to it to create B + // then A and B will have the vast majority of nodes in common, and this eq check will allow not even looking + // at these nodes! + this + case that: LeafHashSet[A] => + // when that is a leaf, we can switch to the simpler Tree/Leaf implementation + // it is OK to swap the arguments because intersect is symmetric + // (we can't do this in case of diff, which is not symmetric) + that.intersect0(this, level, buffer, offset0) + case that: HashTrieSet[A] => + val a = this.elems + var abm = this.bitmap + var ai = 0 + + val b = that.elems + var bbm = that.bitmap + var bi = 0 + + // if the bitmasks do not overlap, the result is definitely empty so we can abort here + if ((abm & bbm) == 0) + return null + + // fetch a new temporary array that is guaranteed to be big enough (32 elements) + var offset = offset0 + var rs = 0 + var rbm = 0 + + // loop as long as there are bits left that are set in both abm and bbm + while ((abm & bbm) != 0) { + // highest remaining bit in abm + val alsb = abm ^ (abm & (abm - 1)) + // highest remaining bit in bbm + val blsb = bbm ^ (bbm & (bbm - 1)) + if (alsb == blsb) { + val sub1 = a(ai).intersect0(b(bi), level + 5, buffer, offset) + if (sub1 ne null) { + rs += sub1.size + rbm |= alsb + buffer(offset) = sub1 + offset += 1 + } + // clear lowest remaining one bit in abm and increase the a index + abm &= ~alsb; + ai += 1 + // clear lowest remaining one bit in bbm and increase the b index + bbm &= ~blsb; + bi += 1 + } else if (unsignedCompare(alsb - 1, blsb - 1)) { + // alsb is smaller than blsb, or alsb is set and blsb is 0 + // in any case, alsb is guaranteed to be set here! + // clear lowest remaining one bit in abm and increase the a index + abm &= ~alsb; + ai += 1 + } else { + // blsb is smaller than alsb, or blsb is set and alsb is 0 + // in any case, blsb is guaranteed to be set here! + // clear lowest remaining one bit in bbm and increase the b index + bbm &= ~blsb; + bi += 1 + } + } + + if (rbm == 0) { + // if the result bitmap is empty, the result is the empty set + null + } else if (rs == size0) { + // if the result has the same number of elements as this, it must be identical to this, + // so we might as well return this + this + } else if (rs == that.size0) { + // if the result has the same number of elements as that, it must be identical to that, + // so we might as well return that + that + } else { + val length = offset - offset0 + if (length == 1 && !buffer(offset0).isInstanceOf[HashTrieSet[A]]) + buffer(offset0) + else { + val elems = new Array[HashSet[A]](length) + System.arraycopy(buffer, offset0, elems, 0, length) + new HashTrieSet[A](rbm, elems, rs) + } + } + case _ => null + } + + override private[immutable] def diff0(that: HashSet[A], level: Int, buffer: Array[HashSet[A]], offset0: Int): HashSet[A] = that match { + case that if that eq this => + // shortcut for when that is this + // this happens often for nodes deeper in the tree, especially when that and this share a common "heritage" + // e.g. you have a large set A and do some small operations (adding and removing elements) to it to create B + // then A and B will have the vast majority of nodes in common, and this eq check will allow not even looking + // at these nodes! + null + case that: HashSet1[A] => + removed0(that.key, that.hash, level) + case that: HashTrieSet[A] => + val a = this.elems + var abm = this.bitmap + var ai = 0 + + val b = that.elems + var bbm = that.bitmap + var bi = 0 + + // fetch a new temporary array that is guaranteed to be big enough (32 elements) + var offset = offset0 + var rs = 0 + var rbm = 0 + + // loop until there are no more bits in abm + while(abm!=0) { + // highest remaining bit in abm + val alsb = abm ^ (abm & (abm - 1)) + // highest remaining bit in bbm + val blsb = bbm ^ (bbm & (bbm - 1)) + if (alsb == blsb) { + val sub1 = a(ai).diff0(b(bi), level + 5, buffer, offset) + if (sub1 ne null) { + rs += sub1.size + rbm |= alsb + buffer(offset) = sub1 + offset += 1 + } + // clear lowest remaining one bit in abm and increase the a index + abm &= ~alsb; ai += 1 + // clear lowest remaining one bit in bbm and increase the b index + bbm &= ~blsb; bi += 1 + } else if (unsignedCompare(alsb - 1, blsb - 1)) { + // alsb is smaller than blsb, or alsb is set and blsb is 0 + // in any case, alsb is guaranteed to be set here! + val sub1 = a(ai) + rs += sub1.size + rbm |= alsb + buffer(offset) = sub1; offset += 1 + // clear lowest remaining one bit in abm and increase the a index + abm &= ~alsb; ai += 1 + } else { + // blsb is smaller than alsb, or blsb is set and alsb is 0 + // in any case, blsb is guaranteed to be set here! + // clear lowest remaining one bit in bbm and increase the b index + bbm &= ~blsb; bi += 1 + } + } + if (rbm == 0) { + null + } else if (rs == this.size0) { + // if the result has the same number of elements as this, it must be identical to this, + // so we might as well return this + this + } else { + val length = offset - offset0 + if (length == 1 && !buffer(offset0).isInstanceOf[HashTrieSet[A]]) + buffer(offset0) + else { + val elems = new Array[HashSet[A]](length) + System.arraycopy(buffer, offset0, elems, 0, length) + new HashTrieSet[A](rbm, elems, rs) + } + } + case that: HashSetCollision1[A] => + // we remove the elements using removed0 so we can use the fact that we know the hash of all elements + // to be removed + @tailrec def removeAll(s:HashSet[A], r:ListSet[A]) : HashSet[A] = + if(r.isEmpty || (s eq null)) s + else removeAll(s.removed0(r.head, that.hash, level), r.tail) + removeAll(this, that.ks) + case _ => this + } + override def removed0(key: A, hash: Int, level: Int): HashSet[A] = { val index = (hash >>> level) & 0x1f val mask = (1 << index) @@ -353,7 +846,7 @@ object HashSet extends ImmutableSetFactory[HashSet] { val sub = elems(offset) val subNew = sub.removed0(key, hash, level + 5) if (sub eq subNew) this - else if (subNew.isEmpty) { + else if (subNew eq null) { val bitmapNew = bitmap ^ mask if (bitmapNew != 0) { val elemsNew = new Array[HashSet[A]](elems.length - 1) @@ -367,7 +860,7 @@ object HashSet extends ImmutableSetFactory[HashSet] { else new HashTrieSet(bitmapNew, elemsNew, sizeNew) } else - HashSet.empty[A] + null } else { val elemsNew = new Array[HashSet[A]](elems.length) Array.copy(elems, 0, elemsNew, 0, elems.length) @@ -526,6 +1019,10 @@ object HashSet extends ImmutableSetFactory[HashSet] { result } + // unsigned comparison + @inline private[this] def unsignedCompare(i: Int, j: Int) = + (i < j) ^ (i < 0) ^ (j < 0) + @SerialVersionUID(2L) private class SerializationProxy[A,B](@transient private var orig: HashSet[A]) extends Serializable { private def writeObject(out: java.io.ObjectOutputStream) { val s = orig.size diff --git a/src/library/scala/collection/immutable/NumericRange.scala b/src/library/scala/collection/immutable/NumericRange.scala index 249d76584d..f1ac161e9a 100644 --- a/src/library/scala/collection/immutable/NumericRange.scala +++ b/src/library/scala/collection/immutable/NumericRange.scala @@ -241,28 +241,79 @@ object NumericRange { else if (start == end) if (isInclusive) 1 else 0 else if (upward != posStep) 0 else { - val diff = num.minus(end, start) - val jumps = num.toLong(num.quot(diff, step)) - val remainder = num.rem(diff, step) - val longCount = jumps + ( - if (!isInclusive && zero == remainder) 0 else 1 - ) - - /* The edge cases keep coming. Since e.g. - * Long.MaxValue + 1 == Long.MinValue - * we do some more improbable seeming checks lest - * overflow turn up as an empty range. + /* We have to be frightfully paranoid about running out of range. + * We also can't assume that the numbers will fit in a Long. + * We will assume that if a > 0, -a can be represented, and if + * a < 0, -a+1 can be represented. We also assume that if we + * can't fit in Int, we can represent 2*Int.MaxValue+3 (at least). + * And we assume that numbers wrap rather than cap when they overflow. */ - // The second condition contradicts an empty result. - val isOverflow = longCount == 0 && num.lt(num.plus(start, step), end) == upward - - if (longCount > scala.Int.MaxValue || longCount < 0L || isOverflow) { - val word = if (isInclusive) "to" else "until" - val descr = List(start, word, end, "by", step) mkString " " - - throw new IllegalArgumentException(descr + ": seqs cannot contain more than Int.MaxValue elements.") + // Check whether we can short-circuit by deferring to Int range. + val startint = num.toInt(start) + if (start == num.fromInt(startint)) { + val endint = num.toInt(end) + if (end == num.fromInt(endint)) { + val stepint = num.toInt(step) + if (step == num.fromInt(stepint)) { + return { + if (isInclusive) Range.inclusive(startint, endint, stepint).length + else Range (startint, endint, stepint).length + } + } + } + } + // If we reach this point, deferring to Int failed. + // Numbers may be big. + val one = num.one + val limit = num.fromInt(Int.MaxValue) + def check(t: T): T = + if (num.gt(t, limit)) throw new IllegalArgumentException("More than Int.MaxValue elements.") + else t + // If the range crosses zero, it might overflow when subtracted + val startside = num.signum(start) + val endside = num.signum(end) + num.toInt{ + if (startside*endside >= 0) { + // We're sure we can subtract these numbers. + // Note that we do not use .rem because of different conventions for Long and BigInt + val diff = num.minus(end, start) + val quotient = check(num.quot(diff, step)) + val remainder = num.minus(diff, num.times(quotient, step)) + if (!isInclusive && zero == remainder) quotient else check(num.plus(quotient, one)) + } + else { + // We might not even be able to subtract these numbers. + // Jump in three pieces: + // * start to -1 or 1, whichever is closer (waypointA) + // * one step, which will take us at least to 0 (ends at waypointB) + // * there to the end + val negone = num.fromInt(-1) + val startlim = if (posStep) negone else one + val startdiff = num.minus(startlim, start) + val startq = check(num.quot(startdiff, step)) + val waypointA = if (startq == zero) start else num.plus(start, num.times(startq, step)) + val waypointB = num.plus(waypointA, step) + check { + if (num.lt(waypointB, end) != upward) { + // No last piece + if (isInclusive && waypointB == end) num.plus(startq, num.fromInt(2)) + else num.plus(startq, one) + } + else { + // There is a last piece + val enddiff = num.minus(end,waypointB) + val endq = check(num.quot(enddiff, step)) + val last = if (endq == zero) waypointB else num.plus(waypointB, num.times(endq, step)) + // Now we have to tally up all the pieces + // 1 for the initial value + // startq steps to waypointA + // 1 step to waypointB + // endq steps to the end (one less if !isInclusive and last==end) + num.plus(startq, num.plus(endq, if (!isInclusive && last==end) one else num.fromInt(2))) + } + } + } } - longCount.toInt } } diff --git a/src/reflect/scala/reflect/macros/Aliases.scala b/src/reflect/scala/reflect/macros/Aliases.scala index cc92cd10c7..d2b878d081 100644 --- a/src/reflect/scala/reflect/macros/Aliases.scala +++ b/src/reflect/scala/reflect/macros/Aliases.scala @@ -40,10 +40,16 @@ trait Aliases { /** The type of tree modifiers. */ type Modifiers = universe.Modifiers - /** The type of compilation runs. */ + /** The type of compilation runs. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") type Run = universe.Run - /** The type of compilation units. */ + /** The type of compilation units. + * @see [[scala.reflect.macros.Enclosures]] + */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") type CompilationUnit = universe.CompilationUnit /** Expr wraps an abstract syntax tree and tags it with its type. */ diff --git a/src/reflect/scala/reflect/macros/Enclosures.scala b/src/reflect/scala/reflect/macros/Enclosures.scala index 5f248d25d3..1ced2e54c6 100644 --- a/src/reflect/scala/reflect/macros/Enclosures.scala +++ b/src/reflect/scala/reflect/macros/Enclosures.scala @@ -8,9 +8,21 @@ import scala.language.existentials // SI-6541 * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * * A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that exposes - * enclosing trees (method, class, compilation unit and currently compiled application), + * enclosing trees (method, class, compilation unit and currently compiled macro application), * the enclosing position of the macro expansion, as well as macros and implicits * that are currently in-flight. + * + * Starting from Scala 2.11.0, the APIs to get the trees enclosing by the current macro application are deprecated, + * and the reasons for that are two-fold. Firstly, we would like to move towards the philosophy of locally-expanded macros, + * as it has proven to be important for understanding of code. Secondly, within the current architecture of scalac, + * we are unable to have c.enclosingTree-style APIs working robustly. Required changes to the typechecker would greatly + * exceed the effort that we would like to expend on this feature given the existence of more pressing concerns at the moment. + * This is somewhat aligned with the overall evolution of macros during the 2.11 development cycle, where we played with + * `c.introduceTopLevel` and `c.introduceMember`, but at the end of the day decided to reject them. + * + * If you're relying on the now deprecated APIs, consider reformulating your macros in terms of completely local expansion + * and/or joining a discussion of a somewhat related potential language feature at [[https://groups.google.com/forum/#!topic/scala-debate/f4CLmYShX6Q]]. + * We also welcome questions and suggestions on our mailing lists, where we would be happy to further discuss this matter. */ trait Enclosures { self: blackbox.Context => @@ -40,46 +52,62 @@ trait Enclosures { def enclosingPosition: Position /** Tree that corresponds to the enclosing method, or EmptyTree if not applicable. + * @see [[scala.reflect.macros.Enclosures]] */ - @deprecated("Use enclosingDef instead, but be wary of changes in semantics", "2.10.1") + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def enclosingMethod: Tree /** Tree that corresponds to the enclosing class, or EmptyTree if not applicable. + * @see [[scala.reflect.macros.Enclosures]] */ - @deprecated("Use enclosingImpl instead, but be wary of changes in semantics", "2.10.1") + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def enclosingClass: Tree /** Tree that corresponds to the enclosing DefDef tree. * Throws `EnclosureException` if there's no such enclosing tree. + * @see [[scala.reflect.macros.Enclosures]] */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def enclosingDef: universe.DefDef /** Tree that corresponds to the enclosing Template tree. * Throws `EnclosureException` if there's no such enclosing tree. + * @see [[scala.reflect.macros.Enclosures]] */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def enclosingTemplate: universe.Template /** Tree that corresponds to the enclosing ImplDef tree (i.e. either ClassDef or ModuleDef). * Throws `EnclosureException` if there's no such enclosing tree. + * @see [[scala.reflect.macros.Enclosures]] */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def enclosingImpl: universe.ImplDef /** Tree that corresponds to the enclosing PackageDef tree. * Throws `EnclosureException` if there's no such enclosing tree. + * @see [[scala.reflect.macros.Enclosures]] */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def enclosingPackage: universe.PackageDef /** Compilation unit that contains this macro application. + * @see [[scala.reflect.macros.Enclosures]] */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def enclosingUnit: CompilationUnit /** Compilation run that contains this macro application. + * @see [[scala.reflect.macros.Enclosures]] */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def enclosingRun: Run /** Indicates than one of the enclosure methods failed to find a tree * of required type among enclosing trees. + * @see [[scala.reflect.macros.Enclosures]] */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") case class EnclosureException(expected: Class[_], enclosingTrees: List[Tree]) extends Exception(s"Couldn't find a tree of type $expected among enclosing trees $enclosingTrees") } diff --git a/src/reflect/scala/reflect/macros/Universe.scala b/src/reflect/scala/reflect/macros/Universe.scala index 297bac2999..d84e6aa737 100644 --- a/src/reflect/scala/reflect/macros/Universe.scala +++ b/src/reflect/scala/reflect/macros/Universe.scala @@ -197,34 +197,44 @@ abstract class Universe extends scala.reflect.api.Universe { def capturedVariableType(vble: Symbol): Type /** The type of compilation runs. + * @see [[scala.reflect.macros.Enclosures]] * @template * @group Macros */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") type Run <: RunContextApi /** Compilation run uniquely identifies current invocation of the compiler * (e.g. can be used to implement per-run caches for macros) and provides access to units of work * of the invocation (currently processed unit of work and the list of all units). + * @see [[scala.reflect.macros.Enclosures]] * @group API */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") trait RunContextApi { /** Currently processed unit of work (a real or a virtual file). */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def currentUnit: CompilationUnit /** All units of work comprising this compilation run. */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def units: Iterator[CompilationUnit] } /** The type of compilation units. + * @see [[scala.reflect.macros.Enclosures]] * @template * @group Macros */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") type CompilationUnit <: CompilationUnitContextApi /** Compilation unit describes a unit of work of the compilation run. * It provides such information as file name, textual representation of the unit and the underlying AST. + * @see [[scala.reflect.macros.Enclosures]] * @group API */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") trait CompilationUnitContextApi { /** Source file corresponding to this compilation unit. * @@ -235,9 +245,11 @@ abstract class Universe extends scala.reflect.api.Universe { * It should not be used unless you know what you are doing. In subsequent releases, this API will be refined * and exposed as a part of scala.reflect.api. */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def source: scala.reflect.internal.util.SourceFile /** The AST that corresponds to this compilation unit. */ + @deprecated("c.enclosingTree-style APIs are now deprecated; consult the scaladoc for more information", "2.11.0") def body: Tree } } diff --git a/test/files/neg/dotless-targs.check b/test/files/neg/dotless-targs.check new file mode 100644 index 0000000000..4aab939f61 --- /dev/null +++ b/test/files/neg/dotless-targs.check @@ -0,0 +1,4 @@ +dotless-targs.scala:2: error: type application is not allowed for postfix operators + def f1 = "f1" isInstanceOf[String] // not ok + ^ +one error found diff --git a/test/files/neg/dotless-targs.scala b/test/files/neg/dotless-targs.scala new file mode 100644 index 0000000000..eff63cbec4 --- /dev/null +++ b/test/files/neg/dotless-targs.scala @@ -0,0 +1,5 @@ +class A { + def f1 = "f1" isInstanceOf[String] // not ok + def f2 = "f2".isInstanceOf[String] // ok + def f3 = "f3" toList // ok +} diff --git a/test/files/neg/t8157.check b/test/files/neg/t8157.check new file mode 100644 index 0000000000..9a21a49a07 --- /dev/null +++ b/test/files/neg/t8157.check @@ -0,0 +1,4 @@ +t8157.scala:1: error: in object Test, multiple overloaded alternatives of method foo define default arguments. +object Test { + ^ +one error found diff --git a/test/files/neg/t8157.scala b/test/files/neg/t8157.scala new file mode 100644 index 0000000000..462d4fa3f1 --- /dev/null +++ b/test/files/neg/t8157.scala @@ -0,0 +1,4 @@ +object Test { + def foo(printer: Any, question: => String, show: Boolean = false)(op: => Any): Any = ??? + def foo[T](question: => String, show: Boolean)(op: => Any = ()): Any = ??? +} diff --git a/test/files/pos/bcode_throw_null/TN.scala b/test/files/pos/bcode_throw_null/TN.scala new file mode 100644 index 0000000000..ed38b59bae --- /dev/null +++ b/test/files/pos/bcode_throw_null/TN.scala @@ -0,0 +1,7 @@ +object TN { + + def pre1(b: Boolean) { + println(if (b) 1 else throw null) + } + +} diff --git a/test/files/pos/dotless-targs.scala b/test/files/pos/dotless-targs.scala new file mode 100644 index 0000000000..8c0e244e4e --- /dev/null +++ b/test/files/pos/dotless-targs.scala @@ -0,0 +1,9 @@ +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def g1: Char = "g1" toList 0 + def g2: Char = "g2" apply 1 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) +} diff --git a/test/files/pos/overzealous-assert-genbcode.scala b/test/files/pos/overzealous-assert-genbcode.scala new file mode 100644 index 0000000000..ddd70b0c44 --- /dev/null +++ b/test/files/pos/overzealous-assert-genbcode.scala @@ -0,0 +1,10 @@ +object Test { + + def main(args: Array[String]) { + args(0) match { + case a: String => while(a == null) {} + } + } + +} + diff --git a/test/files/run/t6253a.scala b/test/files/run/t6253a.scala new file mode 100644 index 0000000000..efa3230df6 --- /dev/null +++ b/test/files/run/t6253a.scala @@ -0,0 +1,64 @@ +import scala.collection.immutable.HashSet + +object Test extends App { + + var hashCount = 0 + + /** + * A key that produces lots of hash collisions, to exercise the part of the code that deals with those + */ + case class Collision(value: Int) { + + override def hashCode = { + // we do not check hash counts for Collision keys because ListSet.++ uses a mutable hashset internally, + // so when we have hash collisions, union will call key.hashCode. + // hashCount += 1 + value / 5 + } + } + + /** + * A key that is identical to int other than that it counts hashCode invocations + */ + case class HashCounter(value: Int) { + + override def hashCode = { + hashCount += 1 + value + } + } + + def testUnion[T](sizes: Seq[Int], offsets: Seq[Double], keyType: String, mkKey: Int => T) { + for { + i <- sizes + o <- offsets + } { + val e = HashSet.empty[T] + val j = (i * o).toInt + // create two sets of size i with overlap o + val a = e ++ (0 until i).map(mkKey) + require(a.size == i, s"Building HashSet of size $i failed. Key type $keyType.") + val b = e ++ (j until (i + j)).map(mkKey) + require(b.size == i, s"Building HashSet of size $i failed. Key type $keyType.") + val as = e ++ (0 until j).map(mkKey) + require(as.size == j, s"Building HashSet of size $j failed. Key type $keyType.") + val hashCount0 = hashCount + val u = a union b + require(hashCount == hashCount0, s"key.hashCode should not be called, but has been called ${hashCount - hashCount0} times. Key type $keyType.") + require(u == (a union scala.collection.mutable.HashSet(b.toSeq: _*)), s"Operation must still work for other sets!") + require(u.size == i + j, s"Expected size ${i+j}. Real size ${u.size}. Key type $keyType.") + for (x <- 0 until i + j) + require(u.contains(mkKey(x)), s"Key type $keyType. Set (0 until ${i + j}) should contain $x but does not.") + val a_as = a union as + val as_a = as union a + require((a_as eq a) || (a_as eq as), s"No structural sharing in a union as. Key type $keyType, a=(0 until $i) as=(0 until $j)") + require((as_a eq a) || (as_a eq as), s"No structural sharing in as union a. Key type $keyType, a=(0 until $i) as=(0 until $j)") + } + } + + val sizes = Seq(1, 10, 100, 1000, 10000, 100000) + val offsets = Seq(0.0, 0.25, 0.5, 0.75, 1.0) + testUnion(sizes, offsets, "int", identity[Int]) + testUnion(sizes, offsets, "hashcounter", HashCounter.apply) + testUnion(sizes, offsets, "collision", Collision.apply) +} diff --git a/test/files/run/t6253b.scala b/test/files/run/t6253b.scala new file mode 100644 index 0000000000..9cbfefd49e --- /dev/null +++ b/test/files/run/t6253b.scala @@ -0,0 +1,62 @@ +import scala.collection.immutable.HashSet + +object Test extends App { + + var hashCount = 0 + + /** + * A key that produces lots of hash collisions, to exercise the part of the code that deals with those + */ + case class Collision(value: Int) { + + override def hashCode = { + hashCount += 1 + value / 5 + } + } + + /** + * A key that is identical to int other than that it counts hashCode invocations + */ + case class HashCounter(value: Int) { + + override def hashCode = { + hashCount += 1 + value + } + } + + def testIntersect[T](sizes: Seq[Int], offsets: Seq[Double], keyType: String, mkKey: Int => T) { + for { + i <- sizes + o <- offsets + } { + val e = HashSet.empty[T] + val j = (i * o).toInt + // create two sets of size i with overlap o + val a = e ++ (0 until i).map(mkKey) + require(a.size == i, s"Building HashSet of size $i failed. Key type $keyType.") + val b = e ++ (j until (i + j)).map(mkKey) + require(b.size == i, s"Building HashSet of size $i failed. Key type $keyType.") + val as = e ++ (0 until j).map(mkKey) + require(as.size == j, s"Building HashSet of size $j failed. Key type $keyType.") + val hashCount0 = hashCount + val u = a intersect b + require(hashCount == hashCount0, s"key.hashCode should not be called, but has been called ${hashCount - hashCount0} times. Key type $keyType.") + require(u == (a intersect scala.collection.mutable.HashSet(b.toSeq: _*)), s"Operation must still work for other sets!") + require(u.size == i - j, s"Expected size ${i + j}. Real size ${u.size}. Key type $keyType.") + for (x <- j until i) + require(u.contains(mkKey(x)), s"Key type $keyType. Set (0 until ${i + j}) should contain $x but does not.") + val a_as = a intersect as + val as_a = as intersect a + require((a_as eq as) || (a_as eq a), s"No structural sharing in a intersect as. Key type $keyType, a=(0 until $i) as=(0 until $j)") + require((as_a eq as) || (as_a eq a), s"No structural sharing in as intersect a. Key type $keyType, a=(0 until $i) as=(0 until $j)") + } + } + + val sizes = Seq(1, 10, 100, 1000, 10000, 100000) + val offsets = Seq(0.0, 0.25, 0.5, 0.75, 1.0) + testIntersect(sizes, offsets, "int", identity[Int]) + testIntersect(sizes, offsets, "hashcounter", HashCounter.apply) + testIntersect(sizes, offsets, "collision", Collision.apply) +} diff --git a/test/files/run/t6253c.scala b/test/files/run/t6253c.scala new file mode 100644 index 0000000000..71dfe1473e --- /dev/null +++ b/test/files/run/t6253c.scala @@ -0,0 +1,63 @@ +import scala.collection.immutable.HashSet + +object Test extends App { + + var hashCount = 0 + + /** + * A key that produces lots of hash collisions, to exercise the part of the code that deals with those + */ + case class Collision(value: Int) { + + override def hashCode = { + hashCount += 1 + value / 5 + } + } + + /** + * A key that is identical to int other than that it counts hashCode invocations + */ + case class HashCounter(value: Int) { + + override def hashCode = { + hashCount += 1 + value + } + } + + def testDiff[T](sizes: Seq[Int], offsets: Seq[Double], keyType: String, mkKey: Int => T) { + for { + i <- sizes + o <- offsets + } { + val e = HashSet.empty[T] + val j = (i * o).toInt + // create two sets of size i with overlap o + val a = e ++ (0 until i).map(mkKey) + require(a.size == i, s"Building HashSet of size $i failed. Key type $keyType.") + val b = e ++ (j until (i + j)).map(mkKey) + require(b.size == i, s"Building HashSet of size $i failed. Key type $keyType.") + val as = e ++ (0 until j).map(mkKey) + require(as.size == j, s"Building HashSet of size $j failed. Key type $keyType.") + val hashCount0 = hashCount + val u = a diff b + require(hashCount == hashCount0, s"key.hashCode should not be called, but has been called ${hashCount - hashCount0} times. Key type $keyType.") + require(u == (a diff scala.collection.mutable.HashSet(b.toSeq: _*)), s"Operation must still work for other sets!") + require(u.size == j, s"Expected size $j. Real size ${u.size}. Key type $keyType.") + for (x <- 0 until j) + require(u.contains(mkKey(x)), s"Key type $keyType. Set (0 until ${i + j}) should contain $x but does not.") + require((as intersect b).isEmpty) + val b_as = b diff as + val as_b = as diff b + require((b_as eq b) || (b_as eq as), s"No structural sharing in b diff as. Key type $keyType, b=($j until ${i + j}) as=(0 until $j)") + require((as_b eq b) || (as_b eq as), s"No structural sharing in as diff b. Key type $keyType, b=($j until ${i + j}) as=(0 until $j)") + } + } + + val sizes = Seq(1, 10, 100, 1000, 10000, 100000) + val offsets = Seq(0.0, 0.25, 0.5, 0.75, 1.0) + testDiff(sizes, offsets, "int", identity[Int]) + testDiff(sizes, offsets, "hashCounter", HashCounter.apply) + testDiff(sizes, offsets, "collision", Collision.apply) +} diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 6fb05ff9a4..38fbfa9f7f 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -224,4 +224,9 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { property("SI-8009") = test { q"`foo`".asInstanceOf[reflect.internal.SymbolTable#Ident].isBackquoted } + + property("SI-8148") = test { + val q"($a, $b) => $_" = q"_ + _" + assert(a.name != b.name) + } } diff --git a/test/junit/scala/collection/NumericRangeTest.scala b/test/junit/scala/collection/NumericRangeTest.scala new file mode 100644 index 0000000000..0260723b9d --- /dev/null +++ b/test/junit/scala/collection/NumericRangeTest.scala @@ -0,0 +1,123 @@ +package scala.collection.immutable + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import scala.math._ +import scala.util._ + +/* Tests various maps by making sure they all agree on the same answers. */ +@RunWith(classOf[JUnit4]) +class RangeConsistencyTest { + def r2nr[T: Integral]( + r: Range, puff: T, stride: T, check: (T,T) => Boolean, bi: T => BigInt + ): List[(BigInt,Try[Int])] = { + val num = implicitly[Integral[T]] + import num._ + val one = num.one + + if (!check(puff, fromInt(r.start))) return Nil + val start = puff * fromInt(r.start) + val sp1 = start + one + val sn1 = start - one + + if (!check(puff, fromInt(r.end))) return Nil + val end = puff * fromInt(r.end) + val ep1 = end + one + val en1 = end - one + + if (!check(stride, fromInt(r.step))) return Nil + val step = stride * fromInt(r.step) + + def NR(s: T, e: T, i: T) = { + val delta = (bi(e) - bi(s)).abs - (if (r.isInclusive) 0 else 1) + val n = if (r.length == 0) BigInt(0) else delta / bi(i).abs + 1 + if (r.isInclusive) { + (n, Try(NumericRange.inclusive(s,e,i).length)) + } + else { + (n, Try(NumericRange(s,e,i).length)) + } + } + + List(NR(start, end, step)) ::: + (if (sn1 < start) List(NR(sn1, end, step)) else Nil) ::: + (if (start < sp1) List(NR(sp1, end, step)) else Nil) ::: + (if (en1 < end) List(NR(start, en1, step)) else Nil) ::: + (if (end < ep1) List(NR(start, ep1, step)) else Nil) + } + + // Motivated by SI-4370: Wrong result for Long.MinValue to Long.MaxValue by Int.MaxValue + @Test + def rangeChurnTest() { + val rn = new Random(4370) + for (i <- 0 to 10000) { control.Breaks.breakable { + val start = rn.nextInt + val end = rn.nextInt + val step = rn.nextInt(4) match { + case 0 => 1 + case 1 => -1 + case 2 => (rn.nextInt(11)+2)*(2*rn.nextInt(2)+1) + case 3 => var x = rn.nextInt; while (x==0) x = rn.nextInt; x + } + val r = if (rn.nextBoolean) Range.inclusive(start, end, step) else Range(start, end, step) + + try { r.length } + catch { case iae: IllegalArgumentException => control.Breaks.break } + + val lpuff = rn.nextInt(4) match { + case 0 => 1L + case 1 => rn.nextInt(11)+2L + case 2 => 1L << rn.nextInt(60) + case 3 => math.max(1L, math.abs(rn.nextLong)) + } + val lstride = rn.nextInt(4) match { + case 0 => lpuff + case 1 => 1L + case 2 => 1L << rn.nextInt(60) + case 3 => math.max(1L, math.abs(rn.nextLong)) + } + val lr = r2nr[Long]( + r, lpuff, lstride, + (a,b) => { val x = BigInt(a)*BigInt(b); x.isValidLong }, + x => BigInt(x) + ) + + lr.foreach{ case (n,t) => assert( + t match { + case Failure(_) => n > Int.MaxValue + case Success(m) => n == m + }, + (r.start, r.end, r.step, r.isInclusive, lpuff, lstride, n, t) + )} + + val bipuff = rn.nextInt(3) match { + case 0 => BigInt(1) + case 1 => BigInt(rn.nextLong) + Long.MaxValue + 2 + case 2 => BigInt("1" + "0"*(rn.nextInt(100)+1)) + } + val bistride = rn.nextInt(3) match { + case 0 => bipuff + case 1 => BigInt(1) + case 2 => BigInt("1" + "0"*(rn.nextInt(100)+1)) + } + val bir = r2nr[BigInt](r, bipuff, bistride, (a,b) => true, identity) + + bir.foreach{ case (n,t) => assert( + t match { + case Failure(_) => n > Int.MaxValue + case Success(m) => n == m + }, + (r.start, r.end, r.step, r.isInclusive, bipuff, bistride, n, t) + )} + }} + } + + @Test + def testSI4370() { assert{ + Try((Long.MinValue to Long.MaxValue by Int.MaxValue).length) match { + case Failure(iae: IllegalArgumentException) => true + case _ => false + } + }} +} |