Skip to content

Commit a9032b0

Browse files
committed
SI-4700 Types with symbolic names print in infix by default
This a partial port of scala/scala#5589 to dotty: when pretty-printing an applied type, if its type constructor has a symbolic name, then always print it in infix form. The PR in scalac also adds an `@showAsInfix` annotation to control this behavior, but we cannot do the same in dotty since we still rely on the standard library from Scala 2.11 and the annotation only exists in 2.12 and up.
1 parent e6be996 commit a9032b0

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,28 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
124124
toTextTuple(args.init)
125125
("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last)
126126
}
127+
128+
def isInfixType(tp: Type): Boolean = tp match {
129+
case AppliedType(tycon, args) =>
130+
args.length == 2 &&
131+
!Character.isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head)
132+
// TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation
133+
case _ =>
134+
false
135+
}
136+
def toTextInfixType(op: Type, args: List[Type]): Text = {
137+
/* SLS 3.2.8: all infix types have the same precedence.
138+
* In A op B op' C, op and op' need the same associativity.
139+
* Therefore, if op is left associative, anything on its right
140+
* needs to be parenthesized if it's an infix type, and vice versa. */
141+
val l :: r :: Nil = args
142+
val isRightAssoc = op.typeSymbol.name.endsWith(":")
143+
val leftArg = if (isRightAssoc && isInfixType(l)) "(" ~ toText(l) ~ ")" else toText(l)
144+
val rightArg = if (!isRightAssoc && isInfixType(r)) "(" ~ toText(r) ~ ")" else toText(r)
145+
146+
leftArg ~ " " ~ toTextLocal(op) ~ " " ~ rightArg
147+
}
148+
127149
homogenize(tp) match {
128150
case x: ConstantType if homogenizedView =>
129151
return toText(x.widen)
@@ -132,6 +154,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
132154
if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*"
133155
if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction)
134156
if (defn.isTupleClass(cls)) return toTextTuple(args)
157+
if (isInfixType(tp)) return toTextInfixType(tycon, args)
135158
return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close
136159
case tp: TypeRef =>
137160
val hideType = !ctx.settings.debugAlias.value && (tp.symbol.isAliasPreferred)

tests/repl/infix-types.check

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
scala> class &&[T,U]
2+
defined class &&
3+
scala> def foo: Int && Boolean = ???
4+
def foo: Int && Boolean
5+
scala> def foo: Int && Boolean && String = ???
6+
def foo: Int && Boolean && String
7+
scala> def foo: Int && (Boolean && String) = ???
8+
def foo: Int && (Boolean && String)
9+
scala> class &:[L, R]
10+
defined class &:
11+
scala> def foo: Int &: String = ???
12+
def foo: Int &: String
13+
scala> def foo: Int &: Boolean &: String = ???
14+
def foo: Int &: Boolean &: String
15+
scala> def foo: (Int && String) &: Boolean = ???
16+
def foo: (Int && String) &: Boolean
17+
scala> def foo: Int && (Boolean &: String) = ???
18+
def foo: Int && (Boolean &: String)
19+
scala> :quit

0 commit comments

Comments
 (0)