Skip to content

Commit e8d00f8

Browse files
committed
improve any.IsConst to be evaluated only if its argument is concrete and known
1 parent d433ce8 commit e8d00f8

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4240,9 +4240,36 @@ object Types {
42404240
case _ => None
42414241
}
42424242

4243-
def isConst(tp : Type) : Option[Type] = tp.fixForEvaluation match {
4244-
case ConstantType(_) => Some(ConstantType(Constant(true)))
4245-
case _ => Some(ConstantType(Constant(false)))
4243+
val opsSet = Set(
4244+
defn.CompiletimeOpsAnyModuleClass,
4245+
defn.CompiletimeOpsIntModuleClass,
4246+
defn.CompiletimeOpsLongModuleClass,
4247+
defn.CompiletimeOpsFloatModuleClass,
4248+
defn.CompiletimeOpsBooleanModuleClass,
4249+
defn.CompiletimeOpsStringModuleClass
4250+
)
4251+
4252+
//Returns Some(true) if the type is a constant.
4253+
//Returns Some(false) if the type is not a constant.
4254+
//Returns None if there is not enough information to determine if the type is a constant.
4255+
//The type is a constant if it is a constant type or a type operation composition of constant types.
4256+
//If we get a type reference for an argument, then the result is not yet known.
4257+
def isConst(tp : Type) : Option[Boolean] = tp.dealias match {
4258+
//known to be constant
4259+
case ConstantType(_) => Some(true)
4260+
//currently not a concrete known type
4261+
case TypeRef(NoPrefix,_) => None
4262+
//currently not a concrete known type
4263+
case _ : TypeParamRef => None
4264+
//constant if the term is constant
4265+
case t : TermRef => isConst(t.underlying)
4266+
//an operation type => recursively check all argument compositions
4267+
case applied : AppliedType if opsSet.contains(applied.typeSymbol.owner) =>
4268+
val argsConst = applied.args.map(isConst)
4269+
if (argsConst.exists(_.isEmpty)) None
4270+
else Some(argsConst.forall(_.get))
4271+
//all other types are considered not to be constant
4272+
case _ => Some(false)
42464273
}
42474274

42484275
def expectArgsNum(expectedNum : Int) : Unit =
@@ -4300,7 +4327,7 @@ object Types {
43004327
case tpnme.Equals => constantFold2(constValue, _ == _)
43014328
case tpnme.NotEquals => constantFold2(constValue, _ != _)
43024329
case tpnme.ToString => constantFold1(constValue, _.toString)
4303-
case tpnme.IsConst => isConst(args.head)
4330+
case tpnme.IsConst => isConst(args.head).map(b => ConstantType(Constant(b)))
43044331
case _ => None
43054332
} else if (owner == defn.CompiletimeOpsIntModuleClass) name match {
43064333
case tpnme.Abs => constantFold1(intValue, _.abs)

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ object any:
2929
* val c1: IsConst[1] = true
3030
* val c2: IsConst["hi"] = true
3131
* val c3: IsConst[false] = true
32+
* val c4: IsConst[Any] = false
33+
* ```
34+
* If the type is not yet known, then `IsConst` remains unevaluated, and
35+
* will be evaluated only at its concrete type application. E.g.:
36+
* ```scala
37+
* //def `isConst`` returns the type `IsConst[X]`, since `X` is not yet known.
38+
* def isConst[X] : IsConst[X] = ???
39+
* val c5 : true = isConst[1] //now the type is known to be a constant
40+
* val c6 : false = isConst[Any] //now the type is known to be not a constant
3241
* ```
3342
* @syntax markdown
3443
*/

tests/neg/singleton-ops-any.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,11 @@ object Test {
3030
final val two = 2
3131
val t47 : IsConst[two.type] = true
3232
val t48: IsConst[Any] = true // error
33-
33+
def isConst[X] : IsConst[X] = ???
34+
val t49 : true = isConst[1]
35+
val t50 : false = isConst[one.type]
36+
def isConst2[X <: Int, Y <: Int] : IsConst[X == Y] = ???
37+
val t51 : true = isConst2[1, 1]
38+
val t52 : false = isConst2[1, one.type]
39+
val t53 : true = isConst2[1, two.type]
3440
}

0 commit comments

Comments
 (0)