Skip to content

Commit 7366891

Browse files
oderskymichelou
authored andcommitted
More robust handling of auto-tupling (scala#12138)
Instead of a state based hidden side channel between adapt and realApply, use only a hint, and check again. Fies scala#12133
1 parent 876ea41 commit 7366891

File tree

3 files changed

+55
-40
lines changed

3 files changed

+55
-40
lines changed

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

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -867,15 +867,12 @@ trait Applications extends Compatibility {
867867
record("typedApply")
868868
val fun1 = typedExpr(tree.fun, originalProto)
869869

870-
// Warning: The following lines are dirty and fragile.
871-
// We record that auto-tupling or untupling was demanded as a side effect in adapt.
872-
// If it was, we assume the tupled-dual proto-type in the rest of the application,
873-
// until, possibly, we have to fall back to insert an implicit on the qualifier.
874-
// This crucially relies on he fact that `proto` is used only in a single call of `adapt`,
875-
// otherwise we would get possible cross-talk between different `adapt` calls using the same
876-
// prototype. A cleaner alternative would be to return a modified prototype from `adapt` together with
877-
// a modified tree but this would be more convoluted and less efficient.
878-
val proto = if (originalProto.hasTupledDual) originalProto.tupledDual else originalProto
870+
// If adaptation created a tupled dual of `originalProto`, pick the right version
871+
// (tupled or not) of originalProto to proceed.
872+
val proto =
873+
if originalProto.hasTupledDual && needsTupledDual(fun1.tpe, originalProto)
874+
then originalProto.tupledDual
875+
else originalProto
879876

880877
// If some of the application's arguments are function literals without explicitly declared
881878
// parameter types, relate the normalized result type of the application with the
@@ -1113,6 +1110,40 @@ trait Applications extends Compatibility {
11131110
tree
11141111
}
11151112

1113+
/** Is `tp` a unary function type or an overloaded type with with only unary function
1114+
* types as alternatives?
1115+
*/
1116+
def isUnary(tp: Type)(using Context): Boolean = tp match {
1117+
case tp: MethodicType =>
1118+
tp.firstParamTypes match {
1119+
case ptype :: Nil => !ptype.isRepeatedParam
1120+
case _ => false
1121+
}
1122+
case tp: TermRef =>
1123+
tp.denot.alternatives.forall(alt => isUnary(alt.info))
1124+
case _ =>
1125+
false
1126+
}
1127+
1128+
/** Should we tuple or untuple the argument before application?
1129+
* If auto-tupling is enabled then
1130+
*
1131+
* - we tuple n-ary arguments where n > 0 if the function consists
1132+
* only of unary alternatives
1133+
* - we untuple tuple arguments of infix operations if the function
1134+
* does not consist only of unary alternatives.
1135+
*/
1136+
def needsTupledDual(funType: Type, pt: FunProto)(using Context): Boolean =
1137+
pt.args match
1138+
case untpd.Tuple(elems) :: Nil =>
1139+
elems.length > 1
1140+
&& pt.applyKind == ApplyKind.InfixTuple
1141+
&& !isUnary(funType)
1142+
case args =>
1143+
args.lengthCompare(1) > 0
1144+
&& isUnary(funType)
1145+
&& Feature.autoTuplingEnabled
1146+
11161147
/** If `tree` is a complete application of a compiler-generated `apply`
11171148
* or `copy` method of an enum case, widen its type to the underlying
11181149
* type by means of a type ascription, as long as the widened type is

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

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3198,37 +3198,6 @@ class Typer extends Namer
31983198
}
31993199
}
32003200

3201-
def isUnary(tp: Type): Boolean = tp match {
3202-
case tp: MethodicType =>
3203-
tp.firstParamTypes match {
3204-
case ptype :: Nil => !ptype.isRepeatedParam
3205-
case _ => false
3206-
}
3207-
case tp: TermRef =>
3208-
tp.denot.alternatives.forall(alt => isUnary(alt.info))
3209-
case _ =>
3210-
false
3211-
}
3212-
3213-
/** Should we tuple or untuple the argument before application?
3214-
* If auto-tupling is enabled then
3215-
*
3216-
* - we tuple n-ary arguments where n > 0 if the function consists
3217-
* only of unary alternatives
3218-
* - we untuple tuple arguments of infix operations if the function
3219-
* does not consist only of unary alternatives.
3220-
*/
3221-
def needsTupledDual(funType: Type, pt: FunProto): Boolean =
3222-
pt.args match
3223-
case untpd.Tuple(elems) :: Nil =>
3224-
elems.length > 1
3225-
&& pt.applyKind == ApplyKind.InfixTuple
3226-
&& !isUnary(funType)
3227-
case args =>
3228-
args.lengthCompare(1) > 0
3229-
&& isUnary(funType)
3230-
&& autoTuplingEnabled
3231-
32323201
def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match {
32333202
case wtp: MethodOrPoly =>
32343203
def methodStr = methPart(tree).symbol.showLocated

tests/pos/i12133.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class B
2+
3+
class A {
4+
def foo(x: B) = ???
5+
def foo(str: String) = ???
6+
}
7+
8+
//implicit class C(x: A) {
9+
// def foo(s: Int*) = s.size
10+
//}
11+
extension (x: A) def foo(s: Int*) = s.size
12+
13+
val a = new A
14+
15+
def test: Unit = a.foo(1, 2)

0 commit comments

Comments
 (0)