diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index b9f2062bac97..9cbf573e799c 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -309,8 +309,9 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { resTp: Type = functionResultType(fun.tpe), additionalFlags: FlagSet = NoFlags): DefDef = { val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags) - // for sams, methParamProtos is the parameter symbols for the sam's method, so that we generate the correct override (based on parameter types) - val methParamSyms = methParamProtos.map { param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName) } + // for sams, methParamProtos is the parameter symbols for the sam's method, + // so that we generate the correct override (based on parameter types) + val methParamSyms = methParamProtos.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName)) methSym setInfo MethodType(methParamSyms, resTp) // we must rewire reference to the function's param symbols -- and not methParamProtos -- to methParamSyms @@ -326,8 +327,8 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { val enclosingStaticModules = owner.ownersIterator.filter(x => !x.hasPackageFlag && x.isModuleClass && x.isStatic) enclosingStaticModules.foldLeft(tree)((tree, moduleClass) => tree.substituteThis(moduleClass, gen.mkAttributedIdent(moduleClass.sourceModule)) ) } - - newDefDef(methSym, substThisForModule(moveToMethod(useMethodParams(fun.body))))(tpt = TypeTree(resTp)) + val body = substThisForModule(moveToMethod(useMethodParams(fun.body))) + newDefDef(methSym, body)(tpt = TypeTree(resTp)) } /** diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 9ea2dbc75603..6d34ef3cccd7 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -14,15 +14,14 @@ package scala package tools.nsc package transform -import scala.PartialFunction.cond import scala.annotation.tailrec -import scala.collection.mutable -import scala.collection.mutable.ListBuffer +import scala.collection.mutable, mutable.{Buffer, ListBuffer} import scala.reflect.internal.util.ListOfNil import scala.tools.nsc.Reporting.WarningCategory import scala.tools.nsc.symtab.Flags._ -/** - uncurry all symbol and tree types (@see UnCurryPhase) -- this includes normalizing all proper types. +/** Uncurry all symbol and tree types, normalizing all proper types. + * * - for every curried parameter list: (ps_1) ... (ps_n) ==> (ps_1, ..., ps_n) * - for every curried application: f(args_1)...(args_n) ==> f(args_1, ..., args_n) * - for every type application: f[Ts] ==> f[Ts]() unless followed by parameters @@ -79,34 +78,36 @@ abstract class UnCurry extends InfoTransform private val forceExpandFunction = settings.Ydelambdafy.value == "inline" private var needTryLift = false private var inConstructorFlag = 0L - private val byNameArgs = mutable.HashSet[Tree]() - private val noApply = mutable.HashSet[Tree]() - private val newMembers = mutable.Map[Symbol, mutable.Buffer[Tree]]() + private val noApply = mutable.HashSet.empty[Tree] + private val newMembers = mutable.Map.empty[Symbol, Buffer[Tree]] // Expand `Function`s in constructors to class instance creation (scala/bug#6666, scala/bug#8363) // We use Java's LambdaMetaFactory (LMF), which requires an interface for the sam's owner private def mustExpandFunction(fun: Function) = { // (TODO: Can't use isInterface, yet, as it hasn't been updated for the new trait encoding) - val canUseLambdaMetaFactory = (fun.attachments.get[SAMFunction] match { + val canUseLambdaMetaFactory = fun.attachments.get[SAMFunction] match { case Some(SAMFunction(userDefinedSamTp, sam, _)) => - // LambdaMetaFactory cannot mix in trait members for us, or instantiate classes -- only pure interfaces need apply + // LambdaMetaFactory cannot mix in trait members for us, or instantiate classes + // -- only pure interfaces need apply erasure.compilesToPureInterface(erasure.javaErasure(userDefinedSamTp).typeSymbol) && - // impl restriction -- we currently use the boxed apply, so not really useful to allow specialized sam types (https://github.com/scala/scala/pull/4971#issuecomment-198119167) + // impl restriction -- we currently use the boxed apply, so not really useful to allow specialized sam types + // (https://github.com/scala/scala/pull/4971#issuecomment-198119167) // specialization and LMF are at odds, since LMF implements the single abstract method, - // but that's the one that specialization leaves generic, whereas we need to implement the specialized one to avoid boxing + // but that's the one that specialization leaves generic, + // whereas we need to implement the specialized one to avoid boxing !specializeTypes.isSpecializedIn(sam, userDefinedSamTp) case _ => true // our built-in FunctionN's are suitable for LambdaMetaFactory by construction - }) + } !canUseLambdaMetaFactory } /** Add a new synthetic member for `currentOwner` */ private def addNewMember(t: Tree): Unit = - newMembers.getOrElseUpdate(currentOwner, mutable.Buffer()) += t + newMembers.getOrElseUpdate(currentOwner, Buffer.empty) += t - /** Process synthetic members for `owner`. They are removed form the `newMembers` as a side-effect. */ + /** Process synthetic members for `owner`. They are removed from the `newMembers` as a side-effect. */ @inline private def useNewMembers[T](owner: Symbol)(f: List[Tree] => T): T = f(newMembers.remove(owner).getOrElse(Nil).toList) @@ -122,17 +123,26 @@ abstract class UnCurry extends InfoTransform EmptyTree } - /* Is tree a reference `x` to a call by name parameter that needs to be converted to - * x.apply()? Note that this is not the case if `x` is used as an argument to another - * call by name parameter. + /* Is `tree` a reference `x` to a call by name parameter and eligible to be converted to x.apply()? + * + * Normally, such a reference evaluates the argument. + * + * This is not the case if `x` is used as an argument to a method that takes it as a call by name parameter. + * Additionally, an expression `() => x` is "unwrapped" to `x` and must not be applied. */ - def isByNameRef(tree: Tree) = ( - tree.isTerm - && (tree.symbol ne null) - && !(tree.symbol.hasPackageFlag || tree.isInstanceOf[This] || tree.isInstanceOf[Super]) - && isByName(tree.symbol) - && !byNameArgs(tree) - ) + def isByNameRefToApply(tree: Tree) = { + val sym = tree.symbol + val maybe = ( + (sym ne null) + && tree.isTerm + && !sym.hasPackageFlag + ) + tree match { + case _ if !maybe => false + case _: This | _: Super => false + case tree => isByName(sym) && !noApply(tree) + } + } // ------- Handling non-local returns ------------------------------------------------- @@ -207,44 +217,41 @@ abstract class UnCurry extends InfoTransform // ------ Transforming anonymous functions and by-name-arguments ---------------- - /** Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to - * - * class \$anon() extends AbstractFunctionN[T_1, .., T_N, R] with Serializable { - * def apply(x_1: T_1, ..., x_N: T_n): R = body - * } - * new \$anon() + /** Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to * + * class \$anon() extends AbstractFunctionN[T_1, .., T_N, R] with Serializable { + * def apply(x_1: T_1, ..., x_N: T_n): R = body + * } + * new \$anon() */ def transformFunction(fun: Function): Tree = // Undo eta expansion for parameterless and nullary methods, EXCEPT if `fun` targets a SAM. - // Normally, we can unwrap `() => cbn` to `cbn` where `cbn` refers to a CBN argument (typically `cbn` is an Ident), + // Normally, we can unwrap `() => cbn` to `cbn` where `cbn` refers to a CBN argument (typically `cbn` is an Ident) // because we know `cbn` will already be a `Function0` thunk. When we're targeting a SAM, // the types don't align and we must preserve the function wrapper. - if (fun.vparams.isEmpty && isByNameRef(fun.body) && fun.attachments.get[SAMFunction].isEmpty) { noApply += fun.body ; fun.body } + if (fun.vparams.isEmpty && isByNameRefToApply(fun.body) && !fun.attachments.contains[SAMFunction]) { + noApply += fun.body + fun.body + } else if (forceExpandFunction || inConstructorFlag != 0) { // Expand the function body into an anonymous class gen.expandFunction(localTyper)(fun, inConstructorFlag) - } else { - val mustExpand = mustExpandFunction(fun) + } + else { // method definition with the same arguments, return type, and body as the original lambda val liftedMethod = gen.mkLiftedFunctionBodyMethod(localTyper)(fun.symbol.owner, fun) - // new function whose body is just a call to the lifted method val newFun = deriveFunction(fun)(_ => localTyper.typedPos(fun.pos)( - gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), (fun.vparams map (_.symbol)) :: Nil) + gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), fun.vparams.map(_.symbol) :: Nil) )) - - if (!mustExpand) { - liftedMethod.symbol.updateAttachment(DelambdafyTarget) - liftedMethod.updateAttachment(DelambdafyTarget) - } - - val typedNewFun = localTyper.typedPos(fun.pos)(Block(liftedMethod :: Nil, super.transform(newFun))) - if (mustExpand) { - val Block(stats, expr : Function) = typedNewFun: @unchecked - treeCopy.Block(typedNewFun, stats, gen.expandFunction(localTyper)(expr, inConstructorFlag)) - } else { - typedNewFun + localTyper.typedPos(fun.pos)(Block(liftedMethod :: Nil, super.transform(newFun))) match { + case typedNewFun @ Block(stats, expr: Function) if mustExpandFunction(fun) => + val expansion = gen.expandFunction(localTyper)(expr, inConstructorFlag) + treeCopy.Block(typedNewFun, stats, expansion) + case typedNewFun => + liftedMethod.symbol.updateAttachment(DelambdafyTarget) + liftedMethod.updateAttachment(DelambdafyTarget) + typedNewFun } } @@ -333,7 +340,7 @@ abstract class UnCurry extends InfoTransform } } } - val args1 = ListBuffer[Tree]() + val args1 = ListBuffer.empty[Tree] args1 ++= args.iterator.take(params.length - 1) args1 += suffix setType params.last.info args1.toList @@ -342,14 +349,27 @@ abstract class UnCurry extends InfoTransform val isVarargs = isVarArgsList(params) val args1 = if (isVarargs) transformVarargs(params.last.info.typeArgs.head.widen) else args + val params0 = + fun.info.paramss match { + case Nil => Iterator.empty + case pss => pss.last.iterator + } + map2Conserve(args1, params) { (arg, param) => - if (!isByNameParamType(param.info)) arg - else if (isByNameRef(arg)) { // thunk does not need to be forced because it's a reference to a by-name arg passed to a by-name param - byNameArgs += arg - arg setType functionType(Nil, arg.tpe) + val param0 = if (params0.hasNext) params0.next() else NoSymbol + if (!isByNameParamType(param.info)) { + if (param0 != NoSymbol && isByNameParamType(param0.info)) // pass 2, sig is uncurried in expansion + noApply += arg + arg + } + else if (isByNameRefToApply(arg)) { + // thunk does not need to be forced because it's a reference to a by-name arg passed to a by-name param + noApply += arg + arg.setType(functionType(Nil, arg.tpe)) } else { log(s"Argument '$arg' at line ${arg.pos.line} is ${param.info} from ${fun.fullName}") - def canUseDirectly(qual: Tree) = qual.tpe.typeSymbol.isSubClass(FunctionClass(0)) && treeInfo.isExprSafeToInline(qual) + def canUseDirectly(qual: Tree) = + qual.tpe.typeSymbol.isSubClass(FunctionClass(0)) && treeInfo.isExprSafeToInline(qual) arg match { // don't add a thunk for by-name argument if argument already is an application of // a Function0. We can then remove the application and use the existing Function0. @@ -456,7 +476,7 @@ abstract class UnCurry extends InfoTransform else translateSynchronized(tree) match { case dd @ DefDef(mods, name, tparams, _, tpt, rhs) => // Remove default argument trees from parameter ValDefs, scala/bug#4812 - val vparamssNoRhs = dd.vparamss mapConserve (_ mapConserve {p => + val vparamssNoRhs = dd.vparamss.mapConserve(_.mapConserve { p => treeCopy.ValDef(p, p.mods, p.name, p.tpt, EmptyTree) }) @@ -484,7 +504,8 @@ abstract class UnCurry extends InfoTransform } } case ValDef(mods, _, _, rhs) => - if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit) + if (sym eq NoSymbol) + throw new IllegalStateException(s"Encountered Valdef without symbol: $tree in $unit") if (!sym.owner.isSourceMethod || mods.isLazy) withNeedLift(needLift = true) { super.transform(tree) } else @@ -544,12 +565,10 @@ abstract class UnCurry extends InfoTransform case _ => val tree1 = super.transform(tree) - if (isByNameRef(tree1)) { - val tree2 = tree1 setType functionType(Nil, tree1.tpe) - return { - if (noApply contains tree2) tree2 - else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil)) - } + if (isByNameRefToApply(tree1)) { + val tree2 = tree1.setType(functionType(Nil, tree1.tpe)) + val tree3 = localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil)) + return tree3 // result type already set } tree1 } @@ -611,14 +630,15 @@ abstract class UnCurry extends InfoTransform val literalRhsIfConst = if (newParamss.head.isEmpty) { // We know newParamss.length == 1 from above ddSym.info.resultType match { - case tp@FoldableConstantType(value) => Literal(value) setType tp setPos newRhs.pos // inlining of gen.mkAttributedQualifier(tp) + case tp @ FoldableConstantType(value) => + Literal(value).setType(tp).setPos(newRhs.pos) // inlining of gen.mkAttributedQualifier(tp) case _ => newRhs } } else newRhs val flatdd = copyDefDef(dd)( vparamss = newParamss, - rhs = nonLocalReturnKeys get ddSym match { + rhs = nonLocalReturnKeys.get(ddSym) match { case Some(k) => atPos(newRhs.pos)(nonLocalReturnTry(literalRhsIfConst, k, ddSym)) case None => literalRhsIfConst } @@ -801,12 +821,20 @@ abstract class UnCurry extends InfoTransform if (dd.symbol.isConstructor) reporter.error(dd.symbol.pos, "A constructor cannot be annotated with a `varargs` annotation.") else { - val ok = cond(dd.symbol.paramss.filter(_.nonEmpty)) { - case initPs :+ lastPs => - initPs.forall(!definitions.isVarArgsList(_)) && definitions.isVarArgsList(lastPs) + def validate(paramss: List[List[Symbol]]): Boolean = paramss match { + case params :: Nil => + isVarArgsList(params) + case params :: _ + if isVarArgsList(params) => + false + case _ :: paramss => + validate(paramss) + case nil => + false } - if (!ok) - reporter.error(dd.pos, "A method annotated with @varargs must have a single repeated parameter in its last parameter list.") + if (!validate(dd.symbol.paramss)) + reporter.error(dd.pos, + "A method annotated with @varargs must have a single repeated parameter in its last parameter list.") } /** @@ -819,7 +847,8 @@ abstract class UnCurry extends InfoTransform * @see [[scala.reflect.internal.transform.UnCurry]] */ private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef): DefDef = { - if (!dd.symbol.hasAnnotation(VarargsClass) || !enteringUncurry(mexists(dd.symbol.paramss)(sym => definitions.isRepeatedParamType(sym.tpe)))) + if (!dd.symbol.hasAnnotation(VarargsClass) + || !enteringUncurry(mexists(dd.symbol.paramss)(sym => isRepeatedParamType(sym.tpe)))) return flatdd val forwSym: Symbol = { @@ -827,13 +856,15 @@ abstract class UnCurry extends InfoTransform flatdd.symbol.attachments.get[VarargsSymbolAttachment] match { case Some(VarargsSymbolAttachment(sym)) => sym case None => - runReporting.warning(dd.pos, s"Could not generate Java varargs forwarder for ${flatdd.symbol}. Please file a bug.", WarningCategory.Other, dd.symbol) + runReporting.warning(dd.pos, + s"Could not generate Java varargs forwarder for ${flatdd.symbol}. Please file a bug.", + WarningCategory.Other, dd.symbol) return flatdd } } val newPs = forwSym.tpe.params - val isRepeated = enteringUncurry(dd.symbol.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe))) + val isRepeated = enteringUncurry(dd.symbol.info.paramss.flatten.map(sym => isRepeatedParamType(sym.tpe))) val oldPs = flatdd.symbol.paramss.head val theTyper = typer.atOwner(dd, currentClass) @@ -855,7 +886,8 @@ abstract class UnCurry extends InfoTransform } // check if the method with that name and those arguments already exists in the template - enteringUncurry(currentClass.info.member(forwSym.name).alternatives.find(s => s != forwSym && s.tpe.matches(forwSym.tpe))) match { + enteringUncurry(currentClass.info.member(forwSym.name).alternatives + .find(s => s != forwSym && s.tpe.matches(forwSym.tpe))) match { case Some(s) => reporter.error(dd.symbol.pos, s"A method annotated with @varargs produces a forwarder method with the same signature ${s.tpe} as an existing method.") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f00f25359682..2609c96f8062 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3045,10 +3045,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (samTp eq NoType) false else { /* Make a synthetic class symbol to represent the synthetic class that - * will be spun up by LMF for this function. This is necessary because - * it's possible that the SAM method might need bridges, and they have - * to go somewhere. Erasure knows to compute bridges for these classes - * just as if they were real templates extending the SAM type. */ + * will be spun up by LMF for this function. This is necessary because + * it's possible that the SAM method might need bridges, and they have + * to go somewhere. Erasure knows to compute bridges for these classes + * just as if they were real templates extending the SAM type. */ val synthCls = fun.symbol.owner.newClassWithInfo( name = tpnme.ANON_CLASS_NAME, parents = ObjectTpe :: samTp :: Nil, @@ -3065,8 +3065,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper fun.setType(samTp) /* Arguably I should do `fun.setSymbol(samCls)` rather than leaning - * on an attachment, but doing that confounds lambdalift's free var - * analysis in a way which does not seem to be trivially reparable. */ + * on an attachment, but doing that confounds lambdalift's free var + * analysis in a way which does not seem to be trivially reparable. */ fun.updateAttachment(SAMFunction(samTp, sam, synthCls)) true @@ -5038,44 +5038,44 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * the function of type `() => $T$`, which evaluates $e$ when it is applied to the empty parameter list `()`. */ def typedEta(methodValue: Tree): Tree = methodValue.tpe match { - case tp@(MethodType(_, _) | PolyType(_, MethodType(_, _))) => // (1) + case tp @ (MethodType(_, _) | PolyType(_, MethodType(_, _))) => // (1) if (tp.params.lengthCompare(definitions.MaxFunctionArity) > 0) MaxFunctionArityError(methodValue, s"; method ${methodValue.symbol.name} cannot be eta-expanded because it takes ${tp.params.length} arguments") else { - val etaPt = - pt match { - case pt: ProtoType => - pt.asFunctionType orElse functionType(WildcardType.fillList(tp.params.length), WildcardType) orElse WildcardType // arity overflow --> NoType - case _ => pt - } - - // We know syntactically methodValue can't refer to a constructor because you can't write `this _` for that (right???) + val etaPt = pt match { + case pt: ProtoType => + pt.asFunctionType + .orElse(functionType(WildcardType.fillList(tp.params.length), WildcardType)) + .orElse(WildcardType) // arity overflow --> NoType + case pt => pt + } + // We know syntactically methodValue can't refer to a constructor because you can't write `this _` typedEtaExpansion(methodValue, mode, etaPt) } case TypeRef(_, ByNameParamClass, _) | NullaryMethodType(_) => // (2) + def warnNullary(tree: Tree) = { + val msg = "Methods without a parameter list and by-name params " + + "can no longer be converted to functions as `m _`, " + + "write a function literal `() => m` instead" + val pos = tree.pos + val action = { + val etaPos = pos.withEnd(pos.end + 2) + if (pos.source.sourceAt(etaPos).endsWith(" _")) + runReporting.codeAction("replace by function literal", etaPos, s"() => ${pos.source.sourceAt(pos)}", msg) + else Nil + } + if (currentRun.isScala3) + context.warning(pos, msg, Scala3Migration, action) + else + context.deprecationWarning(pos, NoSymbol, msg, "2.13.2", action) + } val pos = methodValue.pos - // must create it here to change owner (normally done by typed's typedFunction) + // must create it here to change owner (normally done by typer's typedFunction) val funSym = context.owner.newAnonymousFunctionValue(pos) new ChangeOwnerTraverser(context.owner, funSym) traverse methodValue - val result = typed(Function(Nil, methodValue) setSymbol funSym setPos pos, mode, pt) - - val msg = "Methods without a parameter list and by-name params can no longer be converted to functions as `m _`, " + - "write a function literal `() => m` instead" - - val action = { - val etaPos = pos.withEnd(pos.end + 2) - if (pos.source.sourceAt(etaPos).endsWith(" _")) - runReporting.codeAction("replace by function literal", etaPos, s"() => ${pos.source.sourceAt(pos)}", msg) - else Nil - } - - if (currentRun.isScala3) - context.warning(pos, msg, Scala3Migration, action) - else - context.deprecationWarning(pos, NoSymbol, msg, "2.13.2", action) - - result + typed(Function(Nil, methodValue).setSymbol(funSym).setPos(pos), mode, pt) + .tap(warnNullary) case ErrorType => methodValue diff --git a/src/compiler/scala/tools/tasty/TastyFormat.scala b/src/compiler/scala/tools/tasty/TastyFormat.scala index 95092da86fda..ce88f4e4cded 100644 --- a/src/compiler/scala/tools/tasty/TastyFormat.scala +++ b/src/compiler/scala/tools/tasty/TastyFormat.scala @@ -15,12 +15,14 @@ package scala.tools.tasty // revision: https://github.com/scala/scala3/commit/24bff2a019afcf0f45ddfd3af6b213e7e228471c object TastyFormat { - /** The first four bytes of a TASTy file, followed by four values: - * - `MajorVersion: Int` - see definition in `TastyFormat` - * - `MinorVersion: Int` - see definition in `TastyFormat` - * - `ExperimentalVersion: Int` - see definition in `TastyFormat` - * - `ToolingVersion: String` - arbitrary length string representing the tool that produced the TASTy. - */ + /** The TASTy file header. + * + * The first four bytes of a TASTy file, followed by four values: + * - `MajorVersion: Int` - see definition in `TastyFormat` + * - `MinorVersion: Int` - see definition in `TastyFormat` + * - `ExperimentalVersion: Int` - see definition in `TastyFormat` + * - `ToolingVersion: String` - arbitrary length string representing the tool that produced the TASTy. + */ final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F) /** Natural number. Each increment of the `MajorVersion` begins a @@ -54,39 +56,39 @@ object TastyFormat { */ final val ExperimentalVersion: Int = 0 - /**This method implements a binary relation (`<:<`) between two TASTy versions. + /** This method implements a binary relation (`<:<`) between two TASTy versions. * - * We label the lhs `file` and rhs `compiler`. - * if `file <:< compiler` then the TASTy file is valid to be read. + * We label the lhs `file` and rhs `compiler`. + * if `file <:< compiler` then the TASTy file is valid to be read. * - * A TASTy version, e.g. `v := 28.0-3` is composed of three fields: - * - v.major == 28 - * - v.minor == 0 - * - v.experimental == 3 + * A TASTy version, e.g. `v := 28.0-3` is composed of three fields: + * - v.major == 28 + * - v.minor == 0 + * - v.experimental == 3 * - * TASTy versions have a partial order, for example, - * `a <:< b` and `b <:< a` are both false if - * - `a` and `b` have different `major` fields. - * - `a` and `b` have the same `major` & `minor` fields, - * but different `experimental` fields, both non-zero. + * TASTy versions have a partial order, for example, + * `a <:< b` and `b <:< a` are both false if + * - `a` and `b` have different `major` fields. + * - `a` and `b` have the same `major` & `minor` fields, + * but different `experimental` fields, both non-zero. * - * A TASTy version with a zero value for its `experimental` field - * is considered to be stable. Files with a stable TASTy version - * can be read by a compiler with an unstable TASTy version, - * (where the compiler's TASTy version has a higher `minor` field). + * A TASTy version with a zero value for its `experimental` field + * is considered to be stable. Files with a stable TASTy version + * can be read by a compiler with an unstable TASTy version, + * (where the compiler's TASTy version has a higher `minor` field). * - * A compiler with a stable TASTy version can never read a file - * with an unstable TASTy version. + * A compiler with a stable TASTy version can never read a file + * with an unstable TASTy version. * - * We follow the given algorithm: + * We follow the given algorithm: * - * ``` - * (fileMajor, fileMinor, fileExperimental) match - * case (`compilerMajor`, `compilerMinor`, `compilerExperimental`) => true // full equality - * case (`compilerMajor`, minor, 0) if minor < compilerMinor => true // stable backwards compatibility - * case _ => false - * ``` - * @syntax markdown + * {{{ + * (fileMajor, fileMinor, fileExperimental) match + * case (`compilerMajor`, `compilerMinor`, `compilerExperimental`) => true // full equality + * case (`compilerMajor`, minor, 0) if minor < compilerMinor => true // stable backwards compatibility + * case _ => false + * }}} + * @syntax markdown */ def isVersionCompatible( fileMajor: Int, diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index fe9f22663010..a653c512d66e 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -50,21 +50,23 @@ trait StdAttachments { } /** Stores the trees that give rise to a refined type to be used in reification. - * Unfortunately typed `CompoundTypeTree` is lacking essential info, and the reifier cannot use `CompoundTypeTree.tpe`. - * Therefore we need this hack (see `Reshape.toPreTyperTypeTree` for a detailed explanation). + * + * Typed `CompoundTypeTree` is lacking info required by the reifier, which can't use `CompoundTypeTree.tpe`. + * (See `Reshape.toPreTyperTypeTree` for a detailed explanation.) */ case class CompoundTypeTreeOriginalAttachment(parents: List[Tree], stats: List[Tree]) - /** Attached to a Function node during type checking when the expected type is a SAM type (and not a built-in FunctionN). - * - * Ideally, we'd move to Dotty's Closure AST, which tracks the environment, - * the lifted method that has the implementation, and the target type. - * For backwards compatibility, an attachment is the best we can do right now. - * - * @param samTp the expected type that triggered sam conversion (may be a subtype of the type corresponding to sam's owner) - * @param sam the single abstract method implemented by the Function we're attaching this to - * @param synthCls the (synthetic) class representing the eventual implementation class (spun at runtime by LMF on the JVM) - */ + /** A Function node was type checked with an expected type that is a SAM type and not a built-in FunctionN. + * + * Dotty's Closure AST improves on this because it tracks the environment, + * the lifted method that has the implementation, and the target type. + * + * @param samTp the expected type that triggered sam conversion + * (may be a subtype of the type corresponding to sam's owner) + * @param sam the single abstract method implemented by the Function we're attaching this to + * @param synthCls the (synthetic) class representing the eventual implementation class + * (spun at runtime by LMF on the JVM) + */ case class SAMFunction(samTp: Type, sam: Symbol, synthCls: Symbol) extends PlainAttachment case object DelambdafyTarget extends PlainAttachment diff --git a/src/reflect/scala/reflect/internal/transform/Transforms.scala b/src/reflect/scala/reflect/internal/transform/Transforms.scala index 37874253adb9..2210e3c540d8 100644 --- a/src/reflect/scala/reflect/internal/transform/Transforms.scala +++ b/src/reflect/scala/reflect/internal/transform/Transforms.scala @@ -20,8 +20,8 @@ import scala.language.existentials trait Transforms { self: SymbolTable => /** We need to encode laziness by hand here because the three components refChecks, uncurry and erasure - * are overwritten by objects in Global. - * It would be best of objects could override lazy values. See scala/bug#5187. + * are overridden by objects in Global. + * It would be best if objects could override lazy values. See scala/bug#5187. * In the absence of this, the Lazy functionality should probably be somewhere * in the standard library. Or is it already? */ diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala index afc1a5e4f37b..a01b78d7cdc0 100644 --- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -47,9 +47,8 @@ trait UnCurry { val uncurry: TypeMap = new TypeMap { @tailrec - def apply(tp0: Type): Type = { - val tp = expandAlias(tp0) - tp match { + def apply(tp: Type): Type = + expandAlias(tp) match { case MethodType(params, MethodType(params1, restpe)) => // This transformation is described in UnCurryTransformer.dependentParamTypeErasure val packSymbolsMap = new TypeMap { @@ -67,10 +66,9 @@ trait UnCurry { apply(MethodType(List(), restpe)) case DesugaredParameterType(desugaredTpe) => apply(desugaredTpe) - case _ => + case tp => expandAlias(tp.mapOver(this)) } - } } object DesugaredParameterType { diff --git a/src/reflect/scala/reflect/macros/Attachments.scala b/src/reflect/scala/reflect/macros/Attachments.scala index a85ac8f948f6..9272e6d48979 100644 --- a/src/reflect/scala/reflect/macros/Attachments.scala +++ b/src/reflect/scala/reflect/macros/Attachments.scala @@ -66,8 +66,15 @@ abstract class Attachments { self => } /** Check underlying payload contains an instance of type `T`. */ - def contains[T: ClassTag]: Boolean = - !isEmpty && (all exists matchesTag[T]) + def contains[T: ClassTag]: Boolean = !isEmpty && { + val it = all.iterator + val matchesTagFn = matchesTag[T] + while (it.hasNext) { // OPT: hotspot, hand roll `Set.exists`. + val datum = it.next() + if (matchesTagFn(datum)) return true + } + false + } /** Creates a copy of this attachment with the payload slot of T added/updated with the provided value. * Replaces an existing payload of the same type, if exists. diff --git a/test/files/neg/varargs.check b/test/files/neg/varargs.check index f39ed66e056f..63dae3cfa83c 100644 --- a/test/files/neg/varargs.check +++ b/test/files/neg/varargs.check @@ -16,4 +16,7 @@ varargs.scala:20: error: A method annotated with @varargs must have a single rep varargs.scala:23: error: A method annotated with @varargs must have a single repeated parameter in its last parameter list. @varargs def v6: Int = 1 // nok ^ -6 errors +varargs.scala:24: error: A method annotated with @varargs must have a single repeated parameter in its last parameter list. + @varargs def v7(i: Int*)() = i.sum // nok, was: (?) + ^ +7 errors diff --git a/test/files/neg/varargs.scala b/test/files/neg/varargs.scala index ad0ac0b02a44..251a4c97ea62 100644 --- a/test/files/neg/varargs.scala +++ b/test/files/neg/varargs.scala @@ -21,6 +21,6 @@ object Test { @varargs def v5(a: String)(b: Int*) = a + b.sum // ok @varargs def v6: Int = 1 // nok - @varargs def v7(i: Int*)() = i.sum // ok (?) + @varargs def v7(i: Int*)() = i.sum // nok, was: (?) } diff --git a/test/files/run/t11237.scala b/test/files/run/t11237.scala new file mode 100644 index 000000000000..1257ee08905e --- /dev/null +++ b/test/files/run/t11237.scala @@ -0,0 +1,20 @@ + +trait Semigroup[F] { self => + def append(f1: F, f2: => F): F + val z = 10 +} + +case class Box(i: Int) + +class R extends Runnable { + def run() = { + val boxSemigroup: Semigroup[Box] = (x1, x2) => Box(x1.i + x2.i) + // disallowed, by-name must be inferred + //val boxSemigroup: Semigroup[Box] = (x1: Box, x2: Box) => Box(Math.max(x1.i, x2.i)) + assert(boxSemigroup.append(Box(1), Box(2)) == Box(3)) + } +} + +object Test extends App { + new R().run() +}