Skip to content

Commit

Permalink
[backport] Make the CharSequence wrappers in Predef non-implicit, for…
Browse files Browse the repository at this point in the history
… JDK 15

In JDK 15 CharSequence has an isEmpty method with a default
implementation, which clashes with our Array[Char]#isEmpty,
IndexedSeq[Char]#isEmpty, as well as our StringBuilder and reflect's
Name.

Backport of scala#9292 from 2.13.x to 2.12.x
  • Loading branch information
dwijnand authored and SethTisue committed Nov 18, 2020
1 parent 30c5b82 commit 6d609f1
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 11 deletions.
20 changes: 13 additions & 7 deletions src/library/scala/Predef.scala
Expand Up @@ -97,9 +97,9 @@ import scala.io.StdIn
* @groupprio implicit-classes-any 70
* @groupdesc implicit-classes-any These implicit classes add useful extension methods to every type.
*
* @groupname implicit-classes-char CharSequence Conversions
* @groupprio implicit-classes-char 80
* @groupdesc implicit-classes-char These implicit classes add CharSequence methods to Array[Char] and IndexedSeq[Char] instances.
* @groupname char-sequence-wrappers CharSequence Wrappers
* @groupprio char-sequence-wrappers 80
* @groupdesc char-sequence-wrappers Wrappers that implements CharSequence and were implicit classes.
*
* @groupname conversions-java-to-anyval Java to Scala
* @groupprio conversions-java-to-anyval 90
Expand Down Expand Up @@ -349,22 +349,28 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef {
// and `@deprecatedName(Symbol("<none>"), "2.12.0")` crashes scalac with
// scala.reflect.internal.Symbols$CyclicReference: illegal cyclic reference involving object Symbol
// in run/repl-no-imports-no-predef-power.scala.
/** @group implicit-classes-char */
implicit final class SeqCharSequence(@deprecated("will be made private", "2.12.0") @deprecatedName(null, "2.12.0") val __sequenceOfChars: scala.collection.IndexedSeq[Char]) extends CharSequence {
/** @group char-sequence-wrappers */
final class SeqCharSequence(@deprecated("will be made private", "2.12.0") @deprecatedName(null, "2.12.0") val __sequenceOfChars: scala.collection.IndexedSeq[Char]) extends CharSequence {
def length: Int = __sequenceOfChars.length
def charAt(index: Int): Char = __sequenceOfChars(index)
def subSequence(start: Int, end: Int): CharSequence = new SeqCharSequence(__sequenceOfChars.slice(start, end))
override def toString = __sequenceOfChars mkString ""
}

/** @group implicit-classes-char */
implicit final class ArrayCharSequence(@deprecated("will be made private", "2.12.0") @deprecatedName(null, "2.12.0") val __arrayOfChars: Array[Char]) extends CharSequence {
/** @group char-sequence-wrappers */
def SeqCharSequence(sequenceOfChars: scala.collection.IndexedSeq[Char]): SeqCharSequence = new SeqCharSequence(sequenceOfChars)

/** @group char-sequence-wrappers */
final class ArrayCharSequence(@deprecated("will be made private", "2.12.0") @deprecatedName(null, "2.12.0") val __arrayOfChars: Array[Char]) extends CharSequence {
def length: Int = __arrayOfChars.length
def charAt(index: Int): Char = __arrayOfChars(index)
def subSequence(start: Int, end: Int): CharSequence = new runtime.ArrayCharSequence(__arrayOfChars, start, end)
override def toString = __arrayOfChars mkString ""
}

/** @group char-sequence-wrappers */
def ArrayCharSequence(arrayOfChars: Array[Char]): ArrayCharSequence = new ArrayCharSequence(arrayOfChars)

implicit val StringCanBuildFrom: CanBuildFrom[String, Char, String] = new CanBuildFrom[String, Char, String] {
def apply(from: String) = apply()
def apply() = mutable.StringBuilder.newBuilder
Expand Down
9 changes: 9 additions & 0 deletions src/library/scala/collection/mutable/StringBuilder.scala
Expand Up @@ -447,6 +447,15 @@ final class StringBuilder(private val underlying: JavaStringBuilder)
* @return the string assembled by this StringBuilder
*/
def result(): String = toString


/** Tests whether this builder is empty.
*
* This method is required for JDK15+ compatibility
*
* @return `true` if this builder contains nothing, `false` otherwise.
*/
override def isEmpty: Boolean = underlying.length() == 0
}

object StringBuilder {
Expand Down
10 changes: 8 additions & 2 deletions src/reflect/scala/reflect/internal/Names.scala
Expand Up @@ -188,12 +188,17 @@ trait Names extends api.Names {

// Classes ----------------------------------------------------------------------

// Dummy trait to make Name#isEmpty with override keyword at JDK before 15
sealed trait NameHasIsEmpty {
def isEmpty: Boolean
}

/** The name class.
* TODO - resolve schizophrenia regarding whether to treat Names as Strings
* or Strings as Names. Give names the key functions the absence of which
* make people want Strings all the time.
*/
sealed abstract class Name(protected val index: Int, protected val len: Int, protected val cachedString: String) extends NameApi with CharSequence {
sealed abstract class Name(protected val index: Int, protected val len: Int, protected val cachedString: String) extends NameApi with NameHasIsEmpty with CharSequence {
type ThisNameType >: Null <: Name
protected[this] def thisName: ThisNameType

Expand All @@ -209,8 +214,9 @@ trait Names extends api.Names {

/** The length of this name. */
final def length: Int = len
final def isEmpty = length == 0
final def nonEmpty = !isEmpty
// This method is implements NameHasIsEmpty, and overrides CharSequence's isEmpty on JDK 15+
override final def isEmpty = length == 0

def nameKind: String
def isTermName: Boolean
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/StdNames.scala
Expand Up @@ -77,7 +77,7 @@ trait StdNames {
val suffix = s takeRight edge

val cs = s.toArray
val bytes = Codec toUTF8 cs
val bytes = Codec.toUTF8(new scala.runtime.ArrayCharSequence(cs, 0, cs.length))
md5 update bytes
val md5chars = (md5.digest() map (b => (b & 0xFF).toHexString)).mkString

Expand Down
2 changes: 1 addition & 1 deletion test/files/run/array-charSeq.scala
@@ -1,6 +1,6 @@
object Test {
val arr = Array[Char]('a' to 'i': _*)
var xs: CharSequence = arr
var xs: CharSequence = ArrayCharSequence(arr)
val hash = xs.hashCode

def check(chars: CharSequence) {
Expand Down
30 changes: 30 additions & 0 deletions test/junit/scala/ArrayTest.scala
@@ -0,0 +1,30 @@
package scala

import org.junit.Assert.{ assertArrayEquals, assertFalse, assertTrue }
import org.junit.Test

import scala.runtime.BoxedUnit

class ArrayTest {

@Test
def testArrayIsEmpty(): Unit = {
assertTrue(Array[Int]().isEmpty)
assertTrue(Array[Char]().isEmpty) // scala/bug#12172
assertTrue(Array[String]().isEmpty)

assertFalse(Array(1).isEmpty)
assertFalse(Array[Char](1).isEmpty)
assertFalse(Array("").isEmpty)

def ge[T](a: Array[T]) = a.isEmpty

assertTrue(ge(Array[Int]()))
assertTrue(ge(Array[Char]()))
assertTrue(ge(Array[String]()))

assertFalse(ge(Array(1)))
assertFalse(ge(Array[Char]('x')))
assertFalse(ge(Array("")))
}
}

0 comments on commit 6d609f1

Please sign in to comment.