diff options
Diffstat (limited to 'src')
40 files changed, 472 insertions, 408 deletions
diff --git a/src/compiler/scala/reflect/macros/contexts/Typers.scala b/src/compiler/scala/reflect/macros/contexts/Typers.scala index 4a1122b913..85204d0f1b 100644 --- a/src/compiler/scala/reflect/macros/contexts/Typers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Typers.scala @@ -13,7 +13,7 @@ trait Typers { /** * @see [[scala.tools.reflect.ToolBox.typeCheck]] */ - def typeCheck(tree: Tree, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { + def typecheck(tree: Tree, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { macroLogVerbose("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) val context = callsiteTyper.context val wrapper1 = if (!withImplicitViewsDisabled) (context.withImplicitsEnabled[Tree] _) else (context.withImplicitsDisabled[Tree] _) @@ -24,7 +24,7 @@ trait Typers { // typechecking uses silent anyways (e.g. in typedSelect), so you'll only waste your time // I'd advise fixing the root cause: finding why the context is not set to report errors // (also see reflect.runtime.ToolBoxes.typeCheckExpr for a workaround that might work for you) - wrapper(callsiteTyper.silent(_.typed(tree, pt), reportAmbiguousErrors = false) match { + wrapper(callsiteTyper.silent(_.typed(universe.duplicateAndKeepPositions(tree), pt), reportAmbiguousErrors = false) match { case universe.analyzer.SilentResultValue(result) => macroLogVerbose(result) result @@ -46,7 +46,7 @@ trait Typers { universe.analyzer.inferImplicit(tree, viewTpe, true, callsiteTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw TypecheckException(pos, msg)) } - def resetAllAttrs(tree: Tree): Tree = universe.resetAllAttrs(tree) + def resetAllAttrs(tree: Tree): Tree = universe.resetAllAttrs(universe.duplicateAndKeepPositions(tree)) - def resetLocalAttrs(tree: Tree): Tree = universe.resetLocalAttrs(tree) + def resetLocalAttrs(tree: Tree): Tree = universe.resetLocalAttrs(universe.duplicateAndKeepPositions(tree)) } diff --git a/src/compiler/scala/reflect/reify/Taggers.scala b/src/compiler/scala/reflect/reify/Taggers.scala index 0c7831b592..093c2bee22 100644 --- a/src/compiler/scala/reflect/reify/Taggers.scala +++ b/src/compiler/scala/reflect/reify/Taggers.scala @@ -65,13 +65,13 @@ abstract class Taggers { case _ => translatingReificationErrors(materializer) } - try c.typeCheck(result) + try c.typecheck(result) catch { case terr @ TypecheckException(pos, msg) => failTag(result, terr) } } def materializeExpr(universe: Tree, mirror: Tree, expr: Tree): Tree = { val result = translatingReificationErrors(c.reifyTree(universe, mirror, expr)) - try c.typeCheck(result) + try c.typecheck(result) catch { case terr @ TypecheckException(pos, msg) => failExpr(result, terr) } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 91db1bb92a..61ea9230a7 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -671,6 +671,7 @@ self => def isRawBar = isIdent && in.name == raw.BAR def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT + def isMacro = in.token == IDENTIFIER && in.name == nme.MACROkw def isLiteralToken(token: Token) = token match { case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | @@ -1038,6 +1039,8 @@ self => def identForType(): TypeName = ident().toTypeName def identForType(skipIt: Boolean): TypeName = ident(skipIt).toTypeName + def identOrMacro(): Name = if (isMacro) rawIdent() else ident() + def selector(t: Tree): Tree = { val point = in.offset //assert(t.pos.isDefined, t) @@ -2551,7 +2554,7 @@ self => } else { val nameOffset = in.offset - val name = ident() + val name = identOrMacro() funDefRest(start, nameOffset, mods, name) } } @@ -2584,7 +2587,7 @@ self => } else { if (in.token == EQUALS) { in.nextTokenAllow(nme.MACROkw) - if (in.token == IDENTIFIER && in.name == nme.MACROkw) { + if (isMacro) { in.nextToken() newmods |= Flags.MACRO } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 65bb52159e..87bd498ea1 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -204,8 +204,12 @@ trait Scanners extends ScannersCommon { val idx = name.start - kwOffset if (idx >= 0 && idx < kwArray.length) { token = kwArray(idx) - if (token == IDENTIFIER && allowIdent != name && emitIdentifierDeprecationWarnings) - deprecationWarning(name+" is now a reserved word; usage as an identifier is deprecated") + if (token == IDENTIFIER && allowIdent != name) { + if (name == nme.MACROkw) + syntaxError(name+" is now a reserved word; usage as an identifier is disallowed") + else if (emitIdentifierDeprecationWarnings) + deprecationWarning(name+" is now a reserved word; usage as an identifier is deprecated") + } } } } diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index c546c21d48..933a2f70a1 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -340,7 +340,19 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre else (true, adaptToType(tree, expectedTpe)) } + def adaptAndPostErase(tree: Tree, pt: Type): (Boolean, Tree) = { + val (needsAdapt, adaptedTree) = adapt(tree, pt) + val trans = postErasure.newTransformer(unit) + val postErasedTree = trans.atOwner(currentOwner)(trans.transform(adaptedTree)) // SI-8017 elimnates ErasedValueTypes + (needsAdapt, postErasedTree) + } + enteringPhase(currentRun.posterasurePhase) { + // e.g, in: + // class C(val a: Int) extends AnyVal; (x: Int) => new C(x) + // + // This type is: + // (x: Int)ErasedValueType(class C, Int) val liftedBodyDefTpe: MethodType = { val liftedBodySymbol = { val Apply(method, _) = originalFunction.body @@ -349,8 +361,14 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre liftedBodySymbol.info.asInstanceOf[MethodType] } val (paramNeedsAdaptation, adaptedParams) = (bridgeSyms zip liftedBodyDefTpe.params map {case (bridgeSym, param) => adapt(Ident(bridgeSym) setType bridgeSym.tpe, param.tpe)}).unzip - val body = Apply(gen.mkAttributedSelect(gen.mkAttributedThis(newClass), applyMethod.symbol), adaptedParams) setType applyMethod.symbol.tpe.resultType - val (needsReturnAdaptation, adaptedBody) = adapt(typer.typed(body), ObjectTpe) + // SI-8017 Before, this code used `applyMethod.symbol.info.resultType`. + // But that symbol doesn't have a type history that goes back before `delambdafy`, + // so we just see a plain `Int`, rather than `ErasedValueType(C, Int)`. + // This triggered primitive boxing, rather than value class boxing. + val resTp = liftedBodyDefTpe.finalResultType + val body = Apply(gen.mkAttributedSelect(gen.mkAttributedThis(newClass), applyMethod.symbol), adaptedParams) setType resTp + val (needsReturnAdaptation, adaptedBody) = adaptAndPostErase(body, ObjectTpe) + val needsBridge = (paramNeedsAdaptation contains true) || needsReturnAdaptation if (needsBridge) { val methDef = DefDef(bridgeMethSym, List(bridgeParams), adaptedBody) diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index e31211d321..b4329965fc 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -100,23 +100,36 @@ abstract class Flatten extends InfoTransform { /** Buffers for lifted out classes */ private val liftedDefs = perRunCaches.newMap[Symbol, ListBuffer[Tree]]() - override def transform(tree: Tree): Tree = { + override def transform(tree: Tree): Tree = postTransform { tree match { case PackageDef(_, _) => liftedDefs(tree.symbol.moduleClass) = new ListBuffer + super.transform(tree) case Template(_, _, _) if tree.symbol.isDefinedInPackage => liftedDefs(tree.symbol.owner) = new ListBuffer + super.transform(tree) + case ClassDef(_, _, _, _) if tree.symbol.isNestedClass => + // SI-5508 Ordering important. In `object O { trait A { trait B } }`, we want `B` to appear after `A` in + // the sequence of lifted trees in the enclosing package. Why does this matter? Currently, mixin + // needs to transform `A` first to a chance to create accessors for private[this] trait fields + // *before* it transforms inner classes that refer to them. This also fixes SI-6231. + // + // Alternative solutions + // - create the private[this] accessors eagerly in Namer (but would this cover private[this] fields + // added later phases in compilation?) + // - move the accessor creation to the Mixin info transformer + val liftedBuffer = liftedDefs(tree.symbol.enclosingTopLevelClass.owner) + val index = liftedBuffer.length + liftedBuffer.insert(index, super.transform(tree)) + EmptyTree case _ => + super.transform(tree) } - postTransform(super.transform(tree)) } private def postTransform(tree: Tree): Tree = { val sym = tree.symbol val tree1 = tree match { - case ClassDef(_, _, _, _) if sym.isNestedClass => - liftedDefs(sym.enclosingTopLevelClass.owner) += tree - EmptyTree case Select(qual, name) if sym.isStaticModule && !sym.isTopLevel => exitingFlatten { atPos(tree.pos) { @@ -134,7 +147,10 @@ abstract class Flatten extends InfoTransform { /** Transform statements and add lifted definitions to them. */ override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { val stats1 = super.transformStats(stats, exprOwner) - if (currentOwner.isPackageClass) stats1 ::: liftedDefs(currentOwner).toList + if (currentOwner.isPackageClass) { + val lifted = liftedDefs(currentOwner).toList + stats1 ::: lifted + } else stats1 } } diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 6da971338f..89f9cb4b06 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -1207,21 +1207,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val iface = toInterface(sym.owner.tpe).typeSymbol val ifaceGetter = sym getter iface - def si6231Restriction() { - // See SI-6231 comments in LamdaLift for ideas on how to lift the restriction. - val msg = sm"""Implementation restriction: local ${iface.fullLocationString} is unable to automatically capture the - |free variable ${sym} on behalf of ${currentClass}. You can manually assign it to a val inside the trait, - |and refer to that val in ${currentClass}. For more details, see SI-6231.""" - reporter.error(tree.pos, msg) - } - - if (ifaceGetter == NoSymbol) { - if (sym.isParamAccessor) { - si6231Restriction() - EmptyTree - } - else abort("No getter for " + sym + " in " + iface) - } + if (ifaceGetter == NoSymbol) abort("No getter for " + sym + " in " + iface) else typedPos(tree.pos)((qual DOT ifaceGetter)()) case Assign(Apply(lhs @ Select(qual, _), List()), rhs) => diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 1723c69180..c505d9dc5f 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -923,7 +923,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Return the specialized overload of `m`, in the given environment. */ private def specializedOverload(owner: Symbol, sym: Symbol, env: TypeEnv, nameSymbol: Symbol = NoSymbol): Symbol = { - val newFlags = (sym.flags | SPECIALIZED) & ~(DEFERRED | CASEACCESSOR) + val newFlags = (sym.flags | SPECIALIZED) & ~(DEFERRED | CASEACCESSOR | LAZY) // this method properly duplicates the symbol's info val specname = specializedName(nameSymbol orElse sym, env) ( sym.cloneSymbol(owner, newFlags, newName = specname) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index a18068f2e0..0d46a96b81 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -260,7 +260,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } def isBlackbox(expandee: Tree): Boolean = isBlackbox(dissectApplied(expandee).core.symbol) - def isBlackbox(macroDef: Symbol): Boolean = loadMacroImplBinding(macroDef).map(_.isBlackbox).getOrElse(false) + def isBlackbox(macroDef: Symbol): Boolean = { + val fastTrackBoxity = fastTrack.get(macroDef).map(_.isBlackbox) + val bindingBoxity = loadMacroImplBinding(macroDef).map(_.isBlackbox) + fastTrackBoxity orElse bindingBoxity getOrElse false + } def computeMacroDefTypeFromMacroImplRef(macroDdef: DefDef, macroImplRef: Tree): Type = { macroImplRef match { @@ -345,7 +349,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { new { val universe: self.global.type = self.global val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] - val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).original orElse expandeeTree + val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).original orElse duplicateAndKeepPositions(expandeeTree) val macroRole = universe.analyzer.macroExpanderAttachment(expandeeTree).role } with UnaffiliatedMacroContext { val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing) @@ -403,8 +407,8 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { val wrappedArgs = mapWithIndex(args)((arg, j) => { val fingerprint = implParams(min(j, implParams.length - 1)) fingerprint match { - case LiftedTyped => context.Expr[Nothing](arg)(TypeTag.Nothing) // TODO: SI-5752 - case LiftedUntyped => arg + case LiftedTyped => context.Expr[Nothing](arg.duplicate)(TypeTag.Nothing) // TODO: SI-5752 + case LiftedUntyped => arg.duplicate case _ => abort(s"unexpected fingerprint $fingerprint in $binding with paramss being $paramss " + s"corresponding to arg $arg in $argss") } @@ -634,7 +638,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } if (isBlackbox(expandee)) { - val expanded1 = atPos(enclosingMacroPosition.focus)(Typed(expanded0, TypeTree(innerPt))) + val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt))) typecheck("blackbox typecheck", expanded1, outerPt) } else { val expanded1 = expanded0 diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 39e259fdfd..86bb99e7fa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1132,7 +1132,7 @@ trait Namers extends MethodSynthesis { } } - addDefaultGetters(meth, vparamss, tparams, overriddenSymbol(methResTp)) + addDefaultGetters(meth, ddef, vparamss, tparams, overriddenSymbol(methResTp)) // fast track macros, i.e. macros defined inside the compiler, are hardcoded // hence we make use of that and let them have whatever right-hand side they need @@ -1174,7 +1174,12 @@ trait Namers extends MethodSynthesis { * typechecked, the corresponding param would not yet have the "defaultparam" * flag. */ - private def addDefaultGetters(meth: Symbol, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) { + private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) { + val DefDef(_, _, rtparams0, rvparamss0, _, _) = resetLocalAttrs(ddef.duplicate) + // having defs here is important to make sure that there's no sneaky tree sharing + // in methods with multiple default parameters + def rtparams = rtparams0.map(_.duplicate) + def rvparamss = rvparamss0.map(_.map(_.duplicate)) val methOwner = meth.owner val isConstr = meth.isConstructor val overridden = if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol @@ -1206,23 +1211,36 @@ trait Namers extends MethodSynthesis { // vparamss.foldLeft(Nil: List[List[ValDef]]) { (previous, vparams) => assert(!overrides || vparams.length == baseParamss.head.length, ""+ meth.fullName + ", "+ overridden.fullName) + val rvparams = rvparamss(previous.length) var baseParams = if (overrides) baseParamss.head else Nil - for (vparam <- vparams) { + map2(vparams, rvparams)((vparam, rvparam) => { val sym = vparam.symbol // true if the corresponding parameter of the base class has a default argument val baseHasDefault = overrides && baseParams.head.hasDefault if (sym.hasDefault) { - // generate a default getter for that argument + // Create a "default getter", i.e. a DefDef that will calculate vparam.rhs + // for those who are going to call meth without providing an argument corresponding to vparam. + // After the getter is created, a corresponding synthetic symbol is created and entered into the parent namer. + // + // In the ideal world, this DefDef would be a simple one-liner that just returns vparam.rhs, + // but in scalac things are complicated in two different ways. + // + // 1) Because the underlying language is quite sophisticated, we must allow for those sophistications in our getter. + // Namely: a) our getter has to copy type parameters from the associated method (or the associated class + // if meth is a constructor), because vparam.rhs might refer to one of them, b) our getter has to copy + // preceding value parameter lists from the associated method, because again vparam.rhs might refer to one of them. + // + // 2) Because we have already assigned symbols to type and value parameters that we have to copy, we must jump through + // hoops in order to destroy them and allow subsequent naming create new symbols for our getter. Previously this + // was done in an overly brutal way akin to resetAllAttrs, but now we utilize a resetLocalAttrs-based approach. + // Still far from ideal, but at least enables things like run/macro-default-params that were previously impossible. + val oflag = if (baseHasDefault) OVERRIDE else 0 val name = nme.defaultGetterName(meth.name, posCounter) - // Create trees for the defaultGetter. Uses tools from Unapplies.scala - var deftParams = tparams map copyUntyped[TypeDef] - val defvParamss = mmap(previous) { p => - // in the default getter, remove the default parameter - val p1 = atPos(p.pos.focus) { ValDef(p.mods &~ DEFAULTPARAM, p.name, p.tpt.duplicate, EmptyTree) } - UnTyper.traverse(p1) - p1 + var defTparams = rtparams + val defVparamss = mmap(rvparamss.take(previous.length)){ rvp => + copyValDef(rvp)(mods = rvp.mods &~ DEFAULTPARAM, rhs = EmptyTree) } val parentNamer = if (isConstr) { @@ -1244,7 +1262,8 @@ trait Namers extends MethodSynthesis { return // fix #3649 (prevent crash in erroneous source code) } } - deftParams = cdef.tparams map copyUntypedInvariant + val ClassDef(_, _, rtparams, _) = resetLocalAttrs(cdef.duplicate) + defTparams = rtparams.map(rt => copyTypeDef(rt)(mods = rt.mods &~ (COVARIANT | CONTRAVARIANT))) nmr } else ownerNamer getOrElse { @@ -1255,23 +1274,30 @@ trait Namers extends MethodSynthesis { nmr } - // If the parameter type mentions any type parameter of the method, let the compiler infer the - // return type of the default getter => allow "def foo[T](x: T = 1)" to compile. - // This is better than always using Wildcard for inferring the result type, for example in - // def f(i: Int, m: Int => Int = identity _) = m(i) - // if we use Wildcard as expected, we get "Nothing => Nothing", and the default is not usable. - val names = deftParams map { case TypeDef(_, name, _, _) => name } - val subst = new TypeTreeSubstituter(names contains _) - - val defTpt = subst(copyUntyped(vparam.tpt match { - // default getter for by-name params - case AppliedTypeTree(_, List(arg)) if sym.hasFlag(BYNAMEPARAM) => arg - case t => t - })) - val defRhs = copyUntyped(vparam.rhs) + val defTpt = + // don't mess with tpt's of case copy default getters, because assigning something other than TypeTree() + // will break the carefully orchestrated naming/typing logic that involves enterCopyMethod and caseClassCopyMeth + if (meth.isCaseCopy) TypeTree() + else { + // If the parameter type mentions any type parameter of the method, let the compiler infer the + // return type of the default getter => allow "def foo[T](x: T = 1)" to compile. + // This is better than always using Wildcard for inferring the result type, for example in + // def f(i: Int, m: Int => Int = identity _) = m(i) + // if we use Wildcard as expected, we get "Nothing => Nothing", and the default is not usable. + // TODO: this is a very brittle approach; I sincerely hope that Denys's research into hygiene + // will open the doors to a much better way of doing this kind of stuff + val tparamNames = defTparams map { case TypeDef(_, name, _, _) => name } + val eraseAllMentionsOfTparams = new TypeTreeSubstituter(tparamNames contains _) + eraseAllMentionsOfTparams(rvparam.tpt match { + // default getter for by-name params + case AppliedTypeTree(_, List(arg)) if sym.hasFlag(BYNAMEPARAM) => arg + case t => t + }) + } + val defRhs = rvparam.rhs val defaultTree = atPos(vparam.pos.focus) { - DefDef(Modifiers(paramFlagsToDefaultGetter(meth.flags)) | oflag, name, deftParams, defvParamss, defTpt, defRhs) + DefDef(Modifiers(paramFlagsToDefaultGetter(meth.flags)) | oflag, name, defTparams, defVparamss, defTpt, defRhs) } if (!isConstr) methOwner.resetFlag(INTERFACE) // there's a concrete member now @@ -1286,7 +1312,7 @@ trait Namers extends MethodSynthesis { } posCounter += 1 if (overrides) baseParams = baseParams.tail - } + }) if (overrides) baseParamss = baseParamss.tail previous :+ vparams } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index ec916719dc..95f2620061 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -954,162 +954,166 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case Object_eq | Object_ne | Object_== | Object_!= | Any_== | Any_!= => true case _ => false } - def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match { - case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) => - // Make sure the 'eq' or 'ne' method is the one in AnyRef. - def isReferenceOp = fn.symbol == Object_eq || fn.symbol == Object_ne - def isNew(tree: Tree) = tree match { - case Function(_, _) - | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true - case _ => false - } - def underlyingClass(tp: Type): Symbol = { - val sym = tp.widen.typeSymbol - if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi) - else sym - } - val actual = underlyingClass(args.head.tpe) - val receiver = underlyingClass(qual.tpe) - def onTrees[T](f: List[Tree] => T) = f(List(qual, args.head)) - def onSyms[T](f: List[Symbol] => T) = f(List(receiver, actual)) - - // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol` - def typesString = normalizeAll(qual.tpe.widen)+" and "+normalizeAll(args.head.tpe.widen) - - /* Symbols which limit the warnings we can issue since they may be value types */ - val isMaybeValue = Set[Symbol](AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass) - - // Whether def equals(other: Any) has known behavior: it is the default - // inherited from java.lang.Object, or it is a synthetically generated - // case equals. TODO - more cases are warnable if the target is a synthetic - // equals. - def isUsingWarnableEquals = { - val m = receiver.info.member(nme.equals_) - ((m == Object_equals) || (m == Any_equals) || isMethodCaseEquals(m)) - } - def isMethodCaseEquals(m: Symbol) = m.isSynthetic && m.owner.isCase - def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_)) - // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere. - def isUsingDefaultScalaOp = { - val s = fn.symbol - (s == Object_==) || (s == Object_!=) || (s == Any_==) || (s == Any_!=) - } - def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual) - - // Whether the operands+operator represent a warnable combo (assuming anyrefs) - // Looking for comparisons performed with ==/!= in combination with either an - // equals method inherited from Object or a case class synthetic equals (for - // which we know the logic.) - def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) - def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info) - def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass - def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass - def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass - def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) - def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass - def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass - // includes java.lang.Number if appropriate [SI-5779] - def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s) - def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s) - // used to short-circuit unrelatedTypes check if both sides are special - def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) - val nullCount = onSyms(_ filter (_ == NullClass) size) - def isNonsenseValueClassCompare = ( - !haveSubclassRelationship - && isUsingDefaultScalaOp - && isEitherValueClass - && !isCaseEquals - ) + /** Check the sensibility of using the given `equals` to compare `qual` and `other`. */ + private def checkSensibleEquals(pos: Position, qual: Tree, name: Name, sym: Symbol, other: Tree) = { + def isReferenceOp = sym == Object_eq || sym == Object_ne + def isNew(tree: Tree) = tree match { + case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true + case _ => false + } + def underlyingClass(tp: Type): Symbol = { + val sym = tp.widen.typeSymbol + if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi) + else sym + } + val actual = underlyingClass(other.tpe) + val receiver = underlyingClass(qual.tpe) + def onTrees[T](f: List[Tree] => T) = f(List(qual, other)) + def onSyms[T](f: List[Symbol] => T) = f(List(receiver, actual)) + + // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol` + def typesString = normalizeAll(qual.tpe.widen)+" and "+normalizeAll(other.tpe.widen) + + /* Symbols which limit the warnings we can issue since they may be value types */ + val isMaybeValue = Set[Symbol](AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass) + + // Whether def equals(other: Any) has known behavior: it is the default + // inherited from java.lang.Object, or it is a synthetically generated + // case equals. TODO - more cases are warnable if the target is a synthetic + // equals. + def isUsingWarnableEquals = { + val m = receiver.info.member(nme.equals_) + ((m == Object_equals) || (m == Any_equals) || isMethodCaseEquals(m)) + } + def isMethodCaseEquals(m: Symbol) = m.isSynthetic && m.owner.isCase + def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_)) + // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere. + def isUsingDefaultScalaOp = sym == Object_== || sym == Object_!= || sym == Any_== || sym == Any_!= + def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual) + + // Whether the operands+operator represent a warnable combo (assuming anyrefs) + // Looking for comparisons performed with ==/!= in combination with either an + // equals method inherited from Object or a case class synthetic equals (for + // which we know the logic.) + def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) + def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info) + def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass + def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass + def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass + def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) + def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass + def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass + // includes java.lang.Number if appropriate [SI-5779] + def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s) + def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s) + // used to short-circuit unrelatedTypes check if both sides are special + def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) + val nullCount = onSyms(_ filter (_ == NullClass) size) + def isNonsenseValueClassCompare = ( + !haveSubclassRelationship + && isUsingDefaultScalaOp + && isEitherValueClass + && !isCaseEquals + ) - def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { - val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) - unit.warning(pos, "comparing "+what+" using `"+name.decode+"' will always yield " + msg) - } - def nonSensible(pre: String, alwaysEqual: Boolean) = - nonSensibleWarning(pre+"values of types "+typesString, alwaysEqual) - def nonSensiblyEq() = nonSensible("", true) - def nonSensiblyNeq() = nonSensible("", false) - def nonSensiblyNew() = nonSensibleWarning("a fresh object", false) - - def unrelatedMsg = name match { - case nme.EQ | nme.eq => "never compare equal" - case _ => "always compare unequal" - } - def unrelatedTypes() = { - val weaselWord = if (isEitherValueClass) "" else " most likely" - unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") - } + // Have we already determined that the comparison is non-sensible? I mean, non-sensical? + var isNonSensible = false + + def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { + val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) + unit.warning(pos, s"comparing $what using `${name.decode}' will always yield $msg") + isNonSensible = true + } + def nonSensible(pre: String, alwaysEqual: Boolean) = + nonSensibleWarning(s"${pre}values of types $typesString", alwaysEqual) + def nonSensiblyEq() = nonSensible("", alwaysEqual = true) + def nonSensiblyNeq() = nonSensible("", alwaysEqual = false) + def nonSensiblyNew() = nonSensibleWarning("a fresh object", alwaysEqual = false) + + def unrelatedMsg = name match { + case nme.EQ | nme.eq => "never compare equal" + case _ => "always compare unequal" + } + def unrelatedTypes() = if (!isNonSensible) { + val weaselWord = if (isEitherValueClass) "" else " most likely" + unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") + } - if (nullCount == 2) // null == null + if (nullCount == 2) // null == null + nonSensiblyEq() + else if (nullCount == 1) { + if (onSyms(_ exists isPrimitiveValueClass)) // null == 5 + nonSensiblyNeq() + else if (onTrees( _ exists isNew)) // null == new AnyRef + nonSensiblyNew() + } + else if (isBoolean(receiver)) { + if (!isBoolean(actual) && !isMaybeValue(actual)) // true == 5 + nonSensiblyNeq() + } + else if (isUnit(receiver)) { + if (isUnit(actual)) // () == () nonSensiblyEq() - else if (nullCount == 1) { - if (onSyms(_ exists isPrimitiveValueClass)) // null == 5 - nonSensiblyNeq() - else if (onTrees( _ exists isNew)) // null == new AnyRef - nonSensiblyNew() - } - else if (isBoolean(receiver)) { - if (!isBoolean(actual) && !isMaybeValue(actual)) // true == 5 + else if (!isUnit(actual) && !isMaybeValue(actual)) // () == "abc" + nonSensiblyNeq() + } + else if (isNumeric(receiver)) { + if (!isNumeric(actual)) + if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc" nonSensiblyNeq() - } - else if (isUnit(receiver)) { - if (isUnit(actual)) // () == () - nonSensiblyEq() - else if (!isUnit(actual) && !isMaybeValue(actual)) // () == "abc" + } + else if (isWarnable && !isCaseEquals) { + if (isNew(qual)) // new X == y + nonSensiblyNew() + else if (isNew(other) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y + nonSensiblyNew() + else if (receiver.isEffectivelyFinal && !(receiver isSubClass actual) && !actual.isRefinementClass) { // object X, Y; X == Y + if (isEitherNullable) + nonSensible("non-null ", false) + else nonSensiblyNeq() } - else if (isNumeric(receiver)) { - if (!isNumeric(actual)) - if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc" - nonSensiblyNeq() + } + + // warn if one but not the other is a derived value class + // this is especially important to enable transitioning from + // regular to value classes without silent failures. + if (isNonsenseValueClassCompare) + unrelatedTypes() + // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean + else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { + // better to have lubbed and lost + def warnIfLubless(): Unit = { + val common = global.lub(List(actual.tpe, receiver.tpe)) + if (ObjectTpe <:< common) + unrelatedTypes() } - else if (isWarnable && !isCaseEquals) { - if (isNew(qual)) // new X == y - nonSensiblyNew() - else if (isNew(args.head) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y - nonSensiblyNew() - else if (receiver.isEffectivelyFinal && !(receiver isSubClass actual) && !actual.isRefinementClass) { // object X, Y; X == Y - if (isEitherNullable) - nonSensible("non-null ", false) - else - nonSensiblyNeq() + // warn if actual has a case parent that is not same as receiver's; + // if actual is not a case, then warn if no common supertype, as below + if (isCaseEquals) { + def thisCase = receiver.info.member(nme.equals_).owner + actual.info.baseClasses.find(_.isCase) match { + case Some(p) if p != thisCase => nonSensible("case class ", false) + case None => + // stronger message on (Some(1) == None) + //if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq() + //else + // if a class, it must be super to thisCase (and receiver) since not <: thisCase + if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq() + else if (!haveSubclassRelationship) warnIfLubless() + case _ => } } - - // warn if one but not the other is a derived value class - // this is especially important to enable transitioning from - // regular to value classes without silent failures. - if (isNonsenseValueClassCompare) - unrelatedTypes() - // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean - else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { - // better to have lubbed and lost - def warnIfLubless(): Unit = { - val common = global.lub(List(actual.tpe, receiver.tpe)) - if (ObjectTpe <:< common) - unrelatedTypes() - } - // warn if actual has a case parent that is not same as receiver's; - // if actual is not a case, then warn if no common supertype, as below - if (isCaseEquals) { - def thisCase = receiver.info.member(nme.equals_).owner - actual.info.baseClasses.find(_.isCase) match { - case Some(p) if p != thisCase => nonSensible("case class ", false) - case None => - // stronger message on (Some(1) == None) - //if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq() - //else - // if a class, it must be super to thisCase (and receiver) since not <: thisCase - if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq() - else if (!haveSubclassRelationship) warnIfLubless() - case _ => - } - } - // warn only if they have no common supertype below Object - else if (!haveSubclassRelationship) { - warnIfLubless() - } + // warn only if they have no common supertype below Object + else if (!haveSubclassRelationship) { + warnIfLubless() } + } + } + /** Sensibility check examines flavors of equals. */ + def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match { + case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) => + checkSensibleEquals(pos, qual, name, fn.symbol, args.head) case _ => } @@ -1526,7 +1530,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans transform(qual) case Apply(fn, args) => - // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability analyses in the pattern matcher + // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability + // analyses in the pattern matcher if (!inPattern) { checkImplicitViewOptionApply(tree.pos, fn, args) checkSensible(tree.pos, fn, args) diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 54c665fe56..14f47a00fd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -50,6 +50,10 @@ trait StdAttachments { case _ => false } + /** Returns the original tree of the macro expansion if the argument is a macro expansion or EmptyTree otherwise. + */ + def macroExpandee(tree: Tree): Tree = tree.attachments.get[MacroExpansionAttachment].map(_.expandee).getOrElse(EmptyTree) + /** After macro expansion is completed, links the expandee and the expansion result by annotating them both with a `MacroExpansionAttachment`. * The `expanded` parameter is of type `Any`, because macros can expand both into trees and into annotations. */ @@ -147,4 +151,18 @@ trait StdAttachments { * because someone has put MacroImplRefAttachment on it. */ def isMacroImplRef(tree: Tree): Boolean = tree.attachments.get[MacroImplRefAttachment.type].isDefined + + /** Since mkInvoke, the applyDynamic/selectDynamic/etc desugarer, is disconnected + * from typedNamedApply, the applyDynamicNamed argument rewriter, the latter + * doesn’t know whether it needs to apply the rewriting because the application + * has just been desugared or it needs to hold on because it’s already performed + * a desugaring on this tree. This has led to SI-8006. + * + * This attachment solves the problem by providing a means of communication + * between the two Dynamic desugarers, which solves the aforementioned issue. + */ + case object DynamicRewriteAttachment + def markDynamicRewrite(tree: Tree): Tree = tree.updateAttachment(DynamicRewriteAttachment) + def unmarkDynamicRewrite(tree: Tree): Tree = tree.removeAttachment[DynamicRewriteAttachment.type] + def isDynamicRewrite(tree: Tree): Boolean = tree.attachments.get[DynamicRewriteAttachment.type].isDefined } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 5e89440bc0..dbe85f4f5a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -56,16 +56,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper resetDocComments() } - object UnTyper extends Traverser { - override def traverse(tree: Tree) = { - if (tree.canHaveAttrs) { - tree.clearType() - if (tree.hasSymbolField) tree.symbol = NoSymbol - } - super.traverse(tree) - } - } - sealed abstract class SilentResult[+T] { @inline final def fold[U](none: => U)(f: T => U): U = this match { case SilentResultValue(value) => f(value) @@ -3369,7 +3359,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper (args exists isNamedArg) || // uses a named argument isNamedApplyBlock(fun)) { // fun was transformed to a named apply block => // integrate this application into the block - if (dyna.isApplyDynamicNamed(fun)) dyna.typedNamedApply(tree, fun, args, mode, pt) + if (dyna.isApplyDynamicNamed(fun) && isDynamicRewrite(fun)) dyna.typedNamedApply(tree, fun, args, mode, pt) else tryNamesDefaults } else { val tparams = context.extractUndetparams() @@ -3927,7 +3917,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper gen.mkTuple(List(CODE.LIT(""), arg)) } - val t = treeCopy.Apply(orig, fun, args map argToBinding) + val t = treeCopy.Apply(orig, unmarkDynamicRewrite(fun), args map argToBinding) wrapErrors(t, _.typed(t, mode, pt)) } @@ -3953,9 +3943,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = { debuglog(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") val treeInfo.Applied(treeSelection, _, _) = tree - def isDesugaredApply = treeSelection match { - case Select(`qual`, nme.apply) => true - case _ => false + def isDesugaredApply = { + val protoQual = macroExpandee(qual) orElse qual + treeSelection match { + case Select(`protoQual`, nme.apply) => true + case _ => false + } } acceptsApplyDynamicWithType(qual, name) map { tp => // If tp == NoType, pass only explicit type arguments to applyXXX. Not used at all @@ -3989,7 +3982,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val nameStringLit = atPos(treeSelection.pos.withStart(treeSelection.pos.point).makeTransparent) { Literal(Constant(name.decode)) } - atPos(qual.pos)(Apply(fun, List(nameStringLit))) + markDynamicRewrite(atPos(qual.pos)(Apply(fun, List(nameStringLit)))) case _ => setError(tree) } @@ -4247,7 +4240,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } val ids = for (p <- params) yield Ident(p.name) val selector1 = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) } - val body = treeCopy.Match(tree, selector1, cases) + // SI-8120 If we don't duplicate the cases, the original Match node will share trees with ones that + // receive symbols owned by this function. However if, after a silent mode session, we discard + // this Function and try a different approach (e.g. applying a view to the reciever) we end up + // with orphaned symbols which blows up far down the pipeline (or can be detected with -Ycheck:typer). + val body = treeCopy.Match(tree, selector1, (cases map duplicateAndKeepPositions).asInstanceOf[List[CaseDef]]) typed1(atPos(tree.pos) { Function(params, body) }, mode, pt) } } else diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index ed96f66ab8..ffac29b4b8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -43,12 +43,6 @@ trait Unapplies extends ast.TreeDSL { def unapply(tp: Type): Option[Symbol] = unapplyMember(tp).toOption } - def copyUntyped[T <: Tree](tree: T): T = - returning[T](tree.duplicate)(UnTyper traverse _) - - def copyUntypedInvariant(td: TypeDef): TypeDef = - copyTypeDef(copyUntyped(td))(mods = td.mods &~ (COVARIANT | CONTRAVARIANT)) - private def toIdent(x: DefTree) = Ident(x.name) setPos x.pos.focus private def classType(cdef: ClassDef, tparams: List[TypeDef]): Tree = { @@ -58,8 +52,15 @@ trait Unapplies extends ast.TreeDSL { } private def constrParamss(cdef: ClassDef): List[List[ValDef]] = { - val DefDef(_, _, _, vparamss, _, _) = treeInfo firstConstructor cdef.impl.body - mmap(vparamss)(copyUntyped[ValDef]) + val ClassDef(_, _, _, Template(_, _, body)) = resetLocalAttrs(cdef.duplicate) + val DefDef(_, _, _, vparamss, _, _) = treeInfo firstConstructor body + vparamss + } + + private def constrTparamsInvariant(cdef: ClassDef): List[TypeDef] = { + val ClassDef(_, _, tparams, _) = resetLocalAttrs(cdef.duplicate) + val tparamsInvariant = tparams.map(tparam => copyTypeDef(tparam)(mods = tparam.mods &~ (COVARIANT | CONTRAVARIANT))) + tparamsInvariant } /** The return value of an unapply method of a case class C[Ts] @@ -125,7 +126,7 @@ trait Unapplies extends ast.TreeDSL { /** The apply method corresponding to a case class */ def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef): DefDef = { - val tparams = cdef.tparams map copyUntypedInvariant + val tparams = constrTparamsInvariant(cdef) val cparamss = constrParamss(cdef) def classtpe = classType(cdef, tparams) atPos(cdef.pos.focus)( @@ -141,7 +142,7 @@ trait Unapplies extends ast.TreeDSL { /** The unapply method corresponding to a case class */ def caseModuleUnapplyMeth(cdef: ClassDef): DefDef = { - val tparams = cdef.tparams map copyUntypedInvariant + val tparams = constrTparamsInvariant(cdef) val method = constrParamss(cdef) match { case xs :: _ if xs.nonEmpty && isRepeatedParamType(xs.last.tpt) => nme.unapplySeq case _ => nme.unapply @@ -196,7 +197,7 @@ trait Unapplies extends ast.TreeDSL { treeCopy.ValDef(vd, Modifiers(flags), vd.name, tpt, rhs) } - val tparams = cdef.tparams map copyUntypedInvariant + val tparams = constrTparamsInvariant(cdef) val paramss = classParamss match { case Nil => Nil case ps :: pss => diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala index dd92e14602..bb0bbd79a3 100644 --- a/src/compiler/scala/tools/reflect/FastTrack.scala +++ b/src/compiler/scala/tools/reflect/FastTrack.scala @@ -24,10 +24,12 @@ trait FastTrack { new { val c: c0.type = c0 } with MacroImplementations private implicit def context2quasiquote(c0: MacroContext): QuasiquoteImpls { val c: c0.type } = new { val c: c0.type = c0 } with QuasiquoteImpls - private def make(sym: Symbol)(pf: PartialFunction[Applied, MacroContext => Tree]) = - sym -> new FastTrackEntry(pf) + private def makeBlackbox(sym: Symbol)(pf: PartialFunction[Applied, MacroContext => Tree]) = + sym -> new FastTrackEntry(pf, isBlackbox = true) + private def makeWhitebox(sym: Symbol)(pf: PartialFunction[Applied, MacroContext => Tree]) = + sym -> new FastTrackEntry(pf, isBlackbox = false) - final class FastTrackEntry(pf: PartialFunction[Applied, MacroContext => Tree]) extends (MacroArgs => Any) { + final class FastTrackEntry(pf: PartialFunction[Applied, MacroContext => Tree], val isBlackbox: Boolean) extends (MacroArgs => Any) { def validate(tree: Tree) = pf isDefinedAt Applied(tree) def apply(margs: MacroArgs): margs.c.Expr[Nothing] = { val MacroArgs(c, _) = margs @@ -42,14 +44,14 @@ trait FastTrack { val runDefinitions = currentRun.runDefinitions import runDefinitions._ Map[Symbol, FastTrackEntry]( - make( materializeClassTag) { case Applied(_, ttag :: Nil, _) => _.materializeClassTag(ttag.tpe) }, - make( materializeWeakTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = false) }, - make( materializeTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = true) }, - make( ApiUniverseReify) { case Applied(_, ttag :: Nil, (expr :: _) :: _) => c => c.materializeExpr(c.prefix.tree, EmptyTree, expr) }, - make( StringContext_f) { case Applied(Select(Apply(_, ps), _), _, args) => c => c.macro_StringInterpolation_f(ps, args.flatten, c.expandee.pos) }, - make(ReflectRuntimeCurrentMirror) { case _ => c => currentMirror(c).tree }, - make( QuasiquoteClass_api_apply) { case _ => _.expandQuasiquote }, - make(QuasiquoteClass_api_unapply) { case _ => _.expandQuasiquote } + makeBlackbox( materializeClassTag) { case Applied(_, ttag :: Nil, _) => _.materializeClassTag(ttag.tpe) }, + makeBlackbox( materializeWeakTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = false) }, + makeBlackbox( materializeTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = true) }, + makeBlackbox( ApiUniverseReify) { case Applied(_, ttag :: Nil, (expr :: _) :: _) => c => c.materializeExpr(c.prefix.tree, EmptyTree, expr) }, + makeBlackbox( StringContext_f) { case Applied(Select(Apply(_, ps), _), _, args) => c => c.macro_StringInterpolation_f(ps, args.flatten, c.expandee.pos) }, + makeBlackbox(ReflectRuntimeCurrentMirror) { case _ => c => currentMirror(c).tree }, + makeWhitebox( QuasiquoteClass_api_apply) { case _ => _.expandQuasiquote }, + makeWhitebox(QuasiquoteClass_api_unapply) { case _ => _.expandQuasiquote } ) } } diff --git a/src/compiler/scala/tools/reflect/ToolBox.scala b/src/compiler/scala/tools/reflect/ToolBox.scala index be22003114..236b868842 100644 --- a/src/compiler/scala/tools/reflect/ToolBox.scala +++ b/src/compiler/scala/tools/reflect/ToolBox.scala @@ -21,6 +21,12 @@ trait ToolBox[U <: scala.reflect.api.Universe] { */ def frontEnd: FrontEnd + /** @see `Typers.typecheck` + */ + @deprecated("Use `tb.typecheck` instead", "2.11.0") + def typeCheck(tree: u.Tree, pt: u.Type = u.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = + typecheck(tree, pt, silent, withImplicitViewsDisabled, withMacrosDisabled) + /** Typechecks a tree using this ToolBox. * This populates symbols and types of the tree and possibly transforms it to reflect certain desugarings. * @@ -35,7 +41,7 @@ trait ToolBox[U <: scala.reflect.api.Universe] { * `withImplicitViewsDisabled` recursively prohibits implicit views (though, implicit vals will still be looked up and filled in), default value is false * `withMacrosDisabled` recursively prohibits macro expansions and macro-based implicits, default value is false */ - def typeCheck(tree: u.Tree, pt: u.Type = u.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree + def typecheck(tree: u.Tree, pt: u.Type = u.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree /** Infers an implicit value of the expected type `pt` in top-level context. * Optional `pos` parameter provides a position that will be associated with the implicit search. @@ -47,7 +53,7 @@ trait ToolBox[U <: scala.reflect.api.Universe] { * If `silent` is false, `TypeError` will be thrown in case of an inference error. * If `silent` is true, the typecheck is silent and will return `EmptyTree` if an error occurs. * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. - * Unlike in `typeCheck`, `silent` is true by default. + * Unlike in `typecheck`, `silent` is true by default. */ def inferImplicitValue(pt: u.Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: u.Position = u.NoPosition): u.Tree @@ -61,7 +67,7 @@ trait ToolBox[U <: scala.reflect.api.Universe] { * If `silent` is false, `TypeError` will be thrown in case of an inference error. * If `silent` is true, the typecheck is silent and will return `EmptyTree` if an error occurs. * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. - * Unlike in `typeCheck`, `silent` is true by default. + * Unlike in `typecheck`, `silent` is true by default. */ def inferImplicitView(tree: u.Tree, from: u.Type, to: u.Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: u.Position = u.NoPosition): u.Tree diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index e94b7725cd..af13b7d0ba 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -171,7 +171,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => unwrapped } - def typeCheck(expr: Tree, pt: Type, silent: Boolean, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean): Tree = + def typecheck(expr: Tree, pt: Type, silent: Boolean, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean): Tree = transformDuringTyper(expr, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)( (currentTyper, expr) => { trace("typing (implicit views = %s, macros = %s): ".format(!withImplicitViewsDisabled, !withMacrosDisabled))(showAttributed(expr, true, true, settings.Yshowsymkinds.value)) @@ -348,7 +348,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } } - def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = withCompilerApi { compilerApi => + def typecheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = withCompilerApi { compilerApi => import compilerApi._ if (compiler.settings.verbose) println("importing "+tree+", expectedType = "+expectedType) @@ -356,7 +356,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val cexpectedType: compiler.Type = importer.importType(expectedType) if (compiler.settings.verbose) println("typing "+ctree+", expectedType = "+expectedType) - val ttree: compiler.Tree = compiler.typeCheck(ctree, cexpectedType, silent = silent, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled) + val ttree: compiler.Tree = compiler.typecheck(ctree, cexpectedType, silent = silent, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled) val uttree = exporter.importTree(ttree) uttree } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 6d7aafe266..87ab52414c 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -32,8 +32,16 @@ trait Reifiers { self: Quasiquotes => var nameMap = collection.mutable.HashMap.empty[Name, Set[TermName]].withDefault { _ => Set() } /** Wraps expressions into: - * a sequence of nested withFreshTermName/withFreshTypeName calls which are required - * to force regeneration of randomly generated names on every evaluation of quasiquote. + * a block which starts with a sequence of vals that correspond + * to fresh names that has to be created at evaluation of the quasiquote + * and ends with reified tree: + * + * { + * val name$1: universe.TermName = universe.build.freshTermName(prefix1) + * ... + * val name$N: universe.TermName = universe.build.freshTermName(prefixN) + * tree + * } * * Wraps patterns into: * a call into anonymous class' unapply method required by unapply macro expansion: @@ -50,15 +58,18 @@ trait Reifiers { self: Quasiquotes => */ def wrap(tree: Tree) = if (isReifyingExpressions) { - nameMap.foldLeft(tree) { - case (t, (origname, names)) => + val freshdefs = nameMap.iterator.map { + case (origname, names) => assert(names.size == 1) val FreshName(prefix) = origname - val ctor = TermName("withFresh" + (if (origname.isTermName) "TermName" else "TypeName")) - // q"$u.build.$ctor($prefix) { ${names.head} => $t }" - Apply(Apply(Select(Select(u, nme.build), ctor), List(Literal(Constant(prefix)))), - List(Function(List(ValDef(Modifiers(PARAM), names.head, TypeTree(), EmptyTree)), t))) - } + val nameTypeName = if (origname.isTermName) tpnme.TermName else tpnme.TypeName + val freshName = if (origname.isTermName) nme.freshTermName else nme.freshTypeName + // q"val ${names.head}: $u.$nameTypeName = $u.build.$freshName($prefix)" + ValDef(NoMods, names.head, Select(u, nameTypeName), + Apply(Select(Select(u, nme.build), freshName), Literal(Constant(prefix)) :: Nil)) + }.toList + // q"..$freshdefs; $tree" + SyntacticBlock(freshdefs :+ tree) } else { val freevars = holeMap.toList.map { case (name, _) => Ident(name) } val isVarPattern = tree match { case Bind(name, Ident(nme.WILDCARD)) => true case _ => false } diff --git a/src/library/scala/collection/convert/DecorateAsJava.scala b/src/library/scala/collection/convert/DecorateAsJava.scala index 498bdc5943..6658b6feea 100644 --- a/src/library/scala/collection/convert/DecorateAsJava.scala +++ b/src/library/scala/collection/convert/DecorateAsJava.scala @@ -287,7 +287,7 @@ trait DecorateAsJava { * will be visible via the Scala interface and vice versa. * * If the Scala `concurrent.Map` was previously obtained from an implicit or - * explicit call of `asConcurrentMap(java.util.concurrect.ConcurrentMap)` + * explicit call of `asConcurrentMap(java.util.concurrent.ConcurrentMap)` * then the original Java `ConcurrentMap` will be returned. * * @param m The Scala `concurrent.Map` to be converted. diff --git a/src/library/scala/collection/convert/WrapAsJava.scala b/src/library/scala/collection/convert/WrapAsJava.scala index b6ebf2ff06..9916fe9843 100644 --- a/src/library/scala/collection/convert/WrapAsJava.scala +++ b/src/library/scala/collection/convert/WrapAsJava.scala @@ -244,7 +244,7 @@ trait WrapAsJava { * will be visible via the Scala interface and vice versa. * * If the Scala `concurrent.Map` was previously obtained from an implicit or - * explicit call of `mapAsScalaConcurrentMap(java.util.concurrect.ConcurrentMap)` + * explicit call of `mapAsScalaConcurrentMap(java.util.concurrent.ConcurrentMap)` * then the original Java ConcurrentMap will be returned. * * @param m The Scala `concurrent.Map` to be converted. diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala index c3a6351336..fb0a34e64d 100644 --- a/src/library/scala/collection/immutable/HashMap.scala +++ b/src/library/scala/collection/immutable/HashMap.scala @@ -180,21 +180,6 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { override def get0(key: A, hash: Int, level: Int): Option[B] = if (hash == this.hash && key == this.key) Some(value) else None - // override def updated0[B1 >: B](key: A, hash: Int, level: Int, value: B1, kv: (A, B1)): HashMap[A, B1] = - // if (hash == this.hash && key == this.key) new HashMap1(key, hash, value, kv) - // else { - // var thatindex = (hash >>> level) & 0x1f - // var thisindex = (this.hash >>> level) & 0x1f - // if (hash != this.hash) { - // --new HashTrieMap[A,B1](level+5, this, new HashMap1(key, hash, value, kv)) - // val m = new HashTrieMap[A,B1](0,new Array[HashMap[A,B1]](0),0) // TODO: could save array alloc - // m.updated0(this.key, this.hash, level, this.value, this.kv).updated0(key, hash, level, value, kv) TODO and it will - // } else { - // 32-bit hash collision (rare, but not impossible) - // new HashMapCollision1(hash, ListMap.empty.updated(this.key,this.value).updated(key,value)) - // } - // } - private[collection] override def updated0[B1 >: B](key: A, hash: Int, level: Int, value: B1, kv: (A, B1), merger: Merger[A, B1]): HashMap[A, B1] = if (hash == this.hash && key == this.key ) { if (merger eq null) { @@ -283,24 +268,6 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { // assert(Integer.bitCount(bitmap) == elems.length) // assert(elems.length > 1 || (elems.length == 1 && elems(0).isInstanceOf[HashTrieMap[_,_]])) -/* - def this (level: Int, m1: HashMap1[A,B], m2: HashMap1[A,B]) = { - this(((m1.hash >>> level) & 0x1f) | ((m2.hash >>> level) & 0x1f), { - val idx1 = (m1.hash >>> level) & 0x1f - val idx2 = (m2.hash >>> level) & 0x1f - assert(idx1 != idx2, m1.hash + "==" + m2.hash + " at level " + level) // TODO - val elems = new Array[HashMap[A,B]](2) - if (idx1 < idx2) { - elems(0) = m1 - elems(1) = m2 - } else { - elems(0) = m2 - elems(1) = m1 - } - elems - }, 2) - } -*/ override def size = size0 override def get0(key: A, hash: Int, level: Int): Option[B] = { @@ -379,24 +346,6 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { final override def getElem(cc: AnyRef): (A, B) = cc.asInstanceOf[HashMap1[A, B]].ensurePair } -/* -def time(block: =>Unit) = { val t0 = System.nanoTime; block; println("elapsed: " + (System.nanoTime - t0)/1000000.0) } -var mOld = OldHashMap.empty[Int,Int] -var mNew = HashMap.empty[Int,Int] -time { for (i <- 0 until 100000) mOld = mOld.updated(i,i) } -time { for (i <- 0 until 100000) mOld = mOld.updated(i,i) } -time { for (i <- 0 until 100000) mOld = mOld.updated(i,i) } -time { for (i <- 0 until 100000) mNew = mNew.updated(i,i) } -time { for (i <- 0 until 100000) mNew = mNew.updated(i,i) } -time { for (i <- 0 until 100000) mNew = mNew.updated(i,i) } -time { mOld.iterator.foreach( p => ()) } -time { mOld.iterator.foreach( p => ()) } -time { mOld.iterator.foreach( p => ()) } -time { mNew.iterator.foreach( p => ()) } -time { mNew.iterator.foreach( p => ()) } -time { mNew.iterator.foreach( p => ()) } -*/ - override def foreach[U](f: ((A, B)) => U): Unit = { var i = 0 while (i < elems.length) { diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 0546c54826..9eaceccd9f 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -282,28 +282,10 @@ object HashSet extends ImmutableSetFactory[HashSet] { override def iterator = new TrieIterator[A](elems.asInstanceOf[Array[Iterable[A]]]) { final override def getElem(cc: AnyRef): A = cc.asInstanceOf[HashSet1[A]].key } -/* - -def time(block: =>Unit) = { val t0 = System.nanoTime; block; println("elapsed: " + (System.nanoTime - t0)/1000000.0) } -var mOld = OldHashSet.empty[Int] -var mNew = HashSet.empty[Int] -time { for (i <- 0 until 100000) mOld = mOld + i } -time { for (i <- 0 until 100000) mOld = mOld + i } -time { for (i <- 0 until 100000) mOld = mOld + i } -time { for (i <- 0 until 100000) mNew = mNew + i } -time { for (i <- 0 until 100000) mNew = mNew + i } -time { for (i <- 0 until 100000) mNew = mNew + i } -time { mOld.iterator.foreach( p => ()) } -time { mOld.iterator.foreach( p => ()) } -time { mOld.iterator.foreach( p => ()) } -time { mNew.iterator.foreach( p => ()) } -time { mNew.iterator.foreach( p => ()) } -time { mNew.iterator.foreach( p => ()) } - -*/ + override def foreach[U](f: A => U): Unit = { var i = 0 - while (i < elems.length) { + while (i < elems.length) { elems(i).foreach(f) i += 1 } diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index 5e1de44749..a82e31f5da 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -955,7 +955,7 @@ self => * `Stream`. * @example {{{ * val sov: Stream[Vector[Int]] = Vector(0) #:: Vector(0, 0) #:: sov.zip(sov.tail).map { n => n._1 ++ n._2 } - * sov flatten take 10 mkString ", " + * sov.flatten take 10 mkString ", " * // produces: "0, 0, 0, 0, 0, 0, 0, 0, 0, 0" * }}} */ diff --git a/src/library/scala/collection/mutable/ResizableArray.scala b/src/library/scala/collection/mutable/ResizableArray.scala index 4a12f9588c..c3047522e2 100644 --- a/src/library/scala/collection/mutable/ResizableArray.scala +++ b/src/library/scala/collection/mutable/ResizableArray.scala @@ -89,16 +89,20 @@ trait ResizableArray[A] extends IndexedSeq[A] } } - /** Ensure that the internal array has at `n` cells. */ + /** Ensure that the internal array has at least `n` cells. */ protected def ensureSize(n: Int) { - if (n > array.length) { - var newsize = array.length * 2 - while (n > newsize) - newsize = newsize * 2 - - val newar: Array[AnyRef] = new Array(newsize) - scala.compat.Platform.arraycopy(array, 0, newar, 0, size0) - array = newar + // Use a Long to prevent overflows + val arrayLength: Long = array.length + if (n > arrayLength) { + var newSize: Long = arrayLength * 2 + while (n > newSize) + newSize = newSize * 2 + // Clamp newSize to Int.MaxValue + if (newSize > Int.MaxValue) newSize = Int.MaxValue + + val newArray: Array[AnyRef] = new Array(newSize.toInt) + scala.compat.Platform.arraycopy(array, 0, newArray, 0, size0) + array = newArray } } diff --git a/src/library/scala/math/Numeric.scala b/src/library/scala/math/Numeric.scala index e6644c0dfc..eafbf96993 100644 --- a/src/library/scala/math/Numeric.scala +++ b/src/library/scala/math/Numeric.scala @@ -127,6 +127,8 @@ object Numeric { def toLong(x: Float): Long = x.toLong def toFloat(x: Float): Float = x def toDouble(x: Float): Double = x.toDouble + // logic in Numeric base trait mishandles abs(-0.0f) + override def abs(x: Float): Float = math.abs(x) } trait FloatIsFractional extends FloatIsConflicted with Fractional[Float] { def div(x: Float, y: Float): Float = x / y @@ -149,6 +151,8 @@ object Numeric { def toLong(x: Double): Long = x.toLong def toFloat(x: Double): Float = x.toFloat def toDouble(x: Double): Double = x + // logic in Numeric base trait mishandles abs(-0.0) + override def abs(x: Double): Double = math.abs(x) } trait DoubleIsFractional extends DoubleIsConflicted with Fractional[Double] { def div(x: Double, y: Double): Double = x / y diff --git a/src/library/scala/ref/WeakReference.scala b/src/library/scala/ref/WeakReference.scala index c8fb262a08..6ee40aed5c 100644 --- a/src/library/scala/ref/WeakReference.scala +++ b/src/library/scala/ref/WeakReference.scala @@ -10,7 +10,7 @@ package scala.ref /** - * A wrapper class for java.lag.ref.WeakReference + * A wrapper class for java.lang.ref.WeakReference * The new functionality is (1) results are Option values, instead of using null. * (2) There is an extractor that maps the weak reference itself into an option. * @author Sean McDirmid diff --git a/src/library/scala/sys/process/ProcessBuilder.scala b/src/library/scala/sys/process/ProcessBuilder.scala index feced71dae..88c0cf8e58 100644 --- a/src/library/scala/sys/process/ProcessBuilder.scala +++ b/src/library/scala/sys/process/ProcessBuilder.scala @@ -62,7 +62,7 @@ import ProcessBuilder._ * there's a few methods that create a new `ProcessBuilder` with a * pre-configured input or output. They are `#<`, `#>` and `#>>`, and may take * as input either another `ProcessBuilder` (like the pipe described above), or - * something else such as a `java.io.File` or a `java.lang.InputStream`. + * something else such as a `java.io.File` or a `java.io.InputStream`. * For example: * {{{ * new URL("http://databinder.net/dispatch/About") #> "grep JSON" #>> new File("About_JSON") ! diff --git a/src/library/scala/util/Sorting.scala b/src/library/scala/util/Sorting.scala index 276e157f55..2e021ad9d9 100644 --- a/src/library/scala/util/Sorting.scala +++ b/src/library/scala/util/Sorting.scala @@ -141,14 +141,14 @@ object Sorting { var done = false while (!done) { while (b <= c && x(b) <= v) { - if (x(b) == v) { + if (x(b) equiv v) { swap(a, b) a += 1 } b += 1 } while (c >= b && x(c) >= v) { - if (x(c) == v) { + if (x(c) equiv v) { swap(c, d) d -= 1 } diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 9baf3ec179..6971175f88 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -90,9 +90,9 @@ private[reflect] trait BuildUtils { self: Universe => def RefTree(qual: Tree, sym: Symbol): Tree - def withFreshTermName[T](prefix: String)(f: TermName => T): T + def freshTermName(prefix: String): TermName - def withFreshTypeName[T](prefix: String)(f: TypeName => T): T + def freshTypeName(prefix: String): TypeName val ScalaDot: ScalaDotExtractor diff --git a/src/reflect/scala/reflect/api/Liftables.scala b/src/reflect/scala/reflect/api/Liftables.scala new file mode 100644 index 0000000000..6ac5557caa --- /dev/null +++ b/src/reflect/scala/reflect/api/Liftables.scala @@ -0,0 +1,30 @@ +package scala +package reflect +package api + +// TODO: needs a Scaladoc +trait Liftables { self: Universe => + + // TODO: needs a Scaladoc + trait Liftable[T] { + def apply(value: T): Tree + } + + // TODO: needs a Scaladoc + object Liftable extends StandardLiftableInstances { + def apply[T](f: T => Tree): Liftable[T] = + new Liftable[T] { def apply(value: T): Tree = f(value) } + } + + // TODO: needs a Scaladoc + trait Unliftable[T] { + def unapply(tree: Tree): Option[T] + } + + // TODO: needs a Scaladoc + object Unliftable extends StandardUnliftableInstances { + def apply[T](pf: PartialFunction[Tree, T]): Unliftable[T] = new Unliftable[T] { + def unapply(value: Tree): Option[T] = pf.lift(value) + } + } +} diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala index 6e2e2c3781..1e0854d171 100644 --- a/src/reflect/scala/reflect/api/Printers.scala +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -68,7 +68,7 @@ import java.io.{ PrintWriter, StringWriter } * scala> import scala.reflect.runtime.{currentMirror => cm} * import scala.reflect.runtime.{currentMirror=>cm} * - * scala> showRaw(cm.mkToolBox().typeCheck(tree), printTypes = true) + * scala> showRaw(cm.mkToolBox().typecheck(tree), printTypes = true) * res2: String = Block[1](List( * ClassDef[2](Modifiers(FINAL), newTypeName("C"), List(), Template[3]( * List(Ident[4](newTypeName("AnyRef"))), diff --git a/src/reflect/scala/reflect/api/StandardLiftables.scala b/src/reflect/scala/reflect/api/StandardLiftables.scala index 57464459be..6756d5e114 100644 --- a/src/reflect/scala/reflect/api/StandardLiftables.scala +++ b/src/reflect/scala/reflect/api/StandardLiftables.scala @@ -4,14 +4,7 @@ package api trait StandardLiftables { self: Universe => import build.{SyntacticTuple, ScalaDot} - trait Liftable[T] { - def apply(value: T): Tree - } - - object Liftable { - def apply[T](f: T => Tree): Liftable[T] = - new Liftable[T] { def apply(value: T): Tree = f(value) } - + trait StandardLiftableInstances { private def lift[T: Liftable](value: T): Tree = implicitly[Liftable[T]].apply(value) private def selectScala(names: Name*) = names.tail.foldLeft(ScalaDot(names.head)) { Select(_, _) } private def callScala(names: Name*)(args: List[Tree]) = Apply(selectScala(names: _*), args) @@ -122,15 +115,7 @@ trait StandardLiftables { self: Universe => } } - trait Unliftable[T] { - def unapply(tree: Tree): Option[T] - } - - object Unliftable { - def apply[T](pf: PartialFunction[Tree, T]): Unliftable[T] = new Unliftable[T] { - def unapply(value: Tree): Option[T] = pf.lift(value) - } - + trait StandardUnliftableInstances { private def unliftPrimitive[Unboxed: ClassTag, Boxed: ClassTag] = Unliftable[Unboxed] { case Literal(Constant(value)) if value.getClass == implicitly[ClassTag[Boxed]].runtimeClass @@ -149,7 +134,7 @@ trait StandardLiftables { self: Universe => implicit def unliftString: Unliftable[String] = Unliftable { case Literal(Constant(s: String)) => s } implicit def unliftScalaSymbol: Unliftable[scala.Symbol] = Unliftable { - case Apply(ScalaDot(nme.Symbol), List(Literal(Constant(name: String)))) => scala.Symbol(name) + case Apply(ScalaDot(symbol), List(Literal(Constant(name: String)))) if symbol == nme.Symbol => scala.Symbol(name) } implicit def unliftName[T <: Name : ClassTag]: Unliftable[T] = Unliftable[T] { case Ident(name: T) => name; case Bind(name: T, Ident(nme.WILDCARD)) => name} @@ -223,4 +208,23 @@ trait StandardLiftables { self: Universe => case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: UnliftT3(v3) :: UnliftT4(v4) :: UnliftT5(v5) :: UnliftT6(v6) :: UnliftT7(v7) :: UnliftT8(v8) :: UnliftT9(v9) :: UnliftT10(v10) :: UnliftT11(v11) :: UnliftT12(v12) :: UnliftT13(v13) :: UnliftT14(v14) :: UnliftT15(v15) :: UnliftT16(v16) :: UnliftT17(v17) :: UnliftT18(v18) :: UnliftT19(v19) :: UnliftT20(v20) :: UnliftT21(v21) :: UnliftT22(v22) :: Nil) => Tuple22(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) } } + + // names used internally by implementations of standard liftables and unliftables + import scala.language.implicitConversions + private implicit def cachedNames(nme: self.nme.type): CachedNames.type = CachedNames + private object CachedNames { + val Array = TermName("Array") + val collection = TermName("collection") + val immutable = TermName("immutable") + val Left = TermName("Left") + val List = TermName("List") + val Map = TermName("Map") + val None = TermName("None") + val Right = TermName("Right") + val Set = TermName("Set") + val Some = TermName("Some") + val Symbol = TermName("Symbol") + val Vector = TermName("Vector") + val util = TermName("util") + } } diff --git a/src/reflect/scala/reflect/api/StandardNames.scala b/src/reflect/scala/reflect/api/StandardNames.scala index 4ea6ddc7f8..aec5f19fa0 100644 --- a/src/reflect/scala/reflect/api/StandardNames.scala +++ b/src/reflect/scala/reflect/api/StandardNames.scala @@ -96,20 +96,6 @@ trait StandardNames { * of non-private vals and vars are renamed using `LOCAL_SUFFIX_STRING`. */ val LOCAL_SUFFIX_STRING: String - - protected[reflect] val Array: NameType - protected[reflect] val collection: NameType - protected[reflect] val immutable: NameType - protected[reflect] val Left: NameType - protected[reflect] val List: NameType - protected[reflect] val Map: NameType - protected[reflect] val None: NameType - protected[reflect] val Right: NameType - protected[reflect] val Set: NameType - protected[reflect] val Some: NameType - protected[reflect] val Symbol: NameType - protected[reflect] val util: NameType - protected[reflect] val Vector: NameType } /** Defines standard type names that can be accessed via the [[tpnme]] member. diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala index 534f69a23e..1da2c24306 100644 --- a/src/reflect/scala/reflect/api/Universe.scala +++ b/src/reflect/scala/reflect/api/Universe.scala @@ -78,6 +78,7 @@ abstract class Universe extends Symbols with Mirrors with Printers with Importers + with Liftables with Quasiquotes { /** Use `refiy` to produce the abstract syntax tree representing a given Scala expression. diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 13c2268227..0a81bfa2a5 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -134,9 +134,9 @@ trait BuildUtils { self: SymbolTable => def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym - def withFreshTermName[T](prefix: String)(f: TermName => T): T = f(freshTermName(prefix)) + def freshTermName(prefix: String): TermName = self.freshTermName(prefix) - def withFreshTypeName[T](prefix: String)(f: TypeName => T): T = f(freshTypeName(prefix)) + def freshTypeName(prefix: String): TypeName = self.freshTypeName(prefix) protected implicit def fresh: FreshNameCreator = self.currentFreshNameCreator diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index 3a630b6a16..9c5a593ca5 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -281,7 +281,6 @@ trait Mirrors extends api.Mirrors { class RootPackage extends ModuleSymbol(rootOwner, NoPosition, nme.ROOTPKG) with RootSymbol { this setInfo NullaryMethodType(RootClass.tpe) - RootClass.sourceModule = this override def isRootPackage = true } @@ -296,6 +295,7 @@ trait Mirrors extends api.Mirrors { override def isRoot = true override def isEffectiveRoot = true override def isNestedClass = false + override def sourceModule = RootPackage } // This is <root>, the actual root of everything except the package _root_. @@ -316,6 +316,7 @@ trait Mirrors extends api.Mirrors { class EmptyPackageClass extends PackageClassSymbol(RootClass, NoPosition, tpnme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { override def isEffectiveRoot = true override def isEmptyPackageClass = true + override def sourceModule = EmptyPackage } lazy val EmptyPackageClass = new EmptyPackageClass diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 5ad2b15e8c..ea6afa7349 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -124,15 +124,8 @@ trait StdNames { final val AnyRef: NameType = "AnyRef" final val Array: NameType = "Array" final val List: NameType = "List" - final val Left: NameType = "Left" - final val Right: NameType = "Right" - final val Vector: NameType = "Vector" final val Seq: NameType = "Seq" - final val Set: NameType = "Set" - final val Some: NameType = "Some" final val Symbol: NameType = "Symbol" - final val Map: NameType = "Map" - final val None: NameType = "None" final val WeakTypeTag: NameType = "WeakTypeTag" final val TypeTag : NameType = "TypeTag" final val Expr: NameType = "Expr" @@ -681,6 +674,8 @@ trait StdNames { val find_ : NameType = "find" val flatMap: NameType = "flatMap" val foreach: NameType = "foreach" + val freshTermName: NameType = "freshTermName" + val freshTypeName: NameType = "freshTypeName" val get: NameType = "get" val hashCode_ : NameType = "hashCode" val hash_ : NameType = "hash" @@ -763,7 +758,6 @@ trait StdNames { val typedProductIterator: NameType = "typedProductIterator" val TypeName: NameType = "TypeName" val typeTagToManifest: NameType = "typeTagToManifest" - val util: NameType = "util" val unapply: NameType = "unapply" val unapplySeq: NameType = "unapplySeq" val unbox: NameType = "unbox" @@ -799,7 +793,7 @@ trait StdNames { final val STAR : NameType = "*" final val TILDE: NameType = "~" - final val isUnary: Set[Name] = scala.collection.immutable.Set(MINUS, PLUS, TILDE, BANG) + final val isUnary: Set[Name] = Set(MINUS, PLUS, TILDE, BANG) } // value-conversion methods @@ -852,8 +846,8 @@ trait StdNames { val UNARY_! = encode("unary_!") // Grouped here so Cleanup knows what tests to perform. - val CommonOpNames = scala.collection.immutable.Set[Name](OR, XOR, AND, EQ, NE) - val BooleanOpNames = scala.collection.immutable.Set[Name](ZOR, ZAND, UNARY_!) ++ CommonOpNames + val CommonOpNames = Set[Name](OR, XOR, AND, EQ, NE) + val BooleanOpNames = Set[Name](ZOR, ZAND, UNARY_!) ++ CommonOpNames val add: NameType = "add" val complement: NameType = "complement" diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 0dfcf06874..e15b33e5d7 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -801,9 +801,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => isConstructor && !isPrimaryConstructor /** Is this symbol a synthetic apply or unapply method in a companion object of a case class? */ + // xeno-by: why this obscure use of the CASE flag? why not simply compare name with nme.apply and nme.unapply? final def isCaseApplyOrUnapply = isMethod && isCase && isSynthetic + /** Is this symbol a synthetic copy method in a case class? */ + final def isCaseCopy = + isMethod && owner.isCase && isSynthetic && name == nme.copy + /** Is this symbol a trait which needs an implementation class? */ final def needsImplClass = ( isTrait diff --git a/src/reflect/scala/reflect/macros/Typers.scala b/src/reflect/scala/reflect/macros/Typers.scala index 29c1af110b..5a25801335 100644 --- a/src/reflect/scala/reflect/macros/Typers.scala +++ b/src/reflect/scala/reflect/macros/Typers.scala @@ -23,6 +23,12 @@ trait Typers { */ def openMacros: List[BlackboxContext] + /** @see `Typers.typecheck` + */ + @deprecated("Use `c.typecheck` instead", "2.11.0") + def typeCheck(tree: Tree, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = + typecheck(tree, pt, silent, withImplicitViewsDisabled, withMacrosDisabled) + /** Typechecks the provided tree against the expected type `pt` in the macro callsite context. * * If `silent` is false, `TypecheckException` will be thrown in case of a typecheck error. @@ -36,7 +42,7 @@ trait Typers { * * @throws [[scala.reflect.macros.TypecheckException]] */ - def typeCheck(tree: Tree, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree + def typecheck(tree: Tree, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree /** Infers an implicit value of the expected type `pt` in the macro callsite context. * Optional `pos` parameter provides a position that will be associated with the implicit search. @@ -44,7 +50,7 @@ trait Typers { * If `silent` is false, `TypecheckException` will be thrown in case of an inference error. * If `silent` is true, the typecheck is silent and will return `EmptyTree` if an error occurs. * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. - * Unlike in `typeCheck`, `silent` is true by default. + * Unlike in `typecheck`, `silent` is true by default. * * @throws [[scala.reflect.macros.TypecheckException]] */ @@ -56,7 +62,7 @@ trait Typers { * If `silent` is false, `TypecheckException` will be thrown in case of an inference error. * If `silent` is true, the typecheck is silent and will return `EmptyTree` if an error occurs. * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. - * Unlike in `typeCheck`, `silent` is true by default. + * Unlike in `typecheck`, `silent` is true by default. * * @throws [[scala.reflect.macros.TypecheckException]] */ diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 0f41506382..9c4a3a5fe1 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -204,9 +204,10 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => // inaccessible: this.TypeHistory this.TermName this.TypeName - this.BooleanFlag this.Liftable this.Unliftable + this.BooleanFlag + // inaccessible: this.CachedNames this.WeakTypeTag this.TypeTag this.Expr |