@@ -867,15 +867,12 @@ trait Applications extends Compatibility {
867
867
record(" typedApply" )
868
868
val fun1 = typedExpr(tree.fun, originalProto)
869
869
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
879
876
880
877
// If some of the application's arguments are function literals without explicitly declared
881
878
// parameter types, relate the normalized result type of the application with the
@@ -1113,6 +1110,40 @@ trait Applications extends Compatibility {
1113
1110
tree
1114
1111
}
1115
1112
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
+
1116
1147
/** If `tree` is a complete application of a compiler-generated `apply`
1117
1148
* or `copy` method of an enum case, widen its type to the underlying
1118
1149
* type by means of a type ascription, as long as the widened type is
0 commit comments