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

Add compiletime.ops.string.CharAt #14431

Merged
merged 2 commits into from
May 20, 2022
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,7 @@ class Definitions {
)
private val compiletimePackageBooleanTypes: Set[Name] = Set(tpnme.Not, tpnme.Xor, tpnme.And, tpnme.Or)
private val compiletimePackageStringTypes: Set[Name] = Set(
tpnme.Plus, tpnme.Length, tpnme.Substring, tpnme.Matches
tpnme.Plus, tpnme.Length, tpnme.Substring, tpnme.Matches, tpnme.CharAt
)
private val compiletimePackageOpTypes: Set[Name] =
Set(tpnme.S)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ object StdNames {
final val Plus: N = "+"
final val S: N = "S"
final val Substring: N = "Substring"
final val CharAt: N = "CharAt"
final val Times: N = "*"
final val ToInt: N = "ToInt"
final val ToLong: N = "ToLong"
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4439,6 +4439,8 @@ object Types {
case tpnme.Matches => constantFold2(stringValue, _ matches _)
case tpnme.Substring =>
constantFold3(stringValue, intValue, intValue, (s, b, e) => s.substring(b, e))
case tpnme.CharAt =>
constantFold2AB(stringValue, intValue, _ charAt _)
case _ => None
} else if (owner == defn.CompiletimeOpsBooleanModuleClass) name match {
case tpnme.Not => constantFold1(boolValue, x => !x)
Expand Down
4 changes: 0 additions & 4 deletions library/src/scala/compiletime/ops/any.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package scala.compiletime
package ops

import annotation.experimental

object any:
/** Equality comparison of two singleton types.
* ```scala
Expand Down Expand Up @@ -41,7 +39,6 @@ object any:
* ```
* @syntax markdown
*/
@experimental
type IsConst[X] <: Boolean

/** String conversion of a constant singleton type.
Expand All @@ -51,5 +48,4 @@ object any:
* ```
* @syntax markdown
*/
@experimental
type ToString[+X] <: String
3 changes: 0 additions & 3 deletions library/src/scala/compiletime/ops/double.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package scala.compiletime
package ops

import scala.annotation.experimental

@experimental
object double:
/** Addition of two `Double` singleton types.
* ```scala
Expand Down
3 changes: 0 additions & 3 deletions library/src/scala/compiletime/ops/float.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package scala.compiletime
package ops

import scala.annotation.experimental

@experimental
object float:
/** Addition of two `Float` singleton types.
* ```scala
Expand Down
6 changes: 0 additions & 6 deletions library/src/scala/compiletime/ops/int.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package scala.compiletime
package ops

import annotation.experimental

object int:
/** Successor of a natural number where zero is the type 0 and successors are reduced as if the definition was:
*
Expand Down Expand Up @@ -192,7 +190,6 @@ object int:
* ```
* @syntax markdown
*/
@experimental
type ToLong[+X <: Int] <: Long

/** Float conversion of an `Int` singleton type.
Expand All @@ -201,7 +198,6 @@ object int:
* ```
* @syntax markdown
*/
@experimental
type ToFloat[+X <: Int] <: Float

/** Double conversion of an `Int` singleton type.
Expand All @@ -210,7 +206,6 @@ object int:
* ```
* @syntax markdown
*/
@experimental
type ToDouble[+X <: Int] <: Double

/** Number of zero bits preceding the highest-order ("leftmost")
Expand All @@ -225,5 +220,4 @@ object int:
* ```
* @syntax markdown
*/
@experimental
type NumberOfLeadingZeros[+X <: Int] <: Int
3 changes: 0 additions & 3 deletions library/src/scala/compiletime/ops/long.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package scala.compiletime
package ops

import scala.annotation.experimental

@experimental
object long:
/** Successor of a natural number where zero is the type 0 and successors are reduced as if the definition was:
*
Expand Down
15 changes: 10 additions & 5 deletions library/src/scala/compiletime/ops/string.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package scala.compiletime
package ops

import scala.annotation.experimental

object string:
/** Concatenation of two `String` singleton types.
* ```scala
Expand All @@ -18,7 +16,6 @@ object string:
* ```
* @syntax markdown
*/
@experimental
type Length[+X <: String] <: Int

/** Substring of a `String` singleton type, with a singleton type
Expand All @@ -31,7 +28,6 @@ object string:
* ```
* @syntax markdown
*/
@experimental
type Substring[+S <: String, +IBeg <: Int, +IEnd <: Int] <: String

/** Tests if this `String` singleton type matches the given
Expand All @@ -41,5 +37,14 @@ object string:
* ```
* @syntax markdown
*/
@experimental
type Matches[+S <: String, +Regex <: String] <: Boolean

/** Returns the Char type at the specified index.
* An index ranges from 0 to Length[S] - 1. The first Char of
* the sequence is at index 0, the next at index 1, and so on.
* ```scala
* val c: CharAt["hello", 0] = 'h'
* ```
* @syntax markdown
*/
type CharAt[+S <: String, +Idx <: Int] <: Char
19 changes: 19 additions & 0 deletions tests/neg/singleton-ops-string.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,23 @@ object Test {

val t9: Matches["hamburger", "ham.*"] = true
val t10: Matches["hamburger", "ham.*"] = false // error

val t11: CharAt["String", 0] = 'S'
val t12: CharAt["String", 1] = 't'
val t13: CharAt["String", 2] = '!' // error
// ^^^
// Found: ('!' : Char)
// Required: ('r' : Char)
val t14: CharAt["String", 3] = '!' // error
// ^^^
// Found: ('!' : Char)
// Required: ('i' : Char)
val t15: CharAt["String", 4] = 'n'
val t16: CharAt["String", 5] = 'g'
val t17: CharAt["String", 6] = '!' // error
// ^
// String index out of range: 6
val t18: CharAt["String", -1] = '?' // error
// ^
// String index out of range: -1
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,6 @@ val experimentalDefinitionInLibrary = Set(
"scala.annotation.MainAnnotation",
"scala.annotation.MainAnnotation$",

//// New APIs: compiletime.ops
// Can be stabilized in 3.3.0 or later.
// Needs user feedback
"scala.compiletime.ops.any$.IsConst",
"scala.compiletime.ops.any$.ToString",
"scala.compiletime.ops.double", "scala.compiletime.ops.double$",
"scala.compiletime.ops.float",
"scala.compiletime.ops.float$",
"scala.compiletime.ops.int$.NumberOfLeadingZeros",
"scala.compiletime.ops.int$.ToDouble",
"scala.compiletime.ops.int$.ToFloat",
"scala.compiletime.ops.int$.ToLong",
"scala.compiletime.ops.long", "scala.compiletime.ops.long$",
"scala.compiletime.ops.string$.Length",
"scala.compiletime.ops.string$.Matches",
"scala.compiletime.ops.string$.Substring",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mbovel didn't we say that these operations would stay experimental?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #14431 (comment); when we first discussed it, I hadn't realized that some compile-time operations were already stable. Given this, I re-discussed it with Martin and he agreed to make these operations stable as well.

The argument is that operations on long, float and double are exactly the same as the ones on int which are already stable, so it makes sense to make them stable as well. In term of maintenance, this doesn't change much.

Including operations on strings as well makes sense are they are important building blocks to do anything useful with strings at the type-level (Olivier used them for his work on regular expressions for example).


//// New APIs: Mirror
// Can be stabilized in 3.2.0 or later.
"scala.deriving.Mirror$.fromTuple", // Probably for 3.2.0
Expand Down