@@ -155,17 +155,24 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
155155 case _ => false
156156 }
157157
158- def toTextInfixType (op : Type , args : List [Type ]): Text = changePrec(InfixPrec ) {
159- /* SLS 3.2.8: all infix types have the same precedence.
160- * In A op B op' C, op and op' need the same associativity.
161- * Therefore, if op is left associative, anything on its right
162- * needs to be parenthesized if it's an infix type, and vice versa. */
163- val l :: r :: Nil = args
164- val isRightAssoc = op.typeSymbol.name.endsWith(" :" )
165- val leftArg = if (isRightAssoc) atPrec(InfixPrec + 1 ) { argText(l) } else argText(l)
166- val rightArg = if (! isRightAssoc) atPrec(InfixPrec + 1 ) { argText(r) } else argText(r)
167-
168- leftArg ~ " " ~ simpleNameString(op.classSymbol) ~ " " ~ rightArg
158+ def tyconName (tp : Type ): Name = tp.typeSymbol.name
159+ def checkAssocMismatch (tp : Type , isRightAssoc : Boolean ) = tp match {
160+ case AppliedType (tycon, _) => isInfixType(tp) && tyconName(tycon).endsWith(" :" ) != isRightAssoc
161+ case AndType (_, _) => isRightAssoc
162+ case OrType (_, _) => isRightAssoc
163+ case _ => false
164+ }
165+
166+ def toTextInfixType (opName : Name , l : Type , r : Type )(op : => Text ): Text = {
167+ val isRightAssoc = opName.endsWith(" :" )
168+ val opPrec = parsing.precedence(opName, true )
169+
170+ changePrec(opPrec) {
171+ val leftPrec = if (isRightAssoc || checkAssocMismatch(l, isRightAssoc)) opPrec + 1 else opPrec
172+ val rightPrec = if (! isRightAssoc || checkAssocMismatch(r, isRightAssoc)) opPrec + 1 else opPrec
173+
174+ atPrec(leftPrec) { argText(l) } ~ " " ~ op ~ " " ~ atPrec(rightPrec) { argText(r) }
175+ }
169176 }
170177
171178 homogenize(tp) match {
@@ -174,7 +181,20 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
174181 if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ " *"
175182 if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction, cls.name.isErasedFunction)
176183 if (defn.isTupleClass(cls)) return toTextTuple(args)
177- if (isInfixType(tp)) return toTextInfixType(tycon, args)
184+ if (isInfixType(tp)) {
185+ val l :: r :: Nil = args
186+ val opName = tyconName(tycon)
187+
188+ return toTextInfixType(tyconName(tycon), l, r) { simpleNameString(tycon.classSymbol) }
189+ }
190+
191+ // Since RefinedPrinter, unlike PlainPrinter, can output right-associative type-operators, we must override handling
192+ // of AndType and OrType to account for associativity
193+ case AndType (tp1, tp2) =>
194+ return toTextInfixType(tpnme.raw.AMP , tp1, tp2) { toText(tpnme.raw.AMP ) }
195+ case OrType (tp1, tp2) =>
196+ return toTextInfixType(tpnme.raw.BAR , tp1, tp2) { toText(tpnme.raw.BAR ) }
197+
178198 case EtaExpansion (tycon) =>
179199 return toText(tycon)
180200 case tp : RefinedType if defn.isFunctionType(tp) =>
0 commit comments