Skip to content

Commit 33869c3

Browse files
committed
Fix typing of dependent closures
1 parent e15ebaa commit 33869c3

File tree

6 files changed

+44
-31
lines changed

6 files changed

+44
-31
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,8 @@ class Definitions {
702702
val tsym = ft.typeSymbol
703703
if (isFunctionClass(tsym)) {
704704
val targs = ft.dealias.argInfos
705-
Some(targs.init, targs.last, tsym.name.isImplicitFunction)
705+
if (targs.isEmpty) None
706+
else Some(targs.init, targs.last, tsym.name.isImplicitFunction)
706707
}
707708
else None
708709
}
@@ -921,6 +922,11 @@ class Definitions {
921922
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol)
922923
}
923924

925+
def isDependentFunctionType(tp: Type)(implicit ctx: Context) = tp.stripTypeVar match {
926+
case RefinedType(parent, nme.apply, _) => isFunctionType(parent)
927+
case _ => false
928+
}
929+
924930
// Specialized type parameters defined for scala.Function{0,1,2}.
925931
private lazy val Function1SpecializedParams: collection.Set[Type] =
926932
Set(IntType, LongType, FloatType, DoubleType)

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,12 @@ object Types {
10141014
case _ => this
10151015
}
10161016

1017+
/** If this is a dependent function type, drop the `apply` refinement */
1018+
final def dropDependentRefinement(implicit ctx: Context): Type = stripTypeVar match {
1019+
case RefinedType(parent, nme.apply, _) => parent
1020+
case _ => this
1021+
}
1022+
10171023
/** The type constructor of an applied type, otherwise the type itself */
10181024
final def typeConstructor(implicit ctx: Context): Type = this match {
10191025
case AppliedType(tycon, _) => tycon
@@ -1317,10 +1323,13 @@ object Types {
13171323
* when forming the function type.
13181324
*/
13191325
def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match {
1320-
case mt: MethodType if !mt.isDependent =>
1326+
case mt: MethodType if !mt.isParamDependent =>
13211327
val formals1 = if (dropLast == 0) mt.paramInfos else mt.paramInfos dropRight dropLast
1322-
defn.FunctionOf(
1323-
formals1 mapConserve (_.underlyingIfRepeated(mt.isJavaMethod)), mt.resultType, mt.isImplicitMethod && !ctx.erasedTypes)
1328+
val funType = defn.FunctionOf(
1329+
formals1 mapConserve (_.underlyingIfRepeated(mt.isJavaMethod)),
1330+
mt.nonDependentResultApprox, mt.isImplicitMethod && !ctx.erasedTypes)
1331+
if (mt.isDependent) RefinedType(funType, nme.apply, mt)
1332+
else funType
13241333
}
13251334

13261335
/** The signature of this type. This is by default NotAMethod,
@@ -3745,7 +3754,7 @@ object Types {
37453754
// println(s"absMems: ${absMems map (_.show) mkString ", "}")
37463755
if (absMems.size == 1)
37473756
absMems.head.info match {
3748-
case mt: MethodType if !mt.isDependent => Some(absMems.head)
3757+
case mt: MethodType if !mt.isParamDependent => Some(absMems.head)
37493758
case _ => None
37503759
}
37513760
else if (tp isRef defn.PartialFunctionClass)

compiler/src/dotty/tools/dotc/typer/Dynamic.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ trait Dynamic { self: Typer with Applications =>
144144

145145
tree.tpe.widen match {
146146
case tpe: MethodType =>
147-
if (tpe.isDependent)
148-
fail(i"has a dependent method type")
147+
if (tpe.isParamDependent)
148+
fail(i"has a method type with inter-parameter dependencies")
149149
else if (tpe.paramNames.length > Definitions.MaxStructuralMethodArity)
150150
fail(i"""takes too many parameters.
151151
|Structural types only support methods taking up to ${Definitions.MaxStructuralMethodArity} arguments""")

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,12 +1150,8 @@ class Namer { typer: Typer =>
11501150
vparamss foreach completeParams
11511151
def typeParams = tparams map symbolOfTree
11521152
val paramSymss = ctx.normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor)
1153-
def wrapMethType(restpe: Type): Type = {
1154-
val restpe1 = // try to make anonymous functions non-dependent, so that they can be used in closures
1155-
if (name == nme.ANON_FUN) avoid(restpe, paramSymss.flatten)
1156-
else restpe
1157-
ctx.methodType(tparams map symbolOfTree, paramSymss, restpe1, isJava = ddef.mods is JavaDefined)
1158-
}
1153+
def wrapMethType(restpe: Type): Type =
1154+
ctx.methodType(tparams map symbolOfTree, paramSymss, restpe, isJava = ddef.mods is JavaDefined)
11591155
if (isConstructor) {
11601156
// set result type tree to unit, but take the current class as result type of the symbol
11611157
typedAheadType(ddef.tpt, defn.UnitType)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -688,10 +688,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
688688
// if expected parameter type(s) are wildcards, approximate from below.
689689
// if expected result type is a wildcard, approximate from above.
690690
// this can type the greatest set of admissible closures.
691-
(pt.dealias.argTypesLo.init, pt.dealias.argTypesHi.last)
691+
val funType = pt.dealias
692+
(funType.argTypesLo.init, funType.argTypesHi.last)
692693
case SAMType(meth) =>
693-
val MethodTpe(_, formals, restpe) = meth.info
694-
(formals, restpe)
694+
val mt @ MethodTpe(_, formals, restpe) = meth.info
695+
(formals, if (mt.isDependent) WildcardType else restpe)
695696
case _ =>
696697
(List.tabulate(defaultArity)(alwaysWildcardType), WildcardType)
697698
}
@@ -891,13 +892,17 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
891892
meth1.tpe.widen match {
892893
case mt: MethodType =>
893894
pt match {
894-
case SAMType(meth) if !defn.isFunctionType(pt) && mt <:< meth.info =>
895+
case SAMType(meth)
896+
if !defn.isFunctionType(pt.dealias.dropDependentRefinement) && mt <:< meth.info =>
895897
if (!isFullyDefined(pt, ForceDegree.all))
896898
ctx.error(ex"result type of closure is an underspecified SAM type $pt", tree.pos)
897899
TypeTree(pt)
898900
case _ =>
899-
if (!mt.isDependent) EmptyTree
900-
else throw new java.lang.Error(i"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}, raw type = ${mt.toString}") // !!! DEBUG. Eventually, convert to an error?
901+
if (!mt.isParamDependent) EmptyTree
902+
else throw new java.lang.Error(
903+
i"""internal error: cannot turn method type $mt into closure
904+
|because it has internal parameter dependencies,
905+
|position = ${tree.pos}, raw type = ${mt.toString}""") // !!! DEBUG. Eventually, convert to an error?
901906
}
902907
case tp =>
903908
throw new java.lang.Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}")
@@ -1331,7 +1336,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
13311336
val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef])
13321337
vparamss1.foreach(checkNoForwardDependencies)
13331338
if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1)
1334-
var tpt1 = checkSimpleKinded(typedType(tpt))
1339+
val tpt1 = checkSimpleKinded(typedType(tpt))
13351340

13361341
var rhsCtx = ctx
13371342
if (sym.isConstructor && !sym.isPrimaryConstructor && tparams1.nonEmpty) {
@@ -1347,13 +1352,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
13471352
// Overwrite inline body to make sure it is not evaluated twice
13481353
if (sym.isInlineMethod) Inliner.registerInlineInfo(sym, _ => rhs1)
13491354

1350-
if (sym.isAnonymousFunction) {
1351-
// If we define an anonymous function, make sure the return type does not
1352-
// refer to parameters. This is necessary because closure types are
1353-
// function types so no dependencies on parameters are allowed.
1354-
tpt1 = tpt1.withType(avoid(tpt1.tpe, vparamss1.flatMap(_.map(_.symbol))))
1355-
}
1356-
13571355
assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym)
13581356
//todo: make sure dependent method types do not depend on implicits or by-name params
13591357
}

tests/pos/depfuntype.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ object Test {
33
trait C { type M; val m: M }
44

55
type DF = (x: C) => x.M
6-
val depfun: DF = ??? // (x: C) => x.m
6+
val depfun1: DF = (x: C) => x.m
77
val c = new C { type M = Int; val m = 0 }
8-
val y = depfun(c)
8+
val y = depfun1(c)
99
val y1: Int = y
1010

11+
def depmeth(x: C) = x.m
12+
val depfun2 = depmeth
13+
val depfun3: DF = depfun2
14+
1115
val d: C = c
12-
val z = depfun(d)
16+
val z = depfun1(d)
1317
val z1: d.M = z
14-
}
18+
}

0 commit comments

Comments
 (0)