Skip to content

Commit ac423a3

Browse files
authored
Merge pull request #1459 from dotty-staging/tests3
Refinements to auto-tupling
2 parents f19b6dd + 3360130 commit ac423a3

File tree

9 files changed

+64
-59
lines changed

9 files changed

+64
-59
lines changed

src/dotty/language.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,6 @@ object language {
44

55
class Feature
66

7-
/** Allow higher-kinded type syntax (not yet checked) */
8-
val higherKinds = new Feature
9-
107
/** Keep union types */
118
val keepUnions = new Feature
12-
13-
/** No auto tupling */
14-
val noAutoTupling = new Feature
15-
169
}

src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
493493
*/
494494
def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = {
495495
def toPrefix(sym: Symbol): String =
496-
if (!sym.exists || (sym eq defn.LanguageModuleClass) || (sym eq defn.Scala2LanguageModuleRef)) ""
496+
if (!sym.exists || (sym eq defn.LanguageModuleClass) || (sym eq defn.Scala2LanguageModuleClass)) ""
497497
else toPrefix(sym.owner) + sym.name + "."
498498
def featureName = toPrefix(owner) + feature
499499
def hasImport(implicit ctx: Context): Boolean = {
@@ -512,7 +512,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
512512

513513
/** Is auto-tupling enabled? */
514514
def canAutoTuple =
515-
!featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling)
515+
!featureEnabled(defn.Scala2LanguageModuleClass, nme.noAutoTupling)
516516

517517
def scala2Mode =
518518
featureEnabled(defn.LanguageModuleClass, nme.Scala2)

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -541,17 +541,29 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
541541

542542
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
543543

544+
/** Try same application with an implicit inserted around the qualifier of the function
545+
* part. Return an optional value to indicate success.
546+
*/
547+
def tryWithImplicitOnQualifier(fun1: Tree, proto: FunProto)(implicit ctx: Context): Option[Tree] =
548+
tryInsertImplicitOnQualifier(fun1, proto) flatMap { fun2 =>
549+
tryEither { implicit ctx =>
550+
Some(typedApply(
551+
cpy.Apply(tree)(untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice),
552+
pt)): Option[Tree]
553+
} { (_, _) => None }
554+
}
555+
544556
def realApply(implicit ctx: Context): Tree = track("realApply") {
545-
var proto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree))
546-
val fun1 = typedExpr(tree.fun, proto)
557+
val originalProto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree))
558+
val fun1 = typedExpr(tree.fun, originalProto)
547559

548-
// Warning: The following line is dirty and fragile. We record that auto-tupling was demanded as
560+
// Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as
549561
// a side effect in adapt. If it was, we assume the tupled proto-type in the rest of the application.
550562
// This crucially relies on he fact that `proto` is used only in a single call of `adapt`,
551563
// otherwise we would get possible cross-talk between different `adapt` calls using the same
552564
// prototype. A cleaner alternative would be to return a modified prototype from `adapt` together with
553565
// a modified tree but this would be more convoluted and less efficient.
554-
if (proto.isTupled) proto = proto.tupled
566+
val proto = if (originalProto.isTupled) originalProto.tupled else originalProto
555567

556568
// If some of the application's arguments are function literals without explicitly declared
557569
// parameter types, relate the normalized result type of the application with the
@@ -580,13 +592,11 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
580592
val result = app.result
581593
convertNewGenericArray(ConstFold(result))
582594
} { (failedVal, failedState) =>
583-
val fun2 = tryInsertImplicitOnQualifier(fun1, proto)
584-
if (fun1 eq fun2) {
585-
failedState.commit()
586-
failedVal
587-
} else typedApply(
588-
cpy.Apply(tree)(untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt)
589-
}
595+
def fail = { failedState.commit(); failedVal }
596+
tryWithImplicitOnQualifier(fun1, originalProto).getOrElse(
597+
if (proto eq originalProto) fail
598+
else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
599+
}
590600
case _ =>
591601
handleUnexpectedFunType(tree, fun1)
592602
}

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,26 +1445,27 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
14451445
val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
14461446
if (sel.tpe.isError) sel else adapt(sel, pt)
14471447
} { (failedTree, failedState) =>
1448-
val tree1 = tryInsertImplicitOnQualifier(tree, pt)
1449-
if (tree1 eq tree) fallBack(failedTree, failedState)
1450-
else adapt(tree1, pt)
1451-
}
1448+
tryInsertImplicitOnQualifier(tree, pt) match {
1449+
case Some(tree1) => adapt(tree1, pt)
1450+
case none => fallBack(failedTree, failedState)
1451+
}
1452+
}
14521453

14531454
/** If this tree is a select node `qual.name`, try to insert an implicit conversion
14541455
* `c` around `qual` so that `c(qual).name` conforms to `pt`. If that fails
14551456
* return `tree` itself.
14561457
*/
1457-
def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") {
1458+
def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Option[Tree] = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") {
14581459
tree match {
14591460
case Select(qual, name) =>
14601461
val qualProto = SelectionProto(name, pt, NoViewsAllowed)
14611462
tryEither { implicit ctx =>
14621463
val qual1 = adaptInterpolated(qual, qualProto, EmptyTree)
1463-
if ((qual eq qual1) || ctx.reporter.hasErrors) tree
1464-
else typedSelect(cpy.Select(tree)(untpd.TypedSplice(qual1), name), pt)
1465-
} { (_, _) => tree
1464+
if ((qual eq qual1) || ctx.reporter.hasErrors) None
1465+
else Some(typedSelect(cpy.Select(tree)(untpd.TypedSplice(qual1), name), pt))
1466+
} { (_, _) => None
14661467
}
1467-
case _ => tree
1468+
case _ => None
14681469
}
14691470
}
14701471

tests/neg/autoTuplingTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import dotty.language.noAutoTupling
1+
import language.noAutoTupling
22

33
object autoTuplingNeg2 {
44

tests/pending/pos/t1756.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ with expected type A with Poly[A]. And no solution is found.
1818
To solve this, I added a fallback scheme similar to implicit arguments:
1919
When an implicit view that adds a method matching given arguments and result
2020
type fails, try again without the result type.
21+
22+
However, troubles are not yet over. We now get an oprhan poly param C when pickling
23+
and, if typr printer and -Ylog:front is on, an infinite type of the form
24+
25+
mu x. Ring[LazyRef(x) & A]
2126
*/
2227
trait Ring[T <: Ring[T]] {
2328
def +(that: T): T

tests/pending/pos/t2660.scala

Lines changed: 0 additions & 25 deletions
This file was deleted.

tests/pending/pos/t2913.scala renamed to tests/pos/t2913.scala

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import language.noAutoTupling // try with on and off
21

32
class A {
43
def foo(a: Int) = 0
@@ -10,15 +9,16 @@ class RichA {
109
def foo() = 0
1110
}
1211

13-
object Test {
12+
object TestNoAutoTupling {
13+
import language.noAutoTupling // try with on and off
1414

1515
implicit def AToRichA(a: A): RichA = new RichA
1616

1717
val a = new A
1818
a.foo()
1919
a.foo(1)
2020

21-
a.foo("") // Without implicits, a type error regarding invalid argument types is generated at `""`. This is
21+
a.foo("") // Without implicits, a type error regarding invalid argument types is generated at `""`. This is
2222
// the same position as an argument, so the 'second try' typing with an Implicit View is tried,
2323
// and AToRichA(a).foo("") is found.
2424
//
@@ -29,7 +29,6 @@ object Test {
2929

3030
a.foo("a", "b") // Without implicits, a type error regarding invalid arity is generated at `foo(<error>"", "")`.
3131
// Typers#tryTypedApply:3274 only checks if the error is as the same position as `foo`, `"a"`, or `"b"`.
32-
// None of these po
3332
}
3433

3534
// t0851 is essentially the same:
@@ -53,3 +52,25 @@ object Main {
5352
()
5453
}
5554
}
55+
56+
object TestWithAutoTupling {
57+
58+
implicit def AToRichA(a: A): RichA = new RichA
59+
60+
val a = new A
61+
a.foo()
62+
a.foo(1)
63+
64+
a.foo("") // Without implicits, a type error regarding invalid argument types is generated at `""`. This is
65+
// the same position as an argument, so the 'second try' typing with an Implicit View is tried,
66+
// and AToRichA(a).foo("") is found.
67+
//
68+
// My reading of the spec "7.3 Views" is that `a.foo` denotes a member of `a`, so the view should
69+
// not be triggered.
70+
//
71+
// But perhaps the implementation was changed to solve See https://lampsvn.epfl.ch/trac/scala/ticket/1756
72+
73+
a.foo("a", "b") // Without implicits, a type error regarding invalid arity is generated at `foo(<error>"", "")`.
74+
// Typers#tryTypedApply:3274 only checks if the error is as the same position as `foo`, `"a"`, or `"b"`.
75+
}
76+

0 commit comments

Comments
 (0)