aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-08-05 10:52:13 +0200
committerMartin Odersky <odersky@gmail.com>2013-08-05 17:20:56 +0200
commita326e86d7d8389e8049a77b2cd75458f4573e294 (patch)
treec1daf83a17f692a722bf03eea84b8fb429e4579d /src/dotty/tools
parentdbb4b3f7923427af4ba6e04f258309421d5ee1ab (diff)
downloaddotty-a326e86d7d8389e8049a77b2cd75458f4573e294.tar.gz
dotty-a326e86d7d8389e8049a77b2cd75458f4573e294.tar.bz2
dotty-a326e86d7d8389e8049a77b2cd75458f4573e294.zip
Type checking function trees and closures.
Diffstat (limited to 'src/dotty/tools')
-rw-r--r--src/dotty/tools/dotc/ast/CheckTrees.scala15
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala24
-rw-r--r--src/dotty/tools/dotc/ast/Trees.scala28
-rw-r--r--src/dotty/tools/dotc/ast/TypedTrees.scala16
-rw-r--r--src/dotty/tools/dotc/ast/UntypedTrees.scala2
-rw-r--r--src/dotty/tools/dotc/core/Types.scala9
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Inferencing.scala8
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala53
9 files changed, 97 insertions, 60 deletions
diff --git a/src/dotty/tools/dotc/ast/CheckTrees.scala b/src/dotty/tools/dotc/ast/CheckTrees.scala
index b1806df4b..0bcf87dae 100644
--- a/src/dotty/tools/dotc/ast/CheckTrees.scala
+++ b/src/dotty/tools/dotc/ast/CheckTrees.scala
@@ -106,6 +106,21 @@ object CheckTrees {
case If(cond, thenp, elsep) =>
check(cond.isValue); check(thenp.isValue); check(elsep.isValue)
check(cond.tpe.derivesFrom(defn.BooleanClass))
+ case Closure(env, meth, target) =>
+ meth.tpe.widen match {
+ case mt @ MethodType(_, paramTypes) =>
+ if (target.isEmpty) {
+ check(env.length < paramTypes.length)
+ for ((arg, formal) <- env zip paramTypes)
+ check(arg.tpe <:< formal)
+ }
+ else
+ // env is stored in class, not method
+ target.tpe match {
+ case SAMType(targetMeth) =>
+ check(mt <:< targetMeth.info)
+ }
+ }
case Match(selector, cases) =>
check(selector.isValue)
// are any checks that relate selector and patterns desirable?
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 9f91d3143..0bd98fd2e 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -236,6 +236,12 @@ object desugar {
case tree: ModuleDef => moduleDef(tree)
}
+ /** Make closure corresponding to function params => body */
+ def makeClosure(params: List[ValDef], body: Tree) =
+ Block(
+ DefDef(Modifiers(Synthetic), nme.ANON_FUN, Nil, params :: Nil, EmptyTree, body),
+ Closure(Nil, Ident(nme.ANON_FUN), EmptyTree))
+
def apply(tree: Tree)(implicit ctx: Context): Tree = {
def labelDefAndCall(lname: TermName, rhs: Tree, call: Tree) = {
@@ -268,16 +274,10 @@ object desugar {
}
}
- /** Make closure corresponding to function params => body */
- def makeClosure(params: List[ValDef], body: Tree) =
- Block(
- DefDef(Modifiers(Synthetic), nme.ANON_FUN, Nil, params :: Nil, EmptyTree, body),
- Closure(Nil, Ident(nme.ANON_FUN)))
-
/** Make closure corresponding to partial function { cases } */
- def makeCaseClosure(cases: List[CaseDef]) = {
+ def makeCaseLambda(cases: List[CaseDef]) = {
val param = makeSyntheticParameter()
- makeClosure(param :: Nil, Match(Ident(param.name), cases))
+ Function(param :: Nil, Match(Ident(param.name), cases))
}
/** Create tree for for-comprehension <for (enums) do body> or
@@ -336,9 +336,9 @@ object desugar {
*/
def makeLambda(pat: Tree, body: Tree): Tree = pat match {
case VarPattern(named, tpt) =>
- makeClosure(derivedValDef(Modifiers(Param), named, tpt, EmptyTree) :: Nil, body)
+ Function(derivedValDef(Modifiers(Param), named, tpt, EmptyTree) :: Nil, body)
case _ =>
- makeCaseClosure(CaseDef(pat, EmptyTree, body) :: Nil)
+ makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil)
}
/** If `pat` is not yet a `Bind` wrap it in one with a fresh name
@@ -477,7 +477,7 @@ object desugar {
case If(cond, thenp, EmptyTree) =>
If(cond, thenp, unitLiteral)
case Match(EmptyTree, cases) =>
- makeCaseClosure(cases)
+ makeCaseLambda(cases)
case tree: MemberDef =>
memberDef(tree)
case SymbolLit(str) =>
@@ -490,7 +490,7 @@ object desugar {
ref(defn.FunctionClass(args.length).typeConstructor),
args :+ body)
else
- makeClosure(args.asInstanceOf[List[ValDef]], body)
+ tree // was: makeClosure(args.asInstanceOf[List[ValDef]], body)
case InfixOp(l, op, r) =>
if (ctx.mode is Mode.Type)
AppliedTypeTree(Ident(op), l :: r :: Nil) // op[l, r]
diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index cfc603501..1941412c6 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -435,8 +435,16 @@ object Trees {
type ThisTree[-T >: Untyped] = If[T]
}
- /** A closure with an environment and a reference to a method */
- case class Closure[-T >: Untyped] private[ast] (env: List[Tree[T]], meth: Tree[T])
+ /** A closure with an environment and a reference to a method.
+ * @param env The captured parameters of the closure
+ * @param meth A ref tree that refers to the method of the closure.
+ * The first (env.length) parameters of that method are filled
+ * with env values.
+ * @param tpt Either EmptyTree or a TypeTree. If tpt is EmptyTree the type
+ * of the closure is a function type, otherwise it is the type
+ * given in `tpt`, which must be a SAM type.
+ */
+ case class Closure[-T >: Untyped] private[ast] (env: List[Tree[T]], meth: Tree[T], tpt: Tree[T])
extends TermTree[T] {
type ThisTree[-T >: Untyped] = Closure[T]
}
@@ -847,9 +855,9 @@ object Trees {
case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree
case _ => finalize(tree, untpd.If(cond, thenp, elsep))
}
- def Closure(tree: Tree, env: List[Tree], meth: Tree): Closure = tree match {
- case tree: Closure if (env eq tree.env) && (meth eq tree.meth) => tree
- case _ => finalize(tree, untpd.Closure(env, meth))
+ def Closure(tree: Tree, env: List[Tree], meth: Tree, tpt: Tree): Closure = tree match {
+ case tree: Closure if (env eq tree.env) && (meth eq tree.meth) && (tpt eq tree.tpt) => tree
+ case _ => finalize(tree, untpd.Closure(env, meth, tpt))
}
def Match(tree: Tree, selector: Tree, cases: List[CaseDef]): Match = tree match {
case tree: Match if (selector eq tree.selector) && (cases eq tree.cases) => tree
@@ -985,8 +993,8 @@ object Trees {
cpy.Block(tree, transformStats(stats), transform(expr))
case If(cond, thenp, elsep) =>
cpy.If(tree, transform(cond), transform(thenp), transform(elsep))
- case Closure(env, meth) =>
- cpy.Closure(tree, transform(env), transform(meth))
+ case Closure(env, meth, tpt) =>
+ cpy.Closure(tree, transform(env), transform(meth), transform(tpt))
case Match(selector, cases) =>
cpy.Match(tree, transform(selector), transformSub(cases))
case CaseDef(pat, guard, body) =>
@@ -1090,8 +1098,8 @@ object Trees {
this(this(x, stats), expr)
case If(cond, thenp, elsep) =>
this(this(this(x, cond), thenp), elsep)
- case Closure(env, meth) =>
- this(this(x, env), meth)
+ case Closure(env, meth, tpt) =>
+ this(this(this(x, env), meth), tpt)
case Match(selector, cases) =>
this(this(x, selector), cases)
case CaseDef(pat, guard, body) =>
@@ -1205,7 +1213,7 @@ object Trees {
finishBlock(tree.derivedBlock(transform(stats, c), transform(expr, c)), tree, c, plugins)
case If(cond, thenp, elsep) =>
finishIf(tree.derivedIf(transform(cond, c), transform(thenp, c), transform(elsep, c)), tree, c, plugins)
- case Closure(env, meth) =>
+ case Closure(env, meth, tpt) =>
finishClosure(tree.derivedClosure(transform(env, c), transformSub(meth, c)), tree, c, plugins)
case Match(selector, cases) =>
finishMatch(tree.derivedMatch(transform(selector, c), transformSub(cases, c)), tree, c, plugins)
diff --git a/src/dotty/tools/dotc/ast/TypedTrees.scala b/src/dotty/tools/dotc/ast/TypedTrees.scala
index 037180161..94c747ae1 100644
--- a/src/dotty/tools/dotc/ast/TypedTrees.scala
+++ b/src/dotty/tools/dotc/ast/TypedTrees.scala
@@ -104,14 +104,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If =
untpd.If(cond, thenp, elsep).withType(thenp.tpe | elsep.tpe).checked
- def Closure(env: List[Tree], meth: RefTree)(implicit ctx: Context): Closure =
- untpd.Closure(env, meth).withType(closureType(meth.tpe.widen)).checked
-
- def closureType(tp: Type)(implicit ctx: Context) = tp match {
- case mt @ MethodType(_, formals) =>
- assert(!mt.isDependent)
- val formals1 = formals mapConserve (_.underlyingIfRepeated)
- defn.FunctionType(formals1, mt.resultType)
+ def Closure(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = {
+ val ownType = if (tpt.isEmpty) meth.tpe.widen.toFunctionType else tpt.tpe
+ untpd.Closure(env, meth, tpt).withType(ownType).checked
}
/** A function def
@@ -125,11 +120,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
* where the closure's type is the target type of the expression (FunctionN, unless
* otherwise specified).
*/
- def Closure(meth: TermSymbol, bodyFn: List[Tree] => Tree)(implicit ctx: Context): Block = {
+ def Closure(meth: TermSymbol, bodyFn: List[Tree] => Tree, targetType: Type = NoType)(implicit ctx: Context): Block = {
val rhsFn: List[List[Tree]] => Tree = { case args :: Nil => bodyFn(args) }
+ val targetTpt = if (targetType.exists) TypeTree(targetType) else EmptyTree
Block(
DefDef(meth, rhsFn) :: Nil,
- Closure(Nil, Ident(TermRef.withSym(NoPrefix, meth))))
+ Closure(Nil, Ident(TermRef.withSym(NoPrefix, meth)), targetTpt))
}
def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match =
diff --git a/src/dotty/tools/dotc/ast/UntypedTrees.scala b/src/dotty/tools/dotc/ast/UntypedTrees.scala
index 79bef00e8..964d3a5a4 100644
--- a/src/dotty/tools/dotc/ast/UntypedTrees.scala
+++ b/src/dotty/tools/dotc/ast/UntypedTrees.scala
@@ -63,7 +63,7 @@ object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] {
def Assign(lhs: Tree, rhs: Tree): Assign = new Assign(lhs, rhs)
def Block(stats: List[Tree], expr: Tree): Block = new Block(stats, expr)
def If(cond: Tree, thenp: Tree, elsep: Tree): If = new If(cond, thenp, elsep)
- def Closure(env: List[Tree], meth: Tree): Closure = new Closure(env, meth)
+ def Closure(env: List[Tree], meth: Tree, tpt: Tree): Closure = new Closure(env, meth, tpt)
def Match(selector: Tree, cases: List[CaseDef]): Match = new Match(selector, cases)
def CaseDef(pat: Tree, guard: Tree, body: Tree): CaseDef = new CaseDef(pat, guard, body)
def Return(expr: Tree, from: Tree): Return = new Return(expr, from)
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 8df651180..07f3f284f 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -913,6 +913,15 @@ object Types {
// ----- misc -----------------------------------------------------------
+ /** Turn type into a function type.
+ * @pre this is a non-dependent method type.
+ */
+ def toFunctionType(implicit ctx: Context): Type = this match {
+ case mt @ MethodType(_, formals) if !mt.isDependent =>
+ val formals1 = formals mapConserve (_.underlyingIfRepeated)
+ defn.FunctionType(formals1, mt.resultType)
+ }
+
/** The signature of this type. This is by default NotAMethod,
* but is overridden for PolyTypes, MethodTypes, and TermRefWithSignature types.
* (the reason why we deviate from the "final-method-with-pattern-match-in-base-class"
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index a8c6d5bcf..75b3db186 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -167,7 +167,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
changePrec(GlobalPrec) {
"if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _)
}
- case Closure(env, ref) =>
+ case Closure(env, ref, _) =>
if (env.isEmpty) toText(ref)
else "closure<" ~ toTextGlobal(env, ", ") ~ " | " ~ toTextGlobal(ref) ~ ">"
case Match(sel, cases) =>
diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala
index 5c9c86c0c..ff38ef997 100644
--- a/src/dotty/tools/dotc/typer/Inferencing.scala
+++ b/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -21,20 +21,20 @@ object Inferencing {
* - the overall result of `isFullYDefined` is `true`.
* Variables that are succesfully minimized do not count as uninstantiated.
*/
- def isFullyDefined(tp: Type)(implicit ctx: Context): Boolean = {
+ def isFullyDefined(tp: Type, forceIt: Boolean = false)(implicit ctx: Context): Boolean = {
val nestedCtx = ctx.fresh.withNewTyperState
- val result = new IsFullyDefinedAccumulator()(nestedCtx).traverse(tp)
+ val result = new IsFullyDefinedAccumulator(forceIt)(nestedCtx).traverse(tp)
if (result) nestedCtx.typerState.commit()
result
}
- private class IsFullyDefinedAccumulator(implicit ctx: Context) extends TypeAccumulator[Boolean] {
+ private class IsFullyDefinedAccumulator(forceIt: Boolean)(implicit ctx: Context) extends TypeAccumulator[Boolean] {
def traverse(tp: Type): Boolean = apply(true, tp)
def apply(x: Boolean, tp: Type) = !x || isOK(tp) && foldOver(x, tp)
def isOK(tp: Type): Boolean = tp match {
case _: WildcardType =>
false
- case tvar: TypeVar if !tvar.isInstantiated =>
+ case tvar: TypeVar if forceIt && !tvar.isInstantiated =>
val inst = tvar.instantiate(fromBelow = true)
inst != defn.NothingType && inst != defn.NullType
case _ =>
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index ffd4b2ce1..ea1fa3729 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -412,38 +412,36 @@ class Typer extends Namer with Applications with Implicits {
val protoFormals: List[Type] = pt match {
case _ if pt.typeSymbol == defn.FunctionClass(params.length) =>
pt.typeArgs take params.length
- case MethodType(_, paramTypes) =>
+ case SAMType(meth) =>
+ val MethodType(_, paramTypes) = meth.info
paramTypes
case _ =>
params map Function.const(WildcardType)
}
- val inferredParams =
- for ((param, formal) <- (params, protoFormals).zipped)
- if (param.tpt.isEmpty && isFullyDefined(formal))
- cpy.ValDef(param, param.mods, param.name, untpd.TypeTree(formal), param.rhs)
- else
- param
-
- ???
+ val inferredParams: List[untpd.ValDef] =
+ for ((param, formal) <- params zip protoFormals) yield
+ if (!param.tpt.isEmpty) param
+ else {
+ val paramType =
+ if (isFullyDefined(formal)) formal
+ else errorType(s"missing parameter type", param.pos)
+ cpy.ValDef(param, param.mods, param.name, untpd.TypeTree(paramType), param.rhs)
+ }
+ typed(desugar.makeClosure(inferredParams, tree.body), pt)
}
def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = {
val env1 = tree.env map (typed(_))
val meth1 = typed(tree.meth)
- pt match {
- case SAMType(meth) if !defn.isFunctionType(pt) =>
- ???
- case _ =>
- val ownType = meth1.tpe.widen match {
- case mt: MethodType if !mt.isDependent =>
- closureType(mt)
- case mt: MethodType =>
- errorType(s"cannot turn dependent method types into closures", tree.pos)
- case tp =>
- errorType(s"internal error: closing over non-method $tp", tree.pos)
- }
- cpy.Closure(tree, env1, meth1).withType(ownType)
+ val ownType = meth1.tpe.widen match {
+ case mt: MethodType if !mt.isDependent =>
+ mt.toFunctionType
+ case mt: MethodType =>
+ errorType(s"internal error: cannot turn dependent method type $mt into closure", tree.pos)
+ case tp =>
+ errorType(s"internal error: closing over non-method $tp", tree.pos)
}
+ cpy.Closure(tree, env1, meth1, EmptyTree).withType(ownType)
}
def typedModifiers(mods: untpd.Modifiers)(implicit ctx: Context): Modifiers = {
@@ -704,6 +702,17 @@ class Typer extends Namer with Applications with Implicits {
if (ctx.mode.isExpr) {
if (pt.typeSymbol == defn.UnitClass)
return tpd.Block(tree :: Nil, Literal(Constant()))
+ tree match {
+ case Closure(Nil, id @ Ident(nme.ANON_FUN), _)
+ if defn.isFunctionType(tree.tpe) && !defn.isFunctionType(pt) =>
+ pt match {
+ case SAMType(meth)
+ if tree.tpe <:< meth.info.toFunctionType && isFullyDefined(pt, forceIt = false) =>
+ return cpy.Closure(tree, Nil, id, TypeTree(pt)).withType(pt)
+ case _ =>
+ }
+ case _ =>
+ }
val adapted = inferView(tree, pt)
if (adapted ne EmptyTree) return adapted
}