@@ -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
0 commit comments