summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2016-03-30 17:17:34 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2016-03-30 20:07:34 -0700
commit8e32d00e1679114ee1b3f9a90f235d2ab9feba2e (patch)
treeb7bb9f449797b561baf0a10a7eab004f01a8cf24 /src/compiler
parent7025be9a468419ca6076d78f8da32c6a667fc829 (diff)
downloadscala-8e32d00e1679114ee1b3f9a90f235d2ab9feba2e.tar.gz
scala-8e32d00e1679114ee1b3f9a90f235d2ab9feba2e.tar.bz2
scala-8e32d00e1679114ee1b3f9a90f235d2ab9feba2e.zip
Keep Function when CBN arg thunk targets a SAM
The body of `def delay[T](v: => T) = (v _): F0[T]` becomes `() => v` during `typedEta`, and then uncurry considers whether to strip the function wrapper since `v` is known to be a `Function0` thunk. Stripping is sound when the expected type is `Function0` for this expression, but that's no longer a given, since we could be expecting any nullary SAM. Also sweep up a bit around `typedEta`. Encapsulate the, erm, creative encoding of `m _` as `Typed(m, Function(Nil, EmptyTree))`.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala4
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala7
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala56
4 files changed, 41 insertions, 29 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index d4715471f6..9c0174d89b 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -1713,9 +1713,7 @@ self =>
}
simpleExprRest(app, canApply = true)
case USCORE =>
- atPos(t.pos.start, in.skipToken()) {
- Typed(stripParens(t), Function(Nil, EmptyTree))
- }
+ atPos(t.pos.start, in.skipToken()) { makeMethodValue(stripParens(t)) }
case _ =>
t
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index 473a40f42a..1e9a1762eb 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -35,6 +35,9 @@ abstract class TreeBuilder {
def repeatedApplication(tpe: Tree): Tree =
AppliedTypeTree(rootScalaDot(tpnme.REPEATED_PARAM_CLASS_NAME), List(tpe))
+ // represents `expr _`, as specified in Method Values of spec/06-expressions.md
+ def makeMethodValue(expr: Tree): Tree = Typed(expr, Function(Nil, EmptyTree))
+
def makeImportSelector(name: Name, nameOffset: Int): ImportSelector =
ImportSelector(name, nameOffset, name, nameOffset)
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 44d2469621..628090dba5 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -205,8 +205,11 @@ abstract class UnCurry extends InfoTransform
*
*/
def transformFunction(fun: Function): Tree =
- // Undo eta expansion for parameterless and nullary methods
- if (fun.vparams.isEmpty && isByNameRef(fun.body)) { noApply += fun.body ; fun.body }
+ // 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),
+ // 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 }
else if (forceExpandFunction || inConstructorFlag != 0) {
// Expand the function body into an anonymous class
gen.expandFunction(localTyper)(fun, inConstructorFlag)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index ff0513156b..d7c577e97c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2879,8 +2879,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 =>
// if context.undetparams is not empty, the function was polymorphic,
// so we need the missing arguments to infer its type. See #871
- //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams)
val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams)
+// println(s"typeUnEtaExpanded $fn : ${fn1.tpe} (unwrapped $fun) --> normalized: $ftpe")
+
if (isFunctionType(ftpe) && isFullyDefined(ftpe)) ftpe
else NoType
} orElse { _ => NoType }
@@ -4365,28 +4366,35 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ => tp
}
- def expectingFunctionMatchingFormals(formals: List[Symbol]) =
- isFunctionType(pt) || samMatchesFunctionBasedOnArity(samOf(pt), formals)
-
- def typedEta(expr1: Tree): Tree = expr1.tpe match {
- case TypeRef(_, ByNameParamClass, _) =>
- val expr2 = Function(List(), expr1) setPos expr1.pos
- new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2)
- typed1(expr2, mode, pt)
- case NullaryMethodType(restpe) =>
- val expr2 = Function(List(), expr1) setPos expr1.pos
- new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2)
- typed1(expr2, mode, pt)
- case PolyType(_, MethodType(formals, _)) =>
- if (expectingFunctionMatchingFormals(formals)) expr1
- else adapt(expr1, mode, checkArity(expr1)(functionTypeWildcard(formals.length)))
- case MethodType(formals, _) =>
- if (expectingFunctionMatchingFormals(formals)) expr1
- else adapt(expr1, mode, checkArity(expr1)(functionTypeWildcard(formals.length)))
+
+ /** Eta expand an expression like `m _`, where `m` denotes a method or a by-name argument
+ *
+ * The spec says:
+ * The expression `$e$ _` is well-formed if $e$ is of method type or if $e$ is a call-by-name parameter.
+ * (1) If $e$ is a method with parameters, `$e$ _` represents $e$ converted to a function type
+ * by [eta expansion](#eta-expansion).
+ * (2) If $e$ is a parameterless method or call-by-name parameter of type `=>$T$`, `$e$ _` represents
+ * the function of type `() => $T$`, which evaluates $e$ when it is applied to the empty parameterlist `()`.
+ */
+ def typedEta(methodValue: Tree): Tree = methodValue.tpe match {
+ case tp@(MethodType(_, _) | PolyType(_, MethodType(_, _))) => // (1)
+ val formals = tp.params
+ if (isFunctionType(pt) || samMatchesFunctionBasedOnArity(samOf(pt), formals)) methodValue
+ else adapt(methodValue, mode, checkArity(methodValue)(functionTypeWildcard(formals.length)))
+
+ case TypeRef(_, ByNameParamClass, _) | NullaryMethodType(_) => // (2)
+ val pos = methodValue.pos
+ // must create it here to change owner (normally done by typed's typedFunction)
+ val funSym = context.owner.newAnonymousFunctionValue(pos)
+ new ChangeOwnerTraverser(context.owner, funSym) traverse methodValue
+
+ typed(Function(List(), methodValue) setSymbol funSym setPos pos, mode, pt)
+
case ErrorType =>
- expr1
+ methodValue
+
case _ =>
- UnderscoreEtaError(expr1)
+ UnderscoreEtaError(methodValue)
}
def tryTypedArgs(args: List[Tree], mode: Mode): Option[List[Tree]] = {
@@ -4430,7 +4438,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case Annotated(_, r) => treesInResult(r)
case If(_, t, e) => treesInResult(t) ++ treesInResult(e)
case Try(b, catches, _) => treesInResult(b) ++ catches
- case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r)
+ case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) // a method value
case Select(qual, name) => treesInResult(qual)
case Apply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult)
case TypeApply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult)
@@ -5070,11 +5078,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// because `expr` might contain nested macro calls (see SI-6673)
//
// Note: apparently `Function(Nil, EmptyTree)` is the secret parser marker
- // which means trailing underscore.
+ // which means trailing underscore -- denoting a method value. See makeMethodValue in TreeBuilder.
case Typed(expr, Function(Nil, EmptyTree)) =>
typed1(suppressMacroExpansion(expr), mode, pt) match {
case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(macroDef)
- case exprTyped => typedEta(checkDead(exprTyped))
+ case methodValue => typedEta(checkDead(methodValue))
}
case Typed(expr, tpt) =>
val tpt1 = typedType(tpt, mode) // type the ascribed type first