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

Support universal equality of unsigned numeric types #3584

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 63 additions & 11 deletions auxlib/src/main/scala/scala/runtime/BoxesRunTime.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package scala.runtime
import scala.math.ScalaNumber
import scala.annotation.{nowarn, switch}

import scala.scalanative.meta.LinktimeInfo
import scala.scalanative.unsigned._
import scala.scalanative.unsafe.Size

class BoxesRunTime

/** An object (static class) that defines methods used for creating, reverting,
Expand All @@ -24,21 +28,53 @@ object BoxesRunTime {
private final val LONG = 4
private final val FLOAT = 5
private final val DOUBLE = 6
private final val OTHER = 7
private final val ULONG = 7 // special case for comparing unsigned types
private final val OTHER = 8

/** We don't need to return BYTE and SHORT, as everything which might care
* widens to INT.
*/
private def typeCode(a: java.lang.Object): scala.Int = a match {
case _: java.lang.Integer => INT
case _: java.lang.Double => DOUBLE
case _: java.lang.Long => LONG
case _: java.lang.Character => CHAR
case _: java.lang.Float => FLOAT
case _: java.lang.Byte => INT
case _: java.lang.Short => INT
case _ => OTHER
}
case num: scala.math.ScalaNumber => typeCodeScalaNumber(num)
case num: java.lang.Number => typeCodeNumber(num)
case _: java.lang.Character => CHAR
case _ => OTHER
}

private def typeCodeNumber(a: java.lang.Number): scala.Int = a match {
case _: java.lang.Integer => INT
case _: java.lang.Double => DOUBLE
case _: java.lang.Long => LONG
case _: java.lang.Float => FLOAT
case _: java.lang.Byte => INT
case _: java.lang.Short => INT
case _ => OTHER
}

private def typeCodeScalaNumber(num: scala.math.ScalaNumber): scala.Int =
num match {
case _: UByte => INT
case _: UShort => INT
case _: UInt => LONG
case _: ULong => ULONG
case _: Size => if (LinktimeInfo.is32BitPlatform) INT else LONG
case _: USize => if (LinktimeInfo.is32BitPlatform) LONG else ULONG
case _ => OTHER
}

// Char is unsigned, we don't need to extended int/long
WojciechMazur marked this conversation as resolved.
Show resolved Hide resolved
private def typeCodeScalaNumberForChar(
num: scala.math.ScalaNumber
): scala.Int =
num match {
case _: UByte => INT
case _: UShort => INT
case _: UInt => INT
case _: ULong => LONG
case _: Size => if (LinktimeInfo.is32BitPlatform) INT else LONG
case _: USize => if (LinktimeInfo.is32BitPlatform) INT else LONG
case _ => OTHER
}

// Boxing
@inline def boxToBoolean(v: scala.Boolean): java.lang.Boolean =
Expand Down Expand Up @@ -108,6 +144,18 @@ object BoxesRunTime {
case LONG => xn.longValue() == yn.longValue()
case FLOAT => xn.floatValue() == yn.floatValue()
case DOUBLE => xn.doubleValue() == yn.doubleValue()
case ULONG =>
// todo: use extension to int128 when available
val xnIsUnsigned = xn.isInstanceOf[ULong] || xn.isInstanceOf[USize]
val longVal = if (xnIsUnsigned) xn else yn
val otherVal = if (xnIsUnsigned) yn else xn
otherVal match {
case other: Size if !LinktimeInfo.is32BitPlatform =>
other.longValue() >= 0 && longVal.longValue == other.longValue
case other: java.lang.Long =>
other.longValue() >= 0 && longVal.longValue == other.longValue
case other => longVal.longValue() == other.longValue()
}
case _ =>
if (yn.isInstanceOf[ScalaNumber] && !xn.isInstanceOf[ScalaNumber])
yn.equals(xn)
Expand All @@ -128,7 +176,11 @@ object BoxesRunTime {
if (yc == null) xn == null
else {
val ch = yc.charValue()
(typeCode(xn): @switch) match {
val typeCode = xn match {
case that: ScalaNumber => typeCodeScalaNumberForChar(that)
case that => typeCodeNumber(that)
}
(typeCode: @switch) match {
case INT => xn.intValue() == ch
case LONG => xn.longValue() == ch
case FLOAT => xn.floatValue() == ch
Expand Down
4 changes: 4 additions & 0 deletions docs/user/interop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -513,4 +513,8 @@ using ``byteValue.toUByte``, ``shortValue.toUShort``, ``intValue.toUInt``, ``lon
and conversely ``unsignedByteValue.toByte``, ``unsignedShortValue.toShort``, ``unsignedIntValue.toInt``,
``unsignedLongValue.toLong``, ``unsignedSizeValue.toSize``.

Universal equality is supported between signed and unsigned integers, for example ``-1.toUByte == 255`` or ``65535 == -1.toUShort`` would yield ``true``,
However, similary to singned integers on JVM, class equality between different (boxed) integer types is not supported.
WojciechMazur marked this conversation as resolved.
Show resolved Hide resolved
Usage of `-1.toUByte.equals(255)` would return ``false``, as we're comparing different boxed types (``scala.scalanative.unsigned.UByte`` with ``java.lang.Integer``)

Continue to :ref:`native`.
16 changes: 8 additions & 8 deletions javalib/src/main/scala/java/io/File.scala
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class File(_path: String) extends Serializable with Comparable[File] {
dacl = previousDacl,
sacl = null,
securityDescriptor = securityDescriptorPtr
) == 0.toUInt
) == 0

def setupNewAclEntry() = {
import accctrl.ops._
Expand All @@ -188,7 +188,7 @@ class File(_path: String) extends Serializable with Comparable[File] {
ea.trustee.trusteeType = TrusteeType.TRUSTEE_IS_WELL_KNOWN_GROUP
ea.trustee.sid = !usersGroupSid
}
SetEntriesInAclW(1.toUInt, ea, !previousDacl, newDacl) == 0.toUInt
SetEntriesInAclW(1.toUInt, ea, !previousDacl, newDacl) == 0
}

def assignNewSecurityInfo() =
Expand All @@ -200,7 +200,7 @@ class File(_path: String) extends Serializable with Comparable[File] {
sidGroup = null,
dacl = !newDacl,
sacl = null
) == 0.toUInt
) == 0

try {
getSecurityDescriptor() &&
Expand All @@ -217,7 +217,7 @@ class File(_path: String) extends Serializable with Comparable[File] {
val filename = toCWideStringUTF16LE(properPath)
val attrs = GetFileAttributesW(filename)
val pathExists = attrs != INVALID_FILE_ATTRIBUTES
val notSymLink = (attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0.toUInt
val notSymLink = (attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0
if (notSymLink) // fast path
pathExists
else {
Expand Down Expand Up @@ -718,13 +718,13 @@ object File {
if (isWindows) {
val buffSize = GetCurrentDirectoryW(0.toUInt, null)
val buff: Ptr[windows.WChar] = alloc[windows.WChar](buffSize + 1.toUInt)
if (GetCurrentDirectoryW(buffSize, buff) == 0.toUInt) {
if (GetCurrentDirectoryW(buffSize, buff) == 0) {
throw WindowsException("error in trying to get user directory")
}
fromCWideString(buff, StandardCharsets.UTF_16LE)
} else {
val buff: CString = alloc[CChar](4096)
if (getcwd(buff, 4095.toUInt) == 0.toUInt) {
if (getcwd(buff, 4095.toUInt) == null) {
val errMsg = fromCString(string.strerror(errno.errno))
throw new IOException(
s"error in trying to get user directory - $errMsg"
Expand Down Expand Up @@ -879,7 +879,7 @@ object File {
var i = start
while (i < strlen(path) && path(i) != separatorChar) i += `1U`

if (i == strlen(path).toUInt) resolveLink(path, resolveAbsolute = true)
if (i == strlen(path)) resolveLink(path, resolveAbsolute = true)
else {
// copy path from start to next separator.
// and resolve that subpart.
Expand Down Expand Up @@ -924,7 +924,7 @@ object File {
flags = finalPathFlags
)

if (fileHandle == HandleApiExt.INVALID_HANDLE_VALUE || pathLength == 0.toUInt)
if (fileHandle == HandleApiExt.INVALID_HANDLE_VALUE || pathLength == 0)
null
else buffer
}
Expand Down
12 changes: 0 additions & 12 deletions javalib/src/main/scala/java/lang/Byte.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,6 @@ final class Byte(val _value: scala.Byte)
@inline override def toString(): String =
Byte.toString(_value)

@inline override def __scala_==(other: _Object): scala.Boolean =
other match {
case other: java.lang.Byte => _value == other._value
case other: java.lang.Short => _value == other._value
case other: java.lang.Integer => _value == other._value
case other: java.lang.Long => _value == other._value
case other: java.lang.Float => _value == other._value
case other: java.lang.Double => _value == other._value
case other: java.lang.Character => _value == other._value
case _ => super.__scala_==(other)
}

/*
* Ported from ScalaJS
*
Expand Down
13 changes: 0 additions & 13 deletions javalib/src/main/scala/java/lang/Character.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,6 @@ class Character(val _value: scala.Char)
@inline override def hashCode(): Int =
Character.hashCode(_value)

@inline override def __scala_==(other: _Object): scala.Boolean =
other match {
case other: java.lang.Character => _value == other._value
case other: java.lang.Byte => _value == other._value
case other: java.lang.Short => _value == other._value
case other: java.lang.Integer => _value == other._value
case other: java.lang.Long => _value == other._value
case other: java.lang.Float => _value == other._value
case other: java.lang.Double => _value == other._value
case other: java.lang.Number => other.__scala_==(this)
case _ => super.__scala_==(other)
}

/*
* Ported from ScalaJS
*
Expand Down
12 changes: 0 additions & 12 deletions javalib/src/main/scala/java/lang/Double.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,6 @@ final class Double(val _value: scala.Double)
@inline override def toString(): String =
Double.toString(_value)

@inline override def __scala_==(other: _Object): scala.Boolean =
other match {
case other: java.lang.Double => _value == other._value
case other: java.lang.Byte => _value == other._value
case other: java.lang.Short => _value == other._value
case other: java.lang.Integer => _value == other._value
case other: java.lang.Long => _value == other._value
case other: java.lang.Float => _value == other._value
case other: java.lang.Character => _value == other._value
case _ => super.__scala_==(other)
}

@inline override def __scala_## : scala.Int = {
val dv = _value
val iv = _value.toInt
Expand Down
12 changes: 0 additions & 12 deletions javalib/src/main/scala/java/lang/Float.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,6 @@ final class Float(val _value: scala.Float)
@inline override def toString(): String =
Float.toString(_value)

@inline override def __scala_==(other: _Object): scala.Boolean =
other match {
case other: java.lang.Float => _value == other._value
case other: java.lang.Byte => _value == other._value
case other: java.lang.Short => _value == other._value
case other: java.lang.Integer => _value == other._value
case other: java.lang.Long => _value == other._value
case other: java.lang.Double => _value == other._value
case other: java.lang.Character => _value == other._value
case _ => super.__scala_==(other)
}

@inline override def __scala_## : scala.Int = {
val fv = _value
val iv = _value.toInt
Expand Down
12 changes: 0 additions & 12 deletions javalib/src/main/scala/java/lang/Integer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,6 @@ final class Integer(val _value: scala.Int)
@inline override def toString(): String =
Integer.toString(_value)

@inline override def __scala_==(other: _Object): scala.Boolean =
other match {
case other: java.lang.Integer => _value == other._value
case other: java.lang.Byte => _value == other._value
case other: java.lang.Short => _value == other._value
case other: java.lang.Long => _value == other._value
case other: java.lang.Float => _value == other._value
case other: java.lang.Double => _value == other._value
case other: java.lang.Character => _value == other._value
case _ => super.__scala_==(other)
}

/*
* Ported from ScalaJS
*
Expand Down
12 changes: 0 additions & 12 deletions javalib/src/main/scala/java/lang/Long.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,6 @@ final class Long(val _value: scala.Long)
@inline override def toString(): String =
Long.toString(_value)

@inline override def __scala_==(other: _Object): scala.Boolean =
other match {
case other: java.lang.Long => _value == other._value
case other: java.lang.Byte => _value == other._value
case other: java.lang.Short => _value == other._value
case other: java.lang.Integer => _value == other._value
case other: java.lang.Float => _value == other._value
case other: java.lang.Double => _value == other._value
case other: java.lang.Character => _value == other._value
case _ => super.__scala_==(other)
}

@inline override def __scala_## : scala.Int = {
val lv = _value
val iv = _value.toInt
Expand Down
7 changes: 0 additions & 7 deletions javalib/src/main/scala/java/lang/Number.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,4 @@ abstract class Number extends java.lang._Object with java.io.Serializable {
def floatValue(): scala.Float
def doubleValue(): scala.Double

@inline override def __scala_==(other: _Object): scala.Boolean = {
if (other.isInstanceOf[ScalaNumber] && !this.isInstanceOf[ScalaNumber]) {
other.equals(this)
} else {
this.equals(other)
}
}
}
12 changes: 0 additions & 12 deletions javalib/src/main/scala/java/lang/Short.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,6 @@ final class Short(val _value: scala.Short)
@inline override def toString(): String =
Short.toString(_value)

@inline override def __scala_==(other: _Object): scala.Boolean =
other match {
case other: java.lang.Short => _value == other._value
case other: java.lang.Byte => _value == other._value
case other: java.lang.Integer => _value == other._value
case other: java.lang.Long => _value == other._value
case other: java.lang.Float => _value == other._value
case other: java.lang.Double => _value == other._value
case other: java.lang.Character => _value == other._value
case _ => super.__scala_==(other)
}

/*
* Methods on scala.Short
* The following methods are only here to properly support reflective calls
Expand Down
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/lang/System.scala
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ private object SystemProperties {
val bufSize = 1024.toUInt
if (isWindows) {
val buf: Ptr[CChar16] = stackalloc[CChar16](bufSize)
if (GetCurrentDirectoryW(bufSize, buf) != 0.toUInt)
if (GetCurrentDirectoryW(bufSize, buf) != 0)
Some(fromCWideString(buf, StandardCharsets.UTF_16LE))
else None
} else {
Expand Down
4 changes: 2 additions & 2 deletions javalib/src/main/scala/java/lang/process/WindowsProcess.scala
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,9 @@ object WindowsProcess {
list.add("")
val totalSize = list.scalaOps.foldLeft(0)(_ + _.size + 1) - 1
val blockEnd = block + totalSize
assert(!blockEnd == 0.toUShort, s"not null terminated got ${!blockEnd}")
assert(!blockEnd == 0, s"not null terminated got ${!blockEnd}")
assert(
!(blockEnd - 1) == 0.toUShort,
!(blockEnd - 1) == 0,
s"not null terminated -1, got ${!(blockEnd - 1)}"
)

Expand Down
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/net/NetworkInterface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ object NetworkInterface {

val broadcastAddress: Option[Array[Byte]] =
if (sa.sa_family.toInt == AF_INET6) None
else if ((ifa.ifa_flags & unixIf.IFF_LOOPBACK.toUInt) != 0.toUInt) None
else if ((ifa.ifa_flags & unixIf.IFF_LOOPBACK.toUInt) != 0) None
else Some(sockaddrToByteArray(ifa.ifa_broadaddr))

val prefixLen = decodePrefixLength(ifa.ifa_netmask)
Expand Down
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/net/SocketHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ object SocketHelpers {
if ((ai == null) || (ai.ai_addr == null)) {
false
} else {
ai.ai_addr.sa_family == AF_INET6.toUShort
ai.ai_addr.sa_family == AF_INET6
}
} finally {
freeaddrinfo(!ret)
Expand Down
2 changes: 1 addition & 1 deletion javalib/src/main/scala/java/nio/file/Files.scala
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ object Files {
val filename = toCWideStringUTF16LE(path.toFile().getPath())
val attrs = FileApi.GetFileAttributesW(filename)
val exists = attrs != INVALID_FILE_ATTRIBUTES
def isReparsePoint = (attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0.toUInt
def isReparsePoint = (attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0
exists & isReparsePoint
} else {
val filename = toCString(path.toFile().getPath())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class WindowsAclFileAttributeView(path: Path, options: Array[LinkOption])
dacl = null,
sacl = null,
securityDescriptor = null
) != 0.toUInt) {
) != 0) {
throw WindowsException("Failed to get ownership info")
}
WindowsUserPrincipal(!ownerSid)
Expand Down Expand Up @@ -63,7 +63,7 @@ class WindowsAclFileAttributeView(path: Path, options: Array[LinkOption])
sidGroup = null,
dacl = null,
sacl = null
) != 0.toUInt) {
) != 0) {
throw WindowsException("Failed to set new owner")
}
}
Expand Down