Skip to content

Commit 8fa92ac

Browse files
committed
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.
1 parent f19b6dd commit 8fa92ac

File tree

6 files changed

+59
-31
lines changed

6 files changed

+59
-31
lines changed

src/dotty/language.scala

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ 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
129

13-
/** No auto tupling */
14-
val noAutoTupling = new Feature
10+
val f = "".contains("", (_: Int))
1511

1612
}

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: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import dotty.language.noAutoTupling
2-
31
object autoTuplingNeg2 {
42

53
val x = Some(1, 2) // error: too many arguments for method apply: (x: A)Some[A]

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

Lines changed: 25 additions & 2 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,7 +9,8 @@ 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

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

0 commit comments

Comments
 (0)