From e2a05a5ac38647f9727d1e0ec8c3c14ac82b5de7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 27 Aug 2013 14:12:31 +0200 Subject: Fixed bugs related to typechecking closures. --- src/dotty/tools/dotc/Main.scala | 2 +- src/dotty/tools/dotc/ast/Desugar.scala | 5 ++--- src/dotty/tools/dotc/ast/Trees.scala | 4 +++- src/dotty/tools/dotc/core/TypeComparer.scala | 2 -- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 ++-- src/dotty/tools/dotc/typer/Inferencing.scala | 13 +++++++++++-- src/dotty/tools/dotc/typer/Namer.scala | 4 ++-- src/dotty/tools/dotc/typer/Typer.scala | 13 ++++++++----- tests/pos/functions1.scala | 13 +++++++++++++ tests/pos/implicits1.scala | 16 +++++++++++++++- 10 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 tests/pos/functions1.scala diff --git a/src/dotty/tools/dotc/Main.scala b/src/dotty/tools/dotc/Main.scala index fb2733c27..9fe580b8a 100644 --- a/src/dotty/tools/dotc/Main.scala +++ b/src/dotty/tools/dotc/Main.scala @@ -17,7 +17,7 @@ object Main extends Driver { override def newCompiler(): Compiler = new Compiler override def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Unit = { - if (new config.Settings.Setting.SettingDecorator(ctx.base.settings.resident).value) resident(compiler) + if (new config.Settings.Setting.SettingDecorator(ctx.base.settings.resident).value(ctx)) resident(compiler) else super.doCompile(compiler, fileNames) } } diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 52c9cd187..f998ddec2 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -285,7 +285,7 @@ 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), + DefDef(Modifiers(Synthetic), nme.ANON_FUN, Nil, params :: Nil, TypeTree(), body), Closure(Nil, Ident(nme.ANON_FUN), EmptyTree)) /** Make closure corresponding to partial function { cases } */ @@ -482,8 +482,7 @@ object desugar { AppliedTypeTree(ref(defn.RepeatedParamType), t) else { assert(ctx.mode.isExpr, ctx.mode) - if (op == nme.WILDCARD) tree // desugar later by eta expansion - else Select(t, op) + Select(t, op) } case PrefixOp(op, t) => if ((ctx.mode is Mode.Type) && op == nme.ARROWkw) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 254818b27..2392e52be 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -186,7 +186,7 @@ object Trees { withTypeUnchecked(tpe) } - def withTypeUnchecked(tpe: Type): ThisTree[Type] = { + def withTypeUnchecked(tpe: Type): ThisTree[Type] = { val tree = (if (myTpe == null || (myTpe.asInstanceOf[AnyRef] eq tpe.asInstanceOf[AnyRef])) this @@ -524,6 +524,8 @@ object Trees { type ThisTree[-T >: Untyped] = TypeTree[T] override def initialPos = NoPosition override def isEmpty = !hasType && original.isEmpty + override def toString = + s"TypeTree${if (hasType) s"[$typeOpt]" else s"($original)"}" } /** ref.type */ diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 3cf15479d..b7ae92b46 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -138,8 +138,6 @@ class TypeComparer(implicit val ctx: Context) extends DotClass { } case tp2: AnnotatedType => isSubType(tp1, tp2.tpe) // todo: refine? - case tp2: ProtoType => - tp2.isMatchedBy(tp1) case ErrorType => true case _ => diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 79641d932..3ee86f665 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -171,8 +171,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "if " ~ toText(cond) ~ (" then" provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(" else " ~ _) } case Closure(env, ref, _) => - if (env.isEmpty) toText(ref) - else "closure<" ~ toTextGlobal(env, ", ") ~ " | " ~ toTextGlobal(ref) ~ ">" + "closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~ + toTextGlobal(ref) ~ ")" case Match(sel, cases) => if (sel.isEmpty) blockText(cases) else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) } diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index b8f0e245b..a9d0b287a 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -27,8 +27,8 @@ object Inferencing { * 3. there is an implicit conversion from `tp` to `pt`. */ def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean = ( - tp <:< pt - || pt.typeSymbol == defn.ByNameParamClass && tp <:< pt.typeArgs.head + conforms(tp, pt) + || pt.typeSymbol == defn.ByNameParamClass && conforms(tp, pt.typeArgs.head) || viewExists(tp, pt)) } @@ -83,6 +83,10 @@ object Inferencing { case class PolyProto(nargs: Int, override val resultType: Type) extends UncachedGroundType + object AnyFunctionProto extends UncachedGroundType with ProtoType { + def isMatchedBy(tp: Type)(implicit ctx: Context) = true + } + /** The normalized form of a type * - unwraps polymorphic types, tracking their parameters in the current constraint * - skips implicit parameters @@ -131,6 +135,11 @@ object Inferencing { case tp => tp } + def conforms(tpe: Type, pt: Type)(implicit ctx: Context): Boolean = pt match { + case pt: ProtoType => pt.isMatchedBy(tpe) + case _ => tpe <:< pt + } + def checkBounds(args: List[Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = { } diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 6fe6182b4..887970b67 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -71,9 +71,8 @@ class Namer { typer: Typer => */ lazy val symOfTree = new mutable.HashMap[Tree, Symbol] - /** A map from expanded trees their typed versions. + /** A map from expanded trees to their typed versions. * Populated when trees are typechecked during completion (using method typedAhead). - * Emptied during typer. */ lazy val typedTree = new mutable.HashMap[Tree, tpd.Tree] @@ -189,6 +188,7 @@ class Namer { typer: Typer => /** The expansion of a member def */ def expansion(mdef: DefTree)(implicit ctx: Context): Tree = { val expanded = desugar.defTree(mdef) + println(i"Expansion: $mdef expands to $expanded") if (expanded ne mdef) expandedTree(mdef) = expanded expanded } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index a904aea69..32c7bbea4 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -483,7 +483,7 @@ class Typer extends Namer with Applications with Implicits { def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = { val env1 = tree.env mapconserve (typed(_)) - val meth1 = typed(tree.meth) + val meth1 = typedExpanded(tree.meth) val ownType = meth1.tpe.widen match { case mt: MethodType if !mt.isDependent => mt.toFunctionType @@ -806,7 +806,8 @@ class Typer extends Namer with Applications with Implicits { case tree: untpd.Bind => typedBind(tree, pt) case tree: untpd.Alternative => typedAlternative(tree, pt) case tree: untpd.ValDef => - typedValDef(tree, sym)(localContext) + if (tree.isEmpty) tpd.EmptyValDef + else typedValDef(tree, sym)(localContext) case tree: untpd.DefDef => val typer1 = nestedTyper.remove(sym).get typer1.typedDefDef(tree, sym)(localContext.withTyper(typer1)) @@ -817,6 +818,7 @@ class Typer extends Namer with Applications with Implicits { case tree: untpd.PackageDef => typedPackageDef(tree) case tree: untpd.Annotated => typedAnnotated(tree, pt) case tree: untpd.TypedSplice => tree.tree + case untpd.PostfixOp(tree, nme.WILDCARD) => typed(tree, AnyFunctionProto) case untpd.EmptyTree => tpd.EmptyTree case _ => typed(desugar(tree), pt) } @@ -971,7 +973,7 @@ class Typer extends Namer with Applications with Implicits { case wtp: ExprType => adapt(tree.withType(wtp.resultType), pt) case wtp: ImplicitMethodType => - val args = (wtp.paramNames, wtp.paramTypes).zipped map { (pname, formal) => + val args = (wtp.paramNames, wtp.paramTypes).zipped(identity, identity) map { (pname, formal) => val arg = inferImplicit(formal, EmptyTree, tree.pos.endPos) if (arg.isEmpty) ctx.error(i"no implicit argument of type $formal found for parameter $pname of $methodStr", tree.pos.endPos) @@ -979,7 +981,8 @@ class Typer extends Namer with Applications with Implicits { } adapt(tpd.Apply(tree, args), pt) case wtp: MethodType => - if (defn.isFunctionType(pt) && !tree.symbol.isConstructor) + if ((defn.isFunctionType(pt) || (pt eq AnyFunctionProto)) && + !tree.symbol.isConstructor) etaExpand(tree, wtp) else if (wtp.paramTypes.isEmpty) adapt(tpd.Apply(tree, Nil), pt) @@ -988,7 +991,7 @@ class Typer extends Namer with Applications with Implicits { i"""missing arguments for $methodStr |follow this method with `_' if you want to treat it as a partially applied function""".stripMargin) case _ => - if (tree.tpe <:< pt) tree + if (conforms(tree.tpe, pt)) tree else if (ctx.mode is Mode.Pattern) tree // no subtype check for patterns else if (ctx.mode is Mode.Type) err.typeMismatch(tree, pt) else adaptToSubType(wtp) diff --git a/tests/pos/functions1.scala b/tests/pos/functions1.scala new file mode 100644 index 000000000..72686ca15 --- /dev/null +++ b/tests/pos/functions1.scala @@ -0,0 +1,13 @@ +class X(val elem: Int) extends Object { + def foo(y: String): Int = y.length + elem +} + +object Functions { + + val x = new X(2) + val xe = x.elem + val xf: String => Int = x.foo(_) + val x2: String => Int = x.foo + val x3 = x.foo _ + +} diff --git a/tests/pos/implicits1.scala b/tests/pos/implicits1.scala index 4eae69325..2f0399b74 100644 --- a/tests/pos/implicits1.scala +++ b/tests/pos/implicits1.scala @@ -1,4 +1,6 @@ -class X(elem: Int) extends Object +class X(val elem: Int) { + def foo(y: String): Int = y.length + elem +} object Implicits { @@ -6,6 +8,12 @@ object Implicits { implicit def conv(x: Int): X = new X(x) + class Xdecorator(x: X) extends Object { + def foo(cond: Boolean): Int = if (cond) x.foo("abc") else 0 + } + + implicit def XDecorator(x: X) = new Xdecorator(x) + val a: Object = "abc" val b: Any = "abc" @@ -18,4 +26,10 @@ object Implicits { val z: X = 3 + val c: Int = y.elem + + val d: Int = z.foo("abc") + + // val e: Int = z.foo(true) not yet + } -- cgit v1.2.3