From d5ef867b1f89c79f8620129693e4f1e9bc6f617c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Aug 2016 17:24:11 +0200 Subject: [PATCH 1/4] Refinements to auto tupling There's a nasty interaction with auto-tupling and trying to insert an implicit on the qualifier of a call. If the original call fails, we need to "undo" any auto-tupling decisions in calls where an implicit is inserted on the qualifier. Also: Needed to fix canAutoTuple test so that Scala2 feature is checked instead of dotty's. Also: Drop features in dotty.language that duplicate those in scala.language. --- src/dotty/language.scala | 6 +--- src/dotty/tools/dotc/core/TypeOps.scala | 4 +-- src/dotty/tools/dotc/typer/Applications.scala | 32 ++++++++++++------- src/dotty/tools/dotc/typer/Typer.scala | 19 +++++------ tests/neg/autoTuplingTest.scala | 2 -- tests/{pending => }/pos/t2913.scala | 27 ++++++++++++++-- 6 files changed, 59 insertions(+), 31 deletions(-) rename tests/{pending => }/pos/t2913.scala (61%) diff --git a/src/dotty/language.scala b/src/dotty/language.scala index 96250a9f29b5..0ed75bd266b5 100644 --- a/src/dotty/language.scala +++ b/src/dotty/language.scala @@ -4,13 +4,9 @@ object language { class Feature - /** Allow higher-kinded type syntax (not yet checked) */ - val higherKinds = new Feature - /** Keep union types */ val keepUnions = new Feature - /** No auto tupling */ - val noAutoTupling = new Feature + val f = "".contains("", (_: Int)) } diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 93b1b1f02e7a..b69ce2536f2d 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -493,7 +493,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. */ def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = { def toPrefix(sym: Symbol): String = - if (!sym.exists || (sym eq defn.LanguageModuleClass) || (sym eq defn.Scala2LanguageModuleRef)) "" + if (!sym.exists || (sym eq defn.LanguageModuleClass) || (sym eq defn.Scala2LanguageModuleClass)) "" else toPrefix(sym.owner) + sym.name + "." def featureName = toPrefix(owner) + feature def hasImport(implicit ctx: Context): Boolean = { @@ -512,7 +512,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. /** Is auto-tupling enabled? */ def canAutoTuple = - !featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling) + !featureEnabled(defn.Scala2LanguageModuleClass, nme.noAutoTupling) def scala2Mode = featureEnabled(defn.LanguageModuleClass, nme.Scala2) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index c8f41b7fab60..efd12cb5e13f 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -541,17 +541,29 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { + /** Try same application with an implicit inserted around the qualifier of the function + * part. Return an optional value to indicate success. + */ + def tryWithImplicitOnQualifier(fun1: Tree, proto: FunProto)(implicit ctx: Context): Option[Tree] = + tryInsertImplicitOnQualifier(fun1, proto) flatMap { fun2 => + tryEither { implicit ctx => + Some(typedApply( + cpy.Apply(tree)(untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), + pt)): Option[Tree] + } { (_, _) => None } + } + def realApply(implicit ctx: Context): Tree = track("realApply") { - var proto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree)) - val fun1 = typedExpr(tree.fun, proto) + val originalProto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree)) + val fun1 = typedExpr(tree.fun, originalProto) - // Warning: The following line is dirty and fragile. We record that auto-tupling was demanded as + // Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as // a side effect in adapt. If it was, we assume the tupled proto-type in the rest of the application. // This crucially relies on he fact that `proto` is used only in a single call of `adapt`, // otherwise we would get possible cross-talk between different `adapt` calls using the same // prototype. A cleaner alternative would be to return a modified prototype from `adapt` together with // a modified tree but this would be more convoluted and less efficient. - if (proto.isTupled) proto = proto.tupled + val proto = if (originalProto.isTupled) originalProto.tupled else originalProto // If some of the application's arguments are function literals without explicitly declared // parameter types, relate the normalized result type of the application with the @@ -580,13 +592,11 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val result = app.result convertNewGenericArray(ConstFold(result)) } { (failedVal, failedState) => - val fun2 = tryInsertImplicitOnQualifier(fun1, proto) - if (fun1 eq fun2) { - failedState.commit() - failedVal - } else typedApply( - cpy.Apply(tree)(untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt) - } + def fail = { failedState.commit(); failedVal } + tryWithImplicitOnQualifier(fun1, originalProto).getOrElse( + if (proto eq originalProto) fail + else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail)) + } case _ => handleUnexpectedFunType(tree, fun1) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 2b690ef51164..34cd5448b138 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1445,26 +1445,27 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) if (sel.tpe.isError) sel else adapt(sel, pt) } { (failedTree, failedState) => - val tree1 = tryInsertImplicitOnQualifier(tree, pt) - if (tree1 eq tree) fallBack(failedTree, failedState) - else adapt(tree1, pt) - } + tryInsertImplicitOnQualifier(tree, pt) match { + case Some(tree1) => adapt(tree1, pt) + case none => fallBack(failedTree, failedState) + } + } /** If this tree is a select node `qual.name`, try to insert an implicit conversion * `c` around `qual` so that `c(qual).name` conforms to `pt`. If that fails * return `tree` itself. */ - def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") { + def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Option[Tree] = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") { tree match { case Select(qual, name) => val qualProto = SelectionProto(name, pt, NoViewsAllowed) tryEither { implicit ctx => val qual1 = adaptInterpolated(qual, qualProto, EmptyTree) - if ((qual eq qual1) || ctx.reporter.hasErrors) tree - else typedSelect(cpy.Select(tree)(untpd.TypedSplice(qual1), name), pt) - } { (_, _) => tree + if ((qual eq qual1) || ctx.reporter.hasErrors) None + else Some(typedSelect(cpy.Select(tree)(untpd.TypedSplice(qual1), name), pt)) + } { (_, _) => None } - case _ => tree + case _ => None } } diff --git a/tests/neg/autoTuplingTest.scala b/tests/neg/autoTuplingTest.scala index 37136b760006..62f10b3ea90d 100644 --- a/tests/neg/autoTuplingTest.scala +++ b/tests/neg/autoTuplingTest.scala @@ -1,5 +1,3 @@ -import dotty.language.noAutoTupling - object autoTuplingNeg2 { val x = Some(1, 2) // error: too many arguments for method apply: (x: A)Some[A] diff --git a/tests/pending/pos/t2913.scala b/tests/pos/t2913.scala similarity index 61% rename from tests/pending/pos/t2913.scala rename to tests/pos/t2913.scala index 21700e71ae52..fa91e6f41e19 100644 --- a/tests/pending/pos/t2913.scala +++ b/tests/pos/t2913.scala @@ -1,4 +1,3 @@ -import language.noAutoTupling // try with on and off class A { def foo(a: Int) = 0 @@ -10,7 +9,8 @@ class RichA { def foo() = 0 } -object Test { +object TestNoAutoTupling { + import language.noAutoTupling // try with on and off implicit def AToRichA(a: A): RichA = new RichA @@ -53,3 +53,26 @@ object Main { () } } + +object TestWithAutoTuling { + + implicit def AToRichA(a: A): RichA = new RichA + + val a = new A + a.foo() + a.foo(1) + + a.foo("") // Without implicits, a type error regarding invalid argument types is generated at `""`. This is + // the same position as an argument, so the 'second try' typing with an Implicit View is tried, + // and AToRichA(a).foo("") is found. + // + // My reading of the spec "7.3 Views" is that `a.foo` denotes a member of `a`, so the view should + // not be triggered. + // + // But perhaps the implementation was changed to solve See https://lampsvn.epfl.ch/trac/scala/ticket/1756 + + a.foo("a", "b") // Without implicits, a type error regarding invalid arity is generated at `foo("", "")`. + // Typers#tryTypedApply:3274 only checks if the error is as the same position as `foo`, `"a"`, or `"b"`. + // None of these po +} + From 806a7b3b6f8c6e7df276bceaa7b0a19c580a3486 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Aug 2016 17:50:43 +0200 Subject: [PATCH 2/4] Test reshuffling - Delete redundant t2660 (exists elready in pos) - Comment t1756 - Recategorize tryexpr --- .../not-representable}/pos/tryexpr.scala | 0 tests/pending/pos/t1756.scala | 5 ++++ tests/pending/pos/t2660.scala | 25 ------------------- 3 files changed, 5 insertions(+), 25 deletions(-) rename tests/{pending => disabled/not-representable}/pos/tryexpr.scala (100%) delete mode 100644 tests/pending/pos/t2660.scala diff --git a/tests/pending/pos/tryexpr.scala b/tests/disabled/not-representable/pos/tryexpr.scala similarity index 100% rename from tests/pending/pos/tryexpr.scala rename to tests/disabled/not-representable/pos/tryexpr.scala diff --git a/tests/pending/pos/t1756.scala b/tests/pending/pos/t1756.scala index 58f56ccb9798..34bf273ab945 100644 --- a/tests/pending/pos/t1756.scala +++ b/tests/pending/pos/t1756.scala @@ -18,6 +18,11 @@ with expected type A with Poly[A]. And no solution is found. To solve this, I added a fallback scheme similar to implicit arguments: When an implicit view that adds a method matching given arguments and result type fails, try again without the result type. + +However, troubles are not yet over. We now get an oprhan poly param C when pickling +and, if typr printer and -Ylog:front is on, an infinite type of the form + + mu x. Ring[LazyRef(x) & A] */ trait Ring[T <: Ring[T]] { def +(that: T): T diff --git a/tests/pending/pos/t2660.scala b/tests/pending/pos/t2660.scala deleted file mode 100644 index d42dcc72b5d6..000000000000 --- a/tests/pending/pos/t2660.scala +++ /dev/null @@ -1,25 +0,0 @@ -package hoho - -class G - -class H extends G - -class A[T](x: T) { - - def this(y: G, z: T) = { - this(z) - print(1) - } - - def this(z: H, h: T) = { - this(h) - print(2) - } -} - -object T { - def main(args: Array[String]): Unit = { - implicit def g2h(g: G): H = new H - new A(new H, 23) - } -} From 74aef53c8e894d5ce46151e098c121a73ede9539 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Aug 2016 18:41:43 +0200 Subject: [PATCH 3/4] Add import back. Needed because the test is also run in neg wihtout command line option. --- tests/neg/autoTuplingTest.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/neg/autoTuplingTest.scala b/tests/neg/autoTuplingTest.scala index 62f10b3ea90d..92126ab5dd60 100644 --- a/tests/neg/autoTuplingTest.scala +++ b/tests/neg/autoTuplingTest.scala @@ -1,3 +1,5 @@ +import language.noAutoTupling + object autoTuplingNeg2 { val x = Some(1, 2) // error: too many arguments for method apply: (x: A)Some[A] From 3360130593e3949b455b179f244ad76d7aedfe0e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Aug 2016 10:58:51 +0200 Subject: [PATCH 4/4] Address reviewers comments. --- src/dotty/language.scala | 3 --- tests/pos/t2913.scala | 6 ++---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/dotty/language.scala b/src/dotty/language.scala index 0ed75bd266b5..416a4281ba64 100644 --- a/src/dotty/language.scala +++ b/src/dotty/language.scala @@ -6,7 +6,4 @@ object language { /** Keep union types */ val keepUnions = new Feature - - val f = "".contains("", (_: Int)) - } diff --git a/tests/pos/t2913.scala b/tests/pos/t2913.scala index fa91e6f41e19..f91ed7b51318 100644 --- a/tests/pos/t2913.scala +++ b/tests/pos/t2913.scala @@ -18,7 +18,7 @@ object TestNoAutoTupling { a.foo() a.foo(1) - a.foo("") // Without implicits, a type error regarding invalid argument types is generated at `""`. This is + a.foo("") // Without implicits, a type error regarding invalid argument types is generated at `""`. This is // the same position as an argument, so the 'second try' typing with an Implicit View is tried, // and AToRichA(a).foo("") is found. // @@ -29,7 +29,6 @@ object TestNoAutoTupling { a.foo("a", "b") // Without implicits, a type error regarding invalid arity is generated at `foo("", "")`. // Typers#tryTypedApply:3274 only checks if the error is as the same position as `foo`, `"a"`, or `"b"`. - // None of these po } // t0851 is essentially the same: @@ -54,7 +53,7 @@ object Main { } } -object TestWithAutoTuling { +object TestWithAutoTupling { implicit def AToRichA(a: A): RichA = new RichA @@ -73,6 +72,5 @@ object TestWithAutoTuling { a.foo("a", "b") // Without implicits, a type error regarding invalid arity is generated at `foo("", "")`. // Typers#tryTypedApply:3274 only checks if the error is as the same position as `foo`, `"a"`, or `"b"`. - // None of these po }