Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[backport] Make the CharSequence wrappers in Predef non-implicit, for JDK 15 #9322

Merged
merged 1 commit into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions src/library/scala/Predef.scala
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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("")))
}
}