Skip to content
Open
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 @@ -1430,7 +1430,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.CharAt
tpnme.Plus, tpnme.Length, tpnme.Substring, tpnme.Matches, tpnme.CharAt, tpnme.LT, tpnme.GT, tpnme.LE, tpnme.GE
)
private val compiletimePackageOpTypes: Set[Name] =
Set(tpnme.S, tpnme.From)
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/TypeEval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ object TypeEval:
constantFold3(stringValue, intValue, intValue, (s, b, e) => s.substring(b, e))
case tpnme.CharAt =>
constantFold2AB(stringValue, intValue, _.charAt(_))
case tpnme.LT => constantFold2(stringValue, _ < _)
case tpnme.GT => constantFold2(stringValue, _ > _)
case tpnme.LE => constantFold2(stringValue, _ <= _)
case tpnme.GE => constantFold2(stringValue, _ >= _)
case _ => None
else if owner == defn.CompiletimeOpsBooleanModuleClass then name match
case tpnme.Not => constantFold1(boolValue, x => !x)
Expand Down
44 changes: 44 additions & 0 deletions library/src/scala/compiletime/ops/string.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,50 @@ object string:
*/
infix type +[X <: String, Y <: String] <: String

/** Lexicographic comparison of two `String` singleton types.
* ```scala
* //{
* import compiletime.ops.string.*
* //}
* val hello: "hello " < "world" = true
* ```
* @syntax markdown
*/
infix type <[S1 <: String, S2 <: String] <: Boolean

/** Lexicographic comparison of two `String` singleton types.
* ```scala
* //{
* import compiletime.ops.string.*
* //}
* val hello: "hello " > "world" = false
* ```
* @syntax markdown
*/
infix type >[S1 <: String, S2 <: String] <: Boolean

/** Lexicographic comparison of two `String` singleton types.
* ```scala
* //{
* import compiletime.ops.string.*
* //}
* val hello: "hello " >= "world" = false
* ```
* @syntax markdown
*/
infix type >=[S1 <: String, S2 <: String] <: Boolean

/** Lexicographic comparison of two `String` singleton types.
* ```scala
* //{
* import compiletime.ops.string.*
* //}
* val hello: "hello " <= "world" = true
* ```
* @syntax markdown
*/
infix type <=[S1 <: String, S2 <: String] <: Boolean

/** Length of a `String` singleton type.
* ```scala
* //{
Expand Down
62 changes: 62 additions & 0 deletions tests/neg/singleton-ops-string.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,66 @@ object Test {
val t18: CharAt["String", -1] = '?' // error
// ^
// String index out of range: -1

val t19: "hello" < "world" = true
val t20: "hello" < "world" = false // error

val t21: "hello" > "world" = false
val t22: "hello" > "world" = true // error

val t23: "hello" <= "world" = true
val t24: "hello" <= "world" = false // error
val t25: "hello" <= "hello" = true
val t26: "hello" <= "hello" = false // error

val t27: "hello" >= "world" = false
val t28: "hello" >= "world" = true // error
val t29: "hello" >= "hello" = true
val t30: "hello" >= "hello" = false // error

import scala.compiletime.ops.int./

type If[C <: Boolean, Then, Else] = C match
case true => Then
case false => Else

type ZipTuples[First, Second] = (First, Second) match
case (first *: EmptyTuple, second *: EmptyTuple) => (first, second) *: EmptyTuple
case (first *: firstRest, second *: secondRest) => (first, second) *: ZipTuples[firstRest, secondRest]

type TuplePairs[InputTuple] = InputTuple match
case NamedTuple.NamedTuple[names, values] => ZipTuples[names, values]

type PrependTuplePairs[First, Second, Pairs] = Pairs match
case EmptyTuple => (First *: EmptyTuple) *: (Second *: EmptyTuple) *: EmptyTuple
case left *: right *: EmptyTuple => (First *: left) *: (Second *: right) *: EmptyTuple

type UnzipTuples[Input] = Input match
case (first *: second *: EmptyTuple) *: rest => PrependTuplePairs[first, second, UnzipTuples[rest]]
case EmptyTuple => EmptyTuple

type NamedTupleFromPairs[Input] = UnzipTuples[Input] match
case EmptyTuple => NamedTuple.NamedTuple[EmptyTuple, EmptyTuple]
case left *: right *: EmptyTuple => NamedTuple.NamedTuple[left, right]

type Merge[P1 <: Tuple, P2 <: Tuple] <: Tuple = (P1, P2) match
case (EmptyTuple, h *: t) => h *: t
case (h *: t, EmptyTuple) => h *: t
case ((k1, v1) *: t1, (k2, v2) *: t2) => If[k1 <= k2,
(k1, v1) *: Merge[t1, (k2, v2) *: t2],
(k2, v2) *: Merge[(k1, v1) *: t1, t2]
]

type SortPairs[P <: Tuple] <: Tuple = P match
case EmptyTuple => EmptyTuple
case h *: EmptyTuple => h *: EmptyTuple
case _ => Merge[
SortPairs[Tuple.Take[P, Tuple.Size[P] / 2]],
SortPairs[Tuple.Drop[P, Tuple.Size[P] / 2]]
]

type SortNamedTuple[NT <: NamedTuple.AnyNamedTuple] = NamedTupleFromPairs[SortPairs[TuplePairs[NT]]]

val t31: SortNamedTuple[(foo: 45, bar: "x", baz: 6.0, quux: false)] = (bar = "x", baz = 6.0, foo = 45, quux = false)
val t32: SortNamedTuple[(foo: 45, bar: "x", baz: 6.0, quux: false)] = (foo = 45, bar = "x", baz = 6.0, quux = false) // error
}
Loading