From e60768b62151a160026985269a87fd5b63ee0ae8 Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Wed, 14 Sep 2016 14:06:01 +0100 Subject: SI-4700 Add `@infix` annotation for type printing ``` scala> import scala.annotation.infix import scala.annotation.infix scala> @infix class &&[T, U] defined class $amp$amp scala> def foo: Int && Boolean = ??? foo: Int && Boolean ``` --- src/reflect/scala/reflect/internal/Definitions.scala | 2 ++ src/reflect/scala/reflect/internal/Types.scala | 14 ++++++++++++++ src/reflect/scala/reflect/runtime/JavaUniverseForce.scala | 1 + 3 files changed, 17 insertions(+) (limited to 'src/reflect/scala') diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index eca1bbea5a..8dda5737d4 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -1405,6 +1405,8 @@ trait Definitions extends api.StandardDefinitions { case _ => false } + lazy val ShowAsInfixAnnotationClass = rootMirror.getClassIfDefined("scala.annotation.showAsInfix") + // todo: reconcile with javaSignature!!! def signature(tp: Type): String = { def erasure(tp: Type): Type = tp match { diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 7dda805378..54200dea8e 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -307,6 +307,9 @@ trait Types /** Is this type completed (i.e. not a lazy type)? */ def isComplete: Boolean = true + /** Should this be printed as an infix type (@showAsInfix class &&[T, U])? */ + def isShowAsInfixType: Boolean = false + /** If this is a lazy type, assign a new type to `sym`. */ def complete(sym: Symbol) {} @@ -2097,6 +2100,9 @@ trait Types trivial = fromBoolean(!sym.isTypeParameter && pre.isTrivial && areTrivialTypes(args)) toBoolean(trivial) } + + override def isShowAsInfixType: Boolean = sym.hasAnnotation(ShowAsInfixAnnotationClass) + private[Types] def invalidateTypeRefCaches(): Unit = { parentsCache = null parentsPeriod = NoPeriod @@ -2345,6 +2351,14 @@ trait Types xs.init.mkString("(", ", ", ")") + " => " + xs.last } } + else if (isShowAsInfixType && args.length == 2) + args(0) + " " + sym.decodedName + " " + + ( + if (args(1).isShowAsInfixType) + "(" + args(1) + ")" + else + args(1) + ) else if (isTupleTypeDirect(this)) tupleTypeString else if (sym.isAliasType && prefixChain.exists(_.termSymbol.isSynthetic) && (this ne dealias)) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index caef5535b4..53ac439daa 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -425,6 +425,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.hijackedCoreClasses definitions.symbolsNotPresentInBytecode definitions.isPossibleSyntheticParent + definitions.ShowAsInfixAnnotationClass definitions.abbrvTag definitions.numericWeight definitions.boxedModule -- cgit v1.2.3 From 8badcadbe51f4a02e495f462f5f2666a24d79cb0 Mon Sep 17 00:00:00 2001 From: allisonhb Date: Wed, 7 Dec 2016 20:44:54 -0500 Subject: SI-4700 Show infix types with as few parentheses as needed. --- src/reflect/scala/reflect/internal/Types.scala | 30 ++++++++++++++++++-------- test/files/run/t4700.check | 15 +++++++++++++ test/files/run/t4700.scala | 5 +++++ 3 files changed, 41 insertions(+), 9 deletions(-) (limited to 'src/reflect/scala') diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 54200dea8e..44e96163ea 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2101,7 +2101,9 @@ trait Types toBoolean(trivial) } - override def isShowAsInfixType: Boolean = sym.hasAnnotation(ShowAsInfixAnnotationClass) + /* It only makes sense to show 2-ary type constructors infix. */ + override def isShowAsInfixType: Boolean = + sym.hasAnnotation(ShowAsInfixAnnotationClass) && hasLength(args, 2) private[Types] def invalidateTypeRefCaches(): Unit = { parentsCache = null @@ -2328,6 +2330,22 @@ trait Types case arg :: Nil => s"($arg,)" case _ => args.mkString("(", ", ", ")") } + private def infixTypeString: String = { + /* SLS 3.2.8: all infix types have the same precedence. + * In A op B op' C, op and op' need the same associativity. + * Therefore, if op is left associative, anything on its right + * needs to be parenthesized if it's an infix type, and vice versa. */ + // we should only get here after `isShowInfixType` says we have 2 args + val l :: r :: Nil = args + + val isRightAssoc = typeSymbol.decodedName endsWith ":" + + val lstr = if (isRightAssoc && l.isShowAsInfixType) s"($l)" else l.toString + + val rstr = if (!isRightAssoc && r.isShowAsInfixType) s"($r)" else r.toString + + s"$lstr ${sym.decodedName} $rstr" + } private def customToString = sym match { case RepeatedParamClass | JavaRepeatedParamClass => args.head + "*" case ByNameParamClass => "=> " + args.head @@ -2351,14 +2369,8 @@ trait Types xs.init.mkString("(", ", ", ")") + " => " + xs.last } } - else if (isShowAsInfixType && args.length == 2) - args(0) + " " + sym.decodedName + " " + - ( - if (args(1).isShowAsInfixType) - "(" + args(1) + ")" - else - args(1) - ) + else if (isShowAsInfixType) + infixTypeString else if (isTupleTypeDirect(this)) tupleTypeString else if (sym.isAliasType && prefixChain.exists(_.termSymbol.isSynthetic) && (this ne dealias)) diff --git a/test/files/run/t4700.check b/test/files/run/t4700.check index 30f8124b85..40caf0fd36 100644 --- a/test/files/run/t4700.check +++ b/test/files/run/t4700.check @@ -29,4 +29,19 @@ defined type alias Mappy scala> def foo: Int Mappy (Boolean && String) = ??? foo: Int Mappy (Boolean && String) +scala> @showAsInfix class &:[L, R] +defined class $amp$colon + +scala> def foo: Int &: String = ??? +foo: Int &: String + +scala> def foo: Int &: Boolean &: String = ??? +foo: Int &: Boolean &: String + +scala> def foo: (Int || String) &: Boolean = ??? +foo: (Int || String) &: Boolean + +scala> def foo: Int || (Boolean &: String) = ??? +foo: Int || (Boolean &: String) + scala> :quit diff --git a/test/files/run/t4700.scala b/test/files/run/t4700.scala index 6182656b18..77f0de3d38 100644 --- a/test/files/run/t4700.scala +++ b/test/files/run/t4700.scala @@ -13,6 +13,11 @@ object Test extends ReplTest { |def foo: Int && (Boolean && String) = ??? |@showAsInfix type Mappy[T, U] = Map[T, U] |def foo: Int Mappy (Boolean && String) = ??? + |@showAsInfix class &:[L, R] + |def foo: Int &: String = ??? + |def foo: Int &: Boolean &: String = ??? + |def foo: (Int || String) &: Boolean = ??? + |def foo: Int || (Boolean &: String) = ??? |""".stripMargin } -- cgit v1.2.3 From fab1db5a3854ae737e1d749eb08be9baf41199f5 Mon Sep 17 00:00:00 2001 From: allisonhb Date: Wed, 7 Dec 2016 21:45:30 -0500 Subject: SI-4700 Make infix notation default for symbolic types. Add ability to disable this via the @showAsInfix annotation. --- src/library/scala/annotation/showAsInfix.scala | 42 ++++++++++++---------- .../scala/reflect/internal/AnnotationInfos.scala | 5 +-- src/reflect/scala/reflect/internal/Types.scala | 8 +++-- test/files/run/t4700.check | 27 +++++++------- test/files/run/t4700.scala | 11 +++--- 5 files changed, 50 insertions(+), 43 deletions(-) (limited to 'src/reflect/scala') diff --git a/src/library/scala/annotation/showAsInfix.scala b/src/library/scala/annotation/showAsInfix.scala index 41c93b697f..6c25e08efa 100644 --- a/src/library/scala/annotation/showAsInfix.scala +++ b/src/library/scala/annotation/showAsInfix.scala @@ -1,21 +1,27 @@ package scala.annotation /** - * This annotation, used for two-parameter generic types makes Scala print - * the type using infix notation: - * - * ``` - * scala> class &&[T, U] - * defined class $amp$amp - * - * scala> def foo: Int && Int = ??? - * foo: &&[Int,Int] - * - * scala> @showAsInfix class &&[T, U] - * defined class $amp$amp - * - * scala> def foo: Int && Int = ??? - * foo: Int && Int - * ``` - */ -class showAsInfix extends annotation.StaticAnnotation \ No newline at end of file + * This annotation configures how Scala prints two-parameter generic types. + * + * By default, types with symbolic names are printed infix; while types without + * them are printed using the regular generic type syntax. + * + * Example of usage: + {{{ + scala> class Map[T, U] + defined class Map + + scala> def foo: Int Map Int = ??? + foo: Map[Int,Int] + + scala> @showAsInfix class Map[T, U] + defined class Map + + scala> def foo: Int Map Int = ??? + foo: Int Map Int + }}} + * + * @param enabled whether to show this type as an infix type operator. + * @since 2.12.2 + */ +class showAsInfix(enabled: Boolean = true) extends annotation.StaticAnnotation \ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index cfde164754..14a8e053e9 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -316,8 +316,9 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => /** Check whether any of the arguments mention a symbol */ def refsSymbol(sym: Symbol) = hasArgWhich(_.symbol == sym) - def stringArg(index: Int) = constantAtIndex(index) map (_.stringValue) - def intArg(index: Int) = constantAtIndex(index) map (_.intValue) + def stringArg(index: Int) = constantAtIndex(index) map (_.stringValue) + def intArg(index: Int) = constantAtIndex(index) map (_.intValue) + def booleanArg(index: Int) = constantAtIndex(index) map (_.booleanValue) def symbolArg(index: Int) = argAtIndex(index) collect { case Apply(fun, Literal(str) :: Nil) if fun.symbol == definitions.Symbol_apply => newTermName(str.stringValue) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 44e96163ea..73103668e3 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2101,9 +2101,13 @@ trait Types toBoolean(trivial) } - /* It only makes sense to show 2-ary type constructors infix. */ + /* It only makes sense to show 2-ary type constructors infix. + * By default we do only if it's a symbolic name. */ override def isShowAsInfixType: Boolean = - sym.hasAnnotation(ShowAsInfixAnnotationClass) && hasLength(args, 2) + hasLength(args, 2) && + sym.getAnnotation(ShowAsInfixAnnotationClass) + .map(_ booleanArg 0 getOrElse true) + .getOrElse(!Character.isUnicodeIdentifierStart(sym.decodedName.head)) private[Types] def invalidateTypeRefCaches(): Unit = { parentsCache = null diff --git a/test/files/run/t4700.check b/test/files/run/t4700.check index 40caf0fd36..ae854b959d 100644 --- a/test/files/run/t4700.check +++ b/test/files/run/t4700.check @@ -6,16 +6,7 @@ scala> class &&[T,U] defined class $amp$amp scala> def foo: Int && Boolean = ??? -foo: &&[Int,Boolean] - -scala> @showAsInfix class ||[T,U] -defined class $bar$bar - -scala> def foo: Int || Boolean = ??? -foo: Int || Boolean - -scala> @showAsInfix class &&[T, U] -defined class $amp$amp +foo: Int && Boolean scala> def foo: Int && Boolean && String = ??? foo: Int && Boolean && String @@ -29,7 +20,13 @@ defined type alias Mappy scala> def foo: Int Mappy (Boolean && String) = ??? foo: Int Mappy (Boolean && String) -scala> @showAsInfix class &:[L, R] +scala> @showAsInfix(false) class ||[T,U] +defined class $bar$bar + +scala> def foo: Int || Boolean = ??? +foo: ||[Int,Boolean] + +scala> class &:[L, R] defined class $amp$colon scala> def foo: Int &: String = ??? @@ -38,10 +35,10 @@ foo: Int &: String scala> def foo: Int &: Boolean &: String = ??? foo: Int &: Boolean &: String -scala> def foo: (Int || String) &: Boolean = ??? -foo: (Int || String) &: Boolean +scala> def foo: (Int && String) &: Boolean = ??? +foo: (Int && String) &: Boolean -scala> def foo: Int || (Boolean &: String) = ??? -foo: Int || (Boolean &: String) +scala> def foo: Int && (Boolean &: String) = ??? +foo: Int && (Boolean &: String) scala> :quit diff --git a/test/files/run/t4700.scala b/test/files/run/t4700.scala index 77f0de3d38..7c02676e89 100644 --- a/test/files/run/t4700.scala +++ b/test/files/run/t4700.scala @@ -6,18 +6,17 @@ object Test extends ReplTest { |import scala.annotation.showAsInfix |class &&[T,U] |def foo: Int && Boolean = ??? - |@showAsInfix class ||[T,U] - |def foo: Int || Boolean = ??? - |@showAsInfix class &&[T, U] |def foo: Int && Boolean && String = ??? |def foo: Int && (Boolean && String) = ??? |@showAsInfix type Mappy[T, U] = Map[T, U] |def foo: Int Mappy (Boolean && String) = ??? - |@showAsInfix class &:[L, R] + |@showAsInfix(false) class ||[T,U] + |def foo: Int || Boolean = ??? + |class &:[L, R] |def foo: Int &: String = ??? |def foo: Int &: Boolean &: String = ??? - |def foo: (Int || String) &: Boolean = ??? - |def foo: Int || (Boolean &: String) = ??? + |def foo: (Int && String) &: Boolean = ??? + |def foo: Int && (Boolean &: String) = ??? |""".stripMargin } -- cgit v1.2.3