summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2016-03-31 11:36:02 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2016-03-31 11:36:02 -0700
commit5e5ab186fe5b8cf047fd3da58da29dbc8f9fbd71 (patch)
treedabd389628cc48b730575786e7ef43d9f6657a00 /src/compiler/scala/tools/nsc/typechecker
parent5d7d64482011f72596a634a58138e253b7fe3531 (diff)
downloadscala-5e5ab186fe5b8cf047fd3da58da29dbc8f9fbd71.tar.gz
scala-5e5ab186fe5b8cf047fd3da58da29dbc8f9fbd71.tar.bz2
scala-5e5ab186fe5b8cf047fd3da58da29dbc8f9fbd71.zip
Clarify how/when typedFunction unrolls eta-expansion
Jason points out the recursion will be okay if type checking the function inside the eta-expansion provides fully determined argument types, as the result type is not relevant for this phase of typedFunction.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala37
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala25
2 files changed, 41 insertions, 21 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
index af8257c49b..97de2b6c85 100644
--- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
@@ -17,22 +17,27 @@ import symtab.Flags._
trait EtaExpansion { self: Analyzer =>
import global._
- /** <p>
- * Expand partial function applications of type `type`.
- * </p><pre>
- * p.f(es_1)...(es_n)
- * ==> {
- * <b>private synthetic val</b> eta$f = p.f // if p is not stable
- * ...
- * <b>private synthetic val</b> eta$e_i = e_i // if e_i is not stable
- * ...
- * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m))
- * }</pre>
- * <p>
- * tree is already attributed
- * </p>
- */
- def etaExpand(unit : CompilationUnit, tree: Tree, typer: Typer): Tree = {
+ /** Expand partial method application `p.f(es_1)...(es_n)`.
+ *
+ * We expand this to the following block, which evaluates
+ * the target of the application and its supplied arguments if needed (they are not stable),
+ * and then wraps a Function that abstracts over the missing arguments.
+ *
+ * ```
+ * {
+ * private synthetic val eta$f = p.f // if p is not stable
+ * ...
+ * private synthetic val eta$e_i = e_i // if e_i is not stable
+ * ...
+ * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m))
+ * }
+ * ```
+ *
+ * This is called from instantiateToMethodType after type checking `tree`,
+ * and we realize we have a method type, where a function type (builtin or SAM) is expected.
+ *
+ **/
+ def etaExpand(unit: CompilationUnit, tree: Tree, typer: Typer): Tree = {
val tpe = tree.tpe
var cnt = 0 // for NoPosition
def freshName() = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 8d3e9a0a91..fdf7058ab1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -244,6 +244,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
var context = context0
def context1 = context
+ // for use with silent type checking to when we can't have results with undetermined type params
+ // note that this captures the context var
+ val isMonoContext = (_: Any) => context.undetparams.isEmpty
+
def dropExistential(tp: Type): Type = tp match {
case ExistentialType(tparams, tpe) =>
new SubstWildcardMap(tparams).apply(tp)
@@ -2888,20 +2892,31 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// If we're typing `(a1: T1, ..., aN: TN) => m(a1,..., aN)`, where some Ti are not fully defined,
// type `m` directly (undoing eta-expansion of method m) to determine the argument types.
+ // This tree is the result from one of:
+ // - manual eta-expansion with named arguments (x => f(x));
+ // - wildcard-style eta expansion (`m(_, _,)`);
+ // - instantiateToMethodType adapting a tree of method type to a function type using etaExpand.
+ //
+ // Note that method values are a separate thing (`m _`): they have the idiosyncratic shape
+ // of `Typed(expr, Function(Nil, EmptyTree))`
val ptUnrollingEtaExpansion =
if (paramsMissingType.nonEmpty && pt != ErrorType) fun.body match {
+ // we can compare arguments and parameters by name because there cannot be a binder between
+ // the function's valdefs and the Apply's arguments
case Apply(meth, args) if (vparams corresponds args) { case (p, Ident(name)) => p.name == name case _ => false } =>
+ // We're looking for a method (as indicated by FUNmode in the silent typed below),
+ // so let's make sure our expected type is a MethodType
val methArgs = NoSymbol.newSyntheticValueParams(argpts map { case NoType => WildcardType case tp => tp })
- // we're looking for a method (as indicated by FUNmode), so let's make sure our expected type is a MethodType
- val methPt = MethodType(methArgs, respt)
-
- silent(_.typed(meth, mode.forFunMode, methPt)) filter (_ => context.undetparams.isEmpty) map { methTyped =>
+ silent(_.typed(meth, mode.forFunMode, MethodType(methArgs, respt))) filter (isMonoContext) map { methTyped =>
// if context.undetparams is not empty, the method was polymorphic,
// so we need the missing arguments to infer its type. See #871
val funPt = normalize(methTyped.tpe) baseType FunctionClass(numVparams)
// println(s"typeUnEtaExpanded $meth : ${methTyped.tpe} --> normalized: $funPt")
- if (isFunctionType(funPt) && isFullyDefined(funPt)) funPt
+ // If we are sure this function type provides all the necesarry info, so that we won't have
+ // any undetermined argument types, go ahead an recurse below (`typedFunction(fun, mode, ptUnrollingEtaExpansion)`)
+ // and rest assured we won't end up right back here (and keep recursing)
+ if (isFunctionType(funPt) && funPt.typeArgs.iterator.take(numVparams).forall(isFullyDefined)) funPt
else null
} orElse { _ => null }
case _ => null