Skip to content

Commit 46eb5ea

Browse files
committed
Fix treatment of by name functions
By-name functions like `(=> T) => T` were not treated correctly before. Witness the disabled `-Ycheck:gettersSetters` for transform/TreeCheckers in thge test suite. This commit changes the scheme how => T types are treated and fixes the problems with by-name functions.
1 parent 1070499 commit 46eb5ea

File tree

5 files changed

+64
-51
lines changed

5 files changed

+64
-51
lines changed

src/dotty/tools/dotc/TypeErasure.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ object TypeErasure {
111111
erasure(tp)
112112
}
113113

114+
/** The erasure of a symbol's info. This is different of `erasure` in the way `ExprType`s are
115+
* treated. `eraseInfo` maps them them to nullary method types, whereas `erasure` maps them
116+
* to `Function0`.
117+
*/
118+
def eraseInfo(tp: Type)(implicit ctx: Context): Type = scalaErasureFn.eraseInfo(tp)(erasureCtx)
119+
114120
/** The erasure of a function result type. Differs from normal erasure in that
115121
* Unit is kept instead of being mapped to BoxedUnit.
116122
*/
@@ -135,7 +141,7 @@ object TypeErasure {
135141
if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
136142
else if (sym.isAbstractType) TypeAlias(WildcardType)
137143
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
138-
else erase(tp)(erasureCtx)
144+
else eraseInfo(tp)(erasureCtx)
139145
}
140146

141147
def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !(
@@ -263,7 +269,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
263269
case SuperType(thistpe, supertpe) =>
264270
SuperType(this(thistpe), this(supertpe))
265271
case ExprType(rt) =>
266-
MethodType(Nil, Nil, this(rt))
272+
defn.FunctionClass(0).typeRef
267273
case tp: TypeProxy =>
268274
this(tp.underlying)
269275
case AndType(tp1, tp2) =>
@@ -312,6 +318,11 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
312318
else JavaArrayType(this(elemtp))
313319
}
314320

321+
def eraseInfo(tp: Type)(implicit ctx: Context) = tp match {
322+
case ExprType(rt) => MethodType(Nil, Nil, erasure(rt))
323+
case tp => erasure(tp)
324+
}
325+
315326
private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type =
316327
unsupported("eraseDerivedValueClass")
317328

src/dotty/tools/dotc/transform/ElimByName.scala

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,52 @@ package dotty.tools.dotc
22
package transform
33

44
import TreeTransforms._
5-
import core.DenotTransformers._
6-
import core.Symbols._
7-
import core.SymDenotations._
8-
import core.Contexts._
9-
import core.Types._
10-
import core.Flags._
11-
import core.Decorators._
5+
import core._
6+
import DenotTransformers._
7+
import Symbols._
8+
import SymDenotations._
9+
import Contexts._
10+
import Types._
11+
import Flags._
12+
import Decorators._
1213
import SymUtils._
14+
import util.Attachment
1315
import core.StdNames.nme
1416
import ast.Trees._
1517

16-
/** This phase eliminates ExprTypes `=> T` and replaces them by
18+
object ElimByName {
19+
val ByNameArg = new Attachment.Key[Unit]
20+
}
21+
22+
/** This phase eliminates ExprTypes `=> T` as types of function parameters, and replaces them by
1723
* nullary function types. More precisely:
1824
*
19-
* For parameter types:
25+
* For the types of parameter symbols:
2026
*
2127
* => T ==> () => T
2228
*
23-
* For terms:
29+
* Note that `=> T` types are not eliminated in MnethodTypes. This is done later at erasure.
30+
* Terms are rewritten as follows:
2431
*
2532
* x ==> x.apply() if x is a parameter that had type => T
33+
*
34+
* Arguments to call-by-name parameters are translated as follows. First, the argument is
35+
* rewritten by the rules
36+
*
2637
* e.apply() ==> e if e.apply() is an argument to a call-by-name parameter
2738
* expr ==> () => expr if other expr is an argument to a call-by-name parameter
39+
*
40+
* This makes the argument compatible with a parameter type of () => T, which will be the
41+
* formal parameter type at erasure. But to be -Ycheckable until then, any argument
42+
* ARG rewritten by the rules above is again wrapped in an application ARG.apply(),
43+
* labelled with an `ByNameParam` attachment.
44+
*
45+
* Erasure will later strip wrapped `.apply()` calls with ByNameParam attachments.
46+
*
2847
*/
2948
class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransformer =>
3049
import ast.tpd._
50+
import ElimByName._
3151

3252
override def phaseName: String = "elimByName"
3353

@@ -44,34 +64,24 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform
4464
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
4565
ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) {
4666

47-
def transformArg(arg: Tree, formal: Type): Tree = formal match {
48-
case _: ExprType =>
49-
arg match {
67+
def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match {
68+
case formalExpr: ExprType =>
69+
val argFun = arg match {
5070
case Apply(Select(qual, nme.apply), Nil) if qual.tpe derivesFrom defn.FunctionClass(0) =>
5171
qual
5272
case _ =>
5373
val meth = ctx.newSymbol(
5474
ctx.owner, nme.ANON_FUN, Synthetic | Method, MethodType(Nil, Nil, arg.tpe.widen))
5575
Closure(meth, _ => arg.changeOwner(ctx.owner, meth))
5676
}
77+
val argApplied = argFun.select(defn.Function0_apply).appliedToNone
78+
argApplied.putAttachment(ByNameArg, ())
79+
argApplied
5780
case _ =>
5881
arg
5982
}
6083

61-
/** Given that `info` is the possibly curried) method type of the
62-
* tree's symbol, the method type that corresponds to the current application.
63-
*/
64-
def matchingMethType(info: Type, tree: Tree): Type = tree match {
65-
case Apply(fn, _) => matchingMethType(info.resultType, fn)
66-
case _ => info
67-
}
68-
69-
val origMethType = originalDenotation(tree.fun).info match {
70-
case pt: PolyType => pt.resultType
71-
case mt => mt
72-
}
73-
74-
val MethodType(_, formals) = matchingMethType(origMethType, tree.fun)
84+
val MethodType(_, formals) = tree.fun.tpe.widen
7585
val args1 = tree.args.zipWithConserve(formals)(transformArg)
7686
cpy.Apply(tree)(tree.fun, args1)
7787
}
@@ -99,22 +109,13 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform
99109
case _ => tree
100110
}
101111

102-
def elimByNameParams(tp: Type)(implicit ctx: Context): Type = tp match {
103-
case tp: PolyType =>
104-
tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimByNameParams(tp.resultType))
105-
case tp: MethodType =>
106-
tp.derivedMethodType(tp.paramNames, tp.paramTypes mapConserve transformParamInfo,
107-
elimByNameParams(tp.resultType))
108-
case _ =>
109-
tp
110-
}
112+
override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree =
113+
if (exprBecomesFunction(tree.symbol))
114+
cpy.ValDef(tree)(tpt = tree.tpt.withType(tree.symbol.info))
115+
else tree
111116

112-
def transformParamInfo(tp: Type)(implicit ctx: Context) = tp match {
113-
case ExprType(rt) => defn.FunctionType(Nil, rt)
117+
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match {
118+
case ExprType(rt) if exprBecomesFunction(sym) => defn.FunctionType(Nil, rt)
114119
case _ => tp
115120
}
116-
117-
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type =
118-
if (exprBecomesFunction(sym)) transformParamInfo(tp)
119-
else elimByNameParams(tp)
120121
}

src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
6262
}
6363
}
6464
case ref =>
65-
ref.derivedSingleDenotation(ref.symbol, erasure(ref.info))
65+
ref.derivedSingleDenotation(ref.symbol, eraseInfo(ref.info))
6666
}
6767

6868
val eraser = new Erasure.Typer
@@ -350,7 +350,11 @@ object Erasure extends TypeTestsCasts{
350350

351351
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
352352
val Apply(fun, args) = tree
353-
typedExpr(fun, FunProto(args, pt, this)) match {
353+
if (tree.removeAttachment(ElimByName.ByNameArg).isDefined) {
354+
val Select(qual, nme.apply) = fun
355+
typedUnadapted(qual, pt)
356+
}
357+
else typedExpr(fun, FunProto(args, pt, this)) match {
354358
case fun1: Apply => // arguments passed in prototype were already passed
355359
fun1
356360
case fun1 =>

src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ class TreeChecker {
196196
!pt.isInstanceOf[FunProto])
197197
assert(tree.tpe <:< pt,
198198
s"error at ${sourcePos(tree.pos)}\n" +
199-
err.typeMismatchStr(tree.tpe, pt) + "tree = " + tree)
199+
err.typeMismatchStr(tree.tpe, pt) + "\ntree = " + tree)
200200
tree
201201
}
202202
}

test/dotc/tests.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,7 @@ class tests extends CompilerTest {
105105
@Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice)
106106
@Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", twice)(allowDeepSubtypes)
107107
@Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling", twice)(allowDeepSubtypes)
108-
109-
@Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice)(defaultOptions ++ List("-Ycheck:pat,era,lam"))
110-
//disabled, awaiting fix for call-by-name function types.
111-
108+
@Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice)
112109
@Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", twice)
113110
@Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice)
114111
@Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice)

0 commit comments

Comments
 (0)