@@ -851,15 +851,12 @@ trait Applications extends Compatibility {
851
851
record(" typedApply" )
852
852
val fun1 = typedExpr(tree.fun, originalProto)
853
853
854
- // Warning: The following lines are dirty and fragile.
855
- // We record that auto-tupling or untupling was demanded as a side effect in adapt.
856
- // If it was, we assume the tupled-dual proto-type in the rest of the application,
857
- // until, possibly, we have to fall back to insert an implicit on the qualifier.
858
- // This crucially relies on he fact that `proto` is used only in a single call of `adapt`,
859
- // otherwise we would get possible cross-talk between different `adapt` calls using the same
860
- // prototype. A cleaner alternative would be to return a modified prototype from `adapt` together with
861
- // a modified tree but this would be more convoluted and less efficient.
862
- val proto = if (originalProto.hasTupledDual) originalProto.tupledDual else originalProto
854
+ // If adaptation created a tupled dual of `originalProto`, pick the right version
855
+ // (tupled or not) of originalProto to proceed.
856
+ val proto =
857
+ if originalProto.hasTupledDual && needsTupledDual(fun1.tpe, originalProto)
858
+ then originalProto.tupledDual
859
+ else originalProto
863
860
864
861
// If some of the application's arguments are function literals without explicitly declared
865
862
// parameter types, relate the normalized result type of the application with the
@@ -1097,6 +1094,40 @@ trait Applications extends Compatibility {
1097
1094
tree
1098
1095
}
1099
1096
1097
+ /** Is `tp` a unary function type or an overloaded type with with only unary function
1098
+ * types as alternatives?
1099
+ */
1100
+ def isUnary (tp : Type )(using Context ): Boolean = tp match {
1101
+ case tp : MethodicType =>
1102
+ tp.firstParamTypes match {
1103
+ case ptype :: Nil => ! ptype.isRepeatedParam
1104
+ case _ => false
1105
+ }
1106
+ case tp : TermRef =>
1107
+ tp.denot.alternatives.forall(alt => isUnary(alt.info))
1108
+ case _ =>
1109
+ false
1110
+ }
1111
+
1112
+ /** Should we tuple or untuple the argument before application?
1113
+ * If auto-tupling is enabled then
1114
+ *
1115
+ * - we tuple n-ary arguments where n > 0 if the function consists
1116
+ * only of unary alternatives
1117
+ * - we untuple tuple arguments of infix operations if the function
1118
+ * does not consist only of unary alternatives.
1119
+ */
1120
+ def needsTupledDual (funType : Type , pt : FunProto )(using Context ): Boolean =
1121
+ pt.args match
1122
+ case untpd.Tuple (elems) :: Nil =>
1123
+ elems.length > 1
1124
+ && pt.applyKind == ApplyKind .InfixTuple
1125
+ && ! isUnary(funType)
1126
+ case args =>
1127
+ args.lengthCompare(1 ) > 0
1128
+ && isUnary(funType)
1129
+ && Feature .autoTuplingEnabled
1130
+
1100
1131
/** If `tree` is a complete application of a compiler-generated `apply`
1101
1132
* or `copy` method of an enum case, widen its type to the underlying
1102
1133
* type by means of a type ascription, as long as the widened type is
0 commit comments