Skip to content

Commit 091b0c3

Browse files
committed
Adds compiletime.ops.long, adds other ops, and fixes termref type not being considered.
1 parent 2312258 commit 091b0c3

File tree

12 files changed

+540
-42
lines changed

12 files changed

+540
-42
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ class Definitions {
246246
@tu lazy val CompiletimeOpsPackage: Symbol = requiredPackage("scala.compiletime.ops")
247247
@tu lazy val CompiletimeOpsAnyModuleClass: Symbol = requiredModule("scala.compiletime.ops.any").moduleClass
248248
@tu lazy val CompiletimeOpsIntModuleClass: Symbol = requiredModule("scala.compiletime.ops.int").moduleClass
249+
@tu lazy val CompiletimeOpsLongModuleClass: Symbol = requiredModule("scala.compiletime.ops.long").moduleClass
249250
@tu lazy val CompiletimeOpsStringModuleClass: Symbol = requiredModule("scala.compiletime.ops.string").moduleClass
250251
@tu lazy val CompiletimeOpsBooleanModuleClass: Symbol = requiredModule("scala.compiletime.ops.boolean").moduleClass
251252

@@ -1071,19 +1072,28 @@ class Definitions {
10711072
final def isCompiletime_S(sym: Symbol)(using Context): Boolean =
10721073
sym.name == tpnme.S && sym.owner == CompiletimeOpsIntModuleClass
10731074

1074-
private val compiletimePackageAnyTypes: Set[Name] = Set(tpnme.Equals, tpnme.NotEquals)
1075+
private val compiletimePackageAnyTypes: Set[Name] = Set(
1076+
tpnme.Equals, tpnme.NotEquals, tpnme.IsConst, tpnme.ToString
1077+
)
10751078
private val compiletimePackageIntTypes: Set[Name] = Set(
10761079
tpnme.Plus, tpnme.Minus, tpnme.Times, tpnme.Div, tpnme.Mod,
10771080
tpnme.Lt, tpnme.Gt, tpnme.Ge, tpnme.Le,
1078-
tpnme.Abs, tpnme.Negate, tpnme.Min, tpnme.Max, tpnme.ToString,
1081+
tpnme.Abs, tpnme.Negate, tpnme.Min, tpnme.Max,
1082+
tpnme.ToString, //ToString is moved to ops.any and deprecated for ops.int
1083+
tpnme.NumberOfLeadingZeros,
10791084
tpnme.Xor, tpnme.BitwiseAnd, tpnme.BitwiseOr, tpnme.ASR, tpnme.LSL, tpnme.LSR
10801085
)
1086+
//ToString is moved to ops.any
1087+
private val compiletimePackageLongTypes: Set[Name] = compiletimePackageIntTypes - tpnme.ToString
10811088
private val compiletimePackageBooleanTypes: Set[Name] = Set(tpnme.Not, tpnme.Xor, tpnme.And, tpnme.Or)
1082-
private val compiletimePackageStringTypes: Set[Name] = Set(tpnme.Plus)
1089+
private val compiletimePackageStringTypes: Set[Name] = Set(
1090+
tpnme.Plus, tpnme.Length, tpnme.Substring, tpnme.Matches
1091+
)
10831092
private val compiletimePackageOpTypes: Set[Name] =
10841093
Set(tpnme.S)
10851094
++ compiletimePackageAnyTypes
10861095
++ compiletimePackageIntTypes
1096+
++ compiletimePackageLongTypes
10871097
++ compiletimePackageBooleanTypes
10881098
++ compiletimePackageStringTypes
10891099

@@ -1093,6 +1103,7 @@ class Definitions {
10931103
isCompiletime_S(sym)
10941104
|| sym.owner == CompiletimeOpsAnyModuleClass && compiletimePackageAnyTypes.contains(sym.name)
10951105
|| sym.owner == CompiletimeOpsIntModuleClass && compiletimePackageIntTypes.contains(sym.name)
1106+
|| sym.owner == CompiletimeOpsLongModuleClass && compiletimePackageLongTypes.contains(sym.name)
10961107
|| sym.owner == CompiletimeOpsBooleanModuleClass && compiletimePackageBooleanTypes.contains(sym.name)
10971108
|| sym.owner == CompiletimeOpsStringModuleClass && compiletimePackageStringTypes.contains(sym.name)
10981109
)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -211,29 +211,34 @@ object StdNames {
211211
final val IOOBException: N = "IndexOutOfBoundsException"
212212
final val FunctionXXL: N = "FunctionXXL"
213213

214-
final val Abs: N = "Abs"
215-
final val And: N = "&&"
216-
final val BitwiseAnd: N = "BitwiseAnd"
217-
final val BitwiseOr: N = "BitwiseOr"
218-
final val Div: N = "/"
219-
final val Equals: N = "=="
220-
final val Ge: N = ">="
221-
final val Gt: N = ">"
222-
final val Le: N = "<="
223-
final val Lt: N = "<"
224-
final val Max: N = "Max"
225-
final val Min: N = "Min"
226-
final val Minus: N = "-"
227-
final val Mod: N = "%"
228-
final val Negate: N = "Negate"
229-
final val Not: N = "!"
230-
final val NotEquals: N = "!="
231-
final val Or: N = "||"
232-
final val Plus: N = "+"
233-
final val S: N = "S"
234-
final val Times: N = "*"
235-
final val ToString: N = "ToString"
236-
final val Xor: N = "^"
214+
final val Abs: N = "Abs"
215+
final val And: N = "&&"
216+
final val BitwiseAnd: N = "BitwiseAnd"
217+
final val BitwiseOr: N = "BitwiseOr"
218+
final val Div: N = "/"
219+
final val Equals: N = "=="
220+
final val Ge: N = ">="
221+
final val Gt: N = ">"
222+
final val IsConst: N = "IsConst"
223+
final val Le: N = "<="
224+
final val Length: N = "Length"
225+
final val Lt: N = "<"
226+
final val Matches: N = "Matches"
227+
final val Max: N = "Max"
228+
final val Min: N = "Min"
229+
final val Minus: N = "-"
230+
final val Mod: N = "%"
231+
final val Negate: N = "Negate"
232+
final val Not: N = "!"
233+
final val NotEquals: N = "!="
234+
final val NumberOfLeadingZeros: N = "NumberOfLeadingZeros"
235+
final val Or: N = "||"
236+
final val Plus: N = "+"
237+
final val S: N = "S"
238+
final val Substring: N = "Substring"
239+
final val Times: N = "*"
240+
final val ToString: N = "ToString"
241+
final val Xor: N = "^"
237242

238243
final val ClassfileAnnotation: N = "ClassfileAnnotation"
239244
final val ClassManifest: N = "ClassManifest"

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4174,37 +4174,66 @@ object Types {
41744174

41754175
def tryCompiletimeConstantFold(using Context): Type = tycon match {
41764176
case tycon: TypeRef if defn.isCompiletimeAppliedType(tycon.symbol) =>
4177-
def constValue(tp: Type): Option[Any] = tp.dealias match {
4177+
extension (tp : Type) def fixForEvaluation : Type =
4178+
tp.normalized.dealias match {
4179+
case tp : TermRef => tp.underlying
4180+
case tp => tp
4181+
}
4182+
4183+
def constValue(tp: Type): Option[Any] = tp.fixForEvaluation match {
41784184
case ConstantType(Constant(n)) => Some(n)
41794185
case _ => None
41804186
}
41814187

4182-
def boolValue(tp: Type): Option[Boolean] = tp.dealias match {
4188+
def boolValue(tp: Type): Option[Boolean] = tp.fixForEvaluation match {
41834189
case ConstantType(Constant(n: Boolean)) => Some(n)
41844190
case _ => None
41854191
}
41864192

4187-
def intValue(tp: Type): Option[Int] = tp.dealias match {
4193+
def intValue(tp: Type): Option[Int] = tp.fixForEvaluation match {
41884194
case ConstantType(Constant(n: Int)) => Some(n)
41894195
case _ => None
41904196
}
41914197

4192-
def stringValue(tp: Type): Option[String] = tp.dealias match {
4193-
case ConstantType(Constant(n: String)) => Some(n)
4198+
def longValue(tp: Type): Option[Long] = tp.fixForEvaluation match {
4199+
case ConstantType(Constant(n: Long)) => Some(n)
41944200
case _ => None
41954201
}
41964202

4203+
def stringValue(tp: Type): Option[String] = tp.fixForEvaluation match {
4204+
case ConstantType(Constant(n: String)) => Some(n)
4205+
case _ => None
4206+
}
4207+
def isConst : Option[Type] = args.head.fixForEvaluation match {
4208+
case ConstantType(_) => Some(ConstantType(Constant(true)))
4209+
case _ => Some(ConstantType(Constant(false)))
4210+
}
41974211
def natValue(tp: Type): Option[Int] = intValue(tp).filter(n => n >= 0 && n < Int.MaxValue)
41984212

41994213
def constantFold1[T](extractor: Type => Option[T], op: T => Any): Option[Type] =
4200-
extractor(args.head.normalized).map(a => ConstantType(Constant(op(a))))
4214+
extractor(args.head).map(a => ConstantType(Constant(op(a))))
42014215

42024216
def constantFold2[T](extractor: Type => Option[T], op: (T, T) => Any): Option[Type] =
4217+
constantFold2AB(extractor, extractor, op)
4218+
4219+
def constantFold2AB[TA, TB](extractorA: Type => Option[TA], extractorB: Type => Option[TB], op: (TA, TB) => Any): Option[Type] =
42034220
for {
4204-
a <- extractor(args.head.normalized)
4205-
b <- extractor(args.tail.head.normalized)
4221+
a <- extractorA(args.head)
4222+
b <- extractorB(args.last)
42064223
} yield ConstantType(Constant(op(a, b)))
42074224

4225+
def constantFold3[TA, TB, TC](
4226+
extractorA: Type => Option[TA],
4227+
extractorB: Type => Option[TB],
4228+
extractorC: Type => Option[TC],
4229+
op: (TA, TB, TC) => Any
4230+
): Option[Type] =
4231+
for {
4232+
a <- extractorA(args.head)
4233+
b <- extractorB(args(1))
4234+
c <- extractorC(args.last)
4235+
} yield ConstantType(Constant(op(a, b, c)))
4236+
42084237
trace(i"compiletime constant fold $this", typr, show = true) {
42094238
val name = tycon.symbol.name
42104239
val owner = tycon.symbol.owner
@@ -4216,10 +4245,13 @@ object Types {
42164245
} else if (owner == defn.CompiletimeOpsAnyModuleClass) name match {
42174246
case tpnme.Equals if nArgs == 2 => constantFold2(constValue, _ == _)
42184247
case tpnme.NotEquals if nArgs == 2 => constantFold2(constValue, _ != _)
4248+
case tpnme.ToString if nArgs == 1 => constantFold1(constValue, _.toString)
4249+
case tpnme.IsConst if nArgs == 1 => isConst
42194250
case _ => None
42204251
} else if (owner == defn.CompiletimeOpsIntModuleClass) name match {
42214252
case tpnme.Abs if nArgs == 1 => constantFold1(intValue, _.abs)
42224253
case tpnme.Negate if nArgs == 1 => constantFold1(intValue, x => -x)
4254+
//ToString is deprecated for ops.int, and moved to ops.any
42234255
case tpnme.ToString if nArgs == 1 => constantFold1(intValue, _.toString)
42244256
case tpnme.Plus if nArgs == 2 => constantFold2(intValue, _ + _)
42254257
case tpnme.Minus if nArgs == 2 => constantFold2(intValue, _ - _)
@@ -4244,9 +4276,43 @@ object Types {
42444276
case tpnme.LSR if nArgs == 2 => constantFold2(intValue, _ >>> _)
42454277
case tpnme.Min if nArgs == 2 => constantFold2(intValue, _ min _)
42464278
case tpnme.Max if nArgs == 2 => constantFold2(intValue, _ max _)
4279+
case tpnme.NumberOfLeadingZeros if nArgs == 1 => constantFold1(intValue, Integer.numberOfLeadingZeros(_))
4280+
case _ => None
4281+
} else if (owner == defn.CompiletimeOpsLongModuleClass) name match {
4282+
case tpnme.Abs if nArgs == 1 => constantFold1(longValue, _.abs)
4283+
case tpnme.Negate if nArgs == 1 => constantFold1(longValue, x => -x)
4284+
case tpnme.Plus if nArgs == 2 => constantFold2(longValue, _ + _)
4285+
case tpnme.Minus if nArgs == 2 => constantFold2(longValue, _ - _)
4286+
case tpnme.Times if nArgs == 2 => constantFold2(longValue, _ * _)
4287+
case tpnme.Div if nArgs == 2 => constantFold2(longValue, {
4288+
case (_, 0L) => throw new TypeError("Division by 0")
4289+
case (a, b) => a / b
4290+
})
4291+
case tpnme.Mod if nArgs == 2 => constantFold2(longValue, {
4292+
case (_, 0L) => throw new TypeError("Modulo by 0")
4293+
case (a, b) => a % b
4294+
})
4295+
case tpnme.Lt if nArgs == 2 => constantFold2(longValue, _ < _)
4296+
case tpnme.Gt if nArgs == 2 => constantFold2(longValue, _ > _)
4297+
case tpnme.Ge if nArgs == 2 => constantFold2(longValue, _ >= _)
4298+
case tpnme.Le if nArgs == 2 => constantFold2(longValue, _ <= _)
4299+
case tpnme.Xor if nArgs == 2 => constantFold2(longValue, _ ^ _)
4300+
case tpnme.BitwiseAnd if nArgs == 2 => constantFold2(longValue, _ & _)
4301+
case tpnme.BitwiseOr if nArgs == 2 => constantFold2(longValue, _ | _)
4302+
case tpnme.ASR if nArgs == 2 => constantFold2(longValue, _ >> _)
4303+
case tpnme.LSL if nArgs == 2 => constantFold2(longValue, _ << _)
4304+
case tpnme.LSR if nArgs == 2 => constantFold2(longValue, _ >>> _)
4305+
case tpnme.Min if nArgs == 2 => constantFold2(longValue, _ min _)
4306+
case tpnme.Max if nArgs == 2 => constantFold2(longValue, _ max _)
4307+
case tpnme.NumberOfLeadingZeros if nArgs == 1 =>
4308+
constantFold1(longValue, java.lang.Long.numberOfLeadingZeros(_))
42474309
case _ => None
42484310
} else if (owner == defn.CompiletimeOpsStringModuleClass) name match {
42494311
case tpnme.Plus if nArgs == 2 => constantFold2(stringValue, _ + _)
4312+
case tpnme.Length if nArgs == 1 => constantFold1(stringValue, _.length)
4313+
case tpnme.Matches if nArgs == 2 => constantFold2(stringValue, _ matches _)
4314+
case tpnme.Substring if nArgs == 3 =>
4315+
constantFold3(stringValue, intValue, intValue, (s, b, e) => s.substring(b, e))
42504316
case _ => None
42514317
} else if (owner == defn.CompiletimeOpsBooleanModuleClass) name match {
42524318
case tpnme.Not if nArgs == 1 => constantFold1(boolValue, x => !x)

library/src/scala/compiletime/ops/any.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,22 @@ object any:
2121
* @syntax markdown
2222
*/
2323
type !=[X, Y] <: Boolean
24+
25+
/** Tests if a type is a constant.
26+
* ```scala
27+
* val c1: IsConst[1] = true
28+
* val c2: IsConst["hi"] = true
29+
* val c3: IsConst[false] = true
30+
* ```
31+
* @syntax markdown
32+
*/
33+
type IsConst[X] <: Boolean
34+
35+
/** String conversion of a constant singleton type.
36+
* ```scala
37+
* val s1: ToString[1] = "1"
38+
* val sTrue: ToString[true] = "true"
39+
* ```
40+
* @syntax markdown
41+
*/
42+
type ToString[X] <: String

library/src/scala/compiletime/ops/int.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,19 @@ object int:
181181
* ```
182182
* @syntax markdown
183183
*/
184+
@deprecated("Use compiletime.ops.any.ToString instead.","3.1.0")
184185
type ToString[X <: Int] <: String
186+
187+
/** Number of zero bits preceding the highest-order ("leftmost")
188+
* one-bit in the two's complement binary representation of the specified `Int` singleton type.
189+
* Returns 32 if the specified singleton type has no one-bits in its two's complement representation,
190+
* in other words if it is equal to zero.
191+
* ```scala
192+
* val zero_lzc: NumberOfLeadingZeros[0] = 32
193+
* val eight_lzc: NumberOfLeadingZeros[8] = 28
194+
* type Log2[N <: Int] = 31 - NumberOfLeadingZeros[N]
195+
* val log2of8: Log2[8] = 3
196+
* ```
197+
* @syntax markdown
198+
*/
199+
type NumberOfLeadingZeros[X <: Int] <: Int

0 commit comments

Comments
 (0)