@@ -851,15 +851,12 @@ trait Applications extends Compatibility {
851851 record(" typedApply" )
852852 val fun1 = typedExpr(tree.fun, originalProto)
853853
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
863860
864861 // If some of the application's arguments are function literals without explicitly declared
865862 // parameter types, relate the normalized result type of the application with the
@@ -1097,6 +1094,40 @@ trait Applications extends Compatibility {
10971094 tree
10981095 }
10991096
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+
11001131 /** If `tree` is a complete application of a compiler-generated `apply`
11011132 * or `copy` method of an enum case, widen its type to the underlying
11021133 * type by means of a type ascription, as long as the widened type is
0 commit comments