Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c17d9cc

Browse files
committed
Always just forward args in function expansion
Notice args of forwarder when generating it
1 parent 5e63e4d commit c17d9cc

File tree

5 files changed

+81
-30
lines changed

5 files changed

+81
-30
lines changed

src/compiler/scala/tools/nsc/ast/TreeGen.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,9 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
309309
resTp: Type = functionResultType(fun.tpe),
310310
additionalFlags: FlagSet = NoFlags): DefDef = {
311311
val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags)
312-
// for sams, methParamProtos is the parameter symbols for the sam's method, so that we generate the correct override (based on parameter types)
313-
val methParamSyms = methParamProtos.map { param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName) }
312+
// for sams, methParamProtos is the parameter symbols for the sam's method,
313+
// so that we generate the correct override (based on parameter types)
314+
val methParamSyms = methParamProtos.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName))
314315
methSym setInfo MethodType(methParamSyms, resTp)
315316

316317
// 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 {
326327
val enclosingStaticModules = owner.ownersIterator.filter(x => !x.hasPackageFlag && x.isModuleClass && x.isStatic)
327328
enclosingStaticModules.foldLeft(tree)((tree, moduleClass) => tree.substituteThis(moduleClass, gen.mkAttributedIdent(moduleClass.sourceModule)) )
328329
}
329-
330-
newDefDef(methSym, substThisForModule(moveToMethod(useMethodParams(fun.body))))(tpt = TypeTree(resTp))
330+
val body = substThisForModule(moveToMethod(useMethodParams(fun.body)))
331+
newDefDef(methSym, body)(tpt = TypeTree(resTp))
331332
}
332333

333334
/**

src/compiler/scala/tools/nsc/transform/UnCurry.scala

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import scala.collection.mutable.ListBuffer
2121
import scala.reflect.internal.util.ListOfNil
2222
import scala.tools.nsc.Reporting.WarningCategory
2323
import scala.tools.nsc.symtab.Flags._
24+
import scala.util.chaining._
2425

2526
/** - uncurry all symbol and tree types (@see UnCurryPhase) -- this includes normalizing all proper types.
2627
* - for every curried parameter list: (ps_1) ... (ps_n) ==> (ps_1, ..., ps_n)
@@ -215,15 +216,32 @@ abstract class UnCurry extends InfoTransform
215216
* new \$anon()
216217
*
217218
*/
218-
def transformFunction(fun: Function): Tree =
219+
def transformFunction(fun: Function): Tree = {
220+
// in the forwarding expansion def f(ps) = lifted_f(ps) ensure ps never applied
221+
def noApplyInExpansion(expansion: Tree): Unit =
222+
expansion match {
223+
case Block(ClassDef(_, _, _, Template(_, _, body)) :: Nil, _) =>
224+
body.last match {
225+
case dd: DefDef =>
226+
treeInfo.dissectApplied(dd.rhs).argss match {
227+
case args :: _ => args.foreach(noApply.addOne)
228+
case _ =>
229+
}
230+
case _ =>
231+
}
232+
case _ =>
233+
}
219234
// Undo eta expansion for parameterless and nullary methods, EXCEPT if `fun` targets a SAM.
220-
// Normally, we can unwrap `() => cbn` to `cbn` where `cbn` refers to a CBN argument (typically `cbn` is an Ident),
235+
// Normally, we can unwrap `() => cbn` to `cbn` where `cbn` refers to a CBN argument (typically `cbn` is an Ident)
221236
// because we know `cbn` will already be a `Function0` thunk. When we're targeting a SAM,
222237
// the types don't align and we must preserve the function wrapper.
223-
if (fun.vparams.isEmpty && isByNameRef(fun.body) && fun.attachments.get[SAMFunction].isEmpty) { noApply += fun.body ; fun.body }
238+
if (fun.vparams.isEmpty && isByNameRef(fun.body) && !fun.attachments.contains[SAMFunction]) {
239+
noApply += fun.body
240+
fun.body
241+
}
224242
else if (forceExpandFunction || inConstructorFlag != 0) {
225243
// Expand the function body into an anonymous class
226-
gen.expandFunction(localTyper)(fun, inConstructorFlag)
244+
gen.expandFunction(localTyper)(fun, inConstructorFlag).tap(noApplyInExpansion)
227245
} else {
228246
val mustExpand = mustExpandFunction(fun)
229247
// method definition with the same arguments, return type, and body as the original lambda
@@ -233,20 +251,23 @@ abstract class UnCurry extends InfoTransform
233251
val newFun = deriveFunction(fun)(_ => localTyper.typedPos(fun.pos)(
234252
gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), (fun.vparams map (_.symbol)) :: Nil)
235253
))
254+
def typeNewFun = localTyper.typedPos(fun.pos)(Block(liftedMethod :: Nil, super.transform(newFun)))
236255

237256
if (!mustExpand) {
238257
liftedMethod.symbol.updateAttachment(DelambdafyTarget)
239258
liftedMethod.updateAttachment(DelambdafyTarget)
259+
typeNewFun
240260
}
241-
242-
val typedNewFun = localTyper.typedPos(fun.pos)(Block(liftedMethod :: Nil, super.transform(newFun)))
243-
if (mustExpand) {
244-
val Block(stats, expr : Function) = typedNewFun: @unchecked
245-
treeCopy.Block(typedNewFun, stats, gen.expandFunction(localTyper)(expr, inConstructorFlag))
246-
} else {
247-
typedNewFun
261+
else typeNewFun match {
262+
case typedNewFun @ Block(stats, expr: Function) =>
263+
val expansion = gen.expandFunction(localTyper)(expr, inConstructorFlag).tap(noApplyInExpansion)
264+
//if (expr.vparams.exists(p => isByNameParamType(p.symbol.info)))
265+
// noApplyInExpansion(expansion)
266+
treeCopy.Block(typedNewFun, stats, expansion)
267+
case x => throw new MatchError(x)
248268
}
249269
}
270+
}
250271

251272
def transformArgs(pos: Position, fun: Symbol, args: List[Tree], params: List[Symbol]): List[Tree] = {
252273
val isJava = fun.isJavaDefined
@@ -333,7 +354,7 @@ abstract class UnCurry extends InfoTransform
333354
}
334355
}
335356
}
336-
val args1 = ListBuffer[Tree]()
357+
val args1 = ListBuffer.empty[Tree]
337358
args1 ++= args.iterator.take(params.length - 1)
338359
args1 += suffix setType params.last.info
339360
args1.toList
@@ -344,12 +365,14 @@ abstract class UnCurry extends InfoTransform
344365

345366
map2Conserve(args1, params) { (arg, param) =>
346367
if (!isByNameParamType(param.info)) arg
347-
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
368+
else if (isByNameRef(arg)) {
369+
// thunk does not need to be forced because it's a reference to a by-name arg passed to a by-name param
348370
byNameArgs += arg
349371
arg setType functionType(Nil, arg.tpe)
350372
} else {
351373
log(s"Argument '$arg' at line ${arg.pos.line} is ${param.info} from ${fun.fullName}")
352-
def canUseDirectly(qual: Tree) = qual.tpe.typeSymbol.isSubClass(FunctionClass(0)) && treeInfo.isExprSafeToInline(qual)
374+
def canUseDirectly(qual: Tree) =
375+
qual.tpe.typeSymbol.isSubClass(FunctionClass(0)) && treeInfo.isExprSafeToInline(qual)
353376
arg match {
354377
// don't add a thunk for by-name argument if argument already is an application of
355378
// a Function0. We can then remove the application and use the existing Function0.
@@ -456,7 +479,7 @@ abstract class UnCurry extends InfoTransform
456479
else translateSynchronized(tree) match {
457480
case dd @ DefDef(mods, name, tparams, _, tpt, rhs) =>
458481
// Remove default argument trees from parameter ValDefs, scala/bug#4812
459-
val vparamssNoRhs = dd.vparamss mapConserve (_ mapConserve {p =>
482+
val vparamssNoRhs = dd.vparamss.mapConserve(_.mapConserve { p =>
460483
treeCopy.ValDef(p, p.mods, p.name, p.tpt, EmptyTree)
461484
})
462485

@@ -546,10 +569,10 @@ abstract class UnCurry extends InfoTransform
546569
val tree1 = super.transform(tree)
547570
if (isByNameRef(tree1)) {
548571
val tree2 = tree1 setType functionType(Nil, tree1.tpe)
549-
return {
550-
if (noApply contains tree2) tree2
572+
val tree3 =
573+
if (noApply.contains(tree2)) tree2
551574
else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil))
552-
}
575+
return tree3
553576
}
554577
tree1
555578
}

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3045,10 +3045,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
30453045
if (samTp eq NoType) false
30463046
else {
30473047
/* Make a synthetic class symbol to represent the synthetic class that
3048-
* will be spun up by LMF for this function. This is necessary because
3049-
* it's possible that the SAM method might need bridges, and they have
3050-
* to go somewhere. Erasure knows to compute bridges for these classes
3051-
* just as if they were real templates extending the SAM type. */
3048+
* will be spun up by LMF for this function. This is necessary because
3049+
* it's possible that the SAM method might need bridges, and they have
3050+
* to go somewhere. Erasure knows to compute bridges for these classes
3051+
* just as if they were real templates extending the SAM type. */
30523052
val synthCls = fun.symbol.owner.newClassWithInfo(
30533053
name = tpnme.ANON_CLASS_NAME,
30543054
parents = ObjectTpe :: samTp :: Nil,
@@ -3065,8 +3065,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
30653065
fun.setType(samTp)
30663066

30673067
/* Arguably I should do `fun.setSymbol(samCls)` rather than leaning
3068-
* on an attachment, but doing that confounds lambdalift's free var
3069-
* analysis in a way which does not seem to be trivially reparable. */
3068+
* on an attachment, but doing that confounds lambdalift's free var
3069+
* analysis in a way which does not seem to be trivially reparable. */
30703070
fun.updateAttachment(SAMFunction(samTp, sam, synthCls))
30713071

30723072
true

src/reflect/scala/reflect/macros/Attachments.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,15 @@ abstract class Attachments { self =>
6666
}
6767

6868
/** Check underlying payload contains an instance of type `T`. */
69-
def contains[T: ClassTag]: Boolean =
70-
!isEmpty && (all exists matchesTag[T])
69+
def contains[T: ClassTag]: Boolean = !isEmpty && {
70+
val it = all.iterator
71+
val matchesTagFn = matchesTag[T]
72+
while (it.hasNext) { // OPT: hotspot, hand roll `Set.exists`.
73+
val datum = it.next()
74+
if (matchesTagFn(datum)) return true
75+
}
76+
false
77+
}
7178

7279
/** Creates a copy of this attachment with the payload slot of T added/updated with the provided value.
7380
* Replaces an existing payload of the same type, if exists.

test/files/run/t11237.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
trait Semigroup[F] { self =>
3+
def append(f1: F, f2: => F): F
4+
val z = 10
5+
}
6+
7+
case class Box(i: Int)
8+
9+
class R extends Runnable {
10+
def run() = {
11+
val boxSemigroup: Semigroup[Box] = (x1, x2) => Box(x1.i + x2.i)
12+
// disallowed, by-name must be inferred
13+
//val boxSemigroup: Semigroup[Box] = (x1: Box, x2: Box) => Box(Math.max(x1.i, x2.i))
14+
assert(boxSemigroup.append(Box(1), Box(2)) == Box(3))
15+
}
16+
}
17+
18+
object Test extends App {
19+
new R().run()
20+
}

0 commit comments

Comments
 (0)