diff options
author | Martin Odersky <odersky@gmail.com> | 2013-08-05 10:52:13 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-08-05 17:20:56 +0200 |
commit | a326e86d7d8389e8049a77b2cd75458f4573e294 (patch) | |
tree | c1daf83a17f692a722bf03eea84b8fb429e4579d | |
parent | dbb4b3f7923427af4ba6e04f258309421d5ee1ab (diff) | |
download | dotty-a326e86d7d8389e8049a77b2cd75458f4573e294.tar.gz dotty-a326e86d7d8389e8049a77b2cd75458f4573e294.tar.bz2 dotty-a326e86d7d8389e8049a77b2cd75458f4573e294.zip |
Type checking function trees and closures.
-rw-r--r-- | src/dotty/tools/dotc/ast/CheckTrees.scala | 15 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/Desugar.scala | 24 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/Trees.scala | 28 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/TypedTrees.scala | 16 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/UntypedTrees.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 9 | ||||
-rw-r--r-- | src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inferencing.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 53 |
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 } |