Skip to content

Commit

Permalink
improve any.IsConst to be evaluated only if its argument is concret…
Browse files Browse the repository at this point in the history
…e and known
  • Loading branch information
soronpo committed Oct 26, 2021
1 parent d433ce8 commit e8d00f8
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 5 deletions.
35 changes: 31 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4240,9 +4240,36 @@ object Types {
case _ => None
}

def isConst(tp : Type) : Option[Type] = tp.fixForEvaluation match {
case ConstantType(_) => Some(ConstantType(Constant(true)))
case _ => Some(ConstantType(Constant(false)))
val opsSet = Set(
defn.CompiletimeOpsAnyModuleClass,
defn.CompiletimeOpsIntModuleClass,
defn.CompiletimeOpsLongModuleClass,
defn.CompiletimeOpsFloatModuleClass,
defn.CompiletimeOpsBooleanModuleClass,
defn.CompiletimeOpsStringModuleClass
)

//Returns Some(true) if the type is a constant.
//Returns Some(false) if the type is not a constant.
//Returns None if there is not enough information to determine if the type is a constant.
//The type is a constant if it is a constant type or a type operation composition of constant types.
//If we get a type reference for an argument, then the result is not yet known.
def isConst(tp : Type) : Option[Boolean] = tp.dealias match {
//known to be constant
case ConstantType(_) => Some(true)
//currently not a concrete known type
case TypeRef(NoPrefix,_) => None
//currently not a concrete known type
case _ : TypeParamRef => None
//constant if the term is constant
case t : TermRef => isConst(t.underlying)
//an operation type => recursively check all argument compositions
case applied : AppliedType if opsSet.contains(applied.typeSymbol.owner) =>
val argsConst = applied.args.map(isConst)
if (argsConst.exists(_.isEmpty)) None
else Some(argsConst.forall(_.get))
//all other types are considered not to be constant
case _ => Some(false)
}

def expectArgsNum(expectedNum : Int) : Unit =
Expand Down Expand Up @@ -4300,7 +4327,7 @@ object Types {
case tpnme.Equals => constantFold2(constValue, _ == _)
case tpnme.NotEquals => constantFold2(constValue, _ != _)
case tpnme.ToString => constantFold1(constValue, _.toString)
case tpnme.IsConst => isConst(args.head)
case tpnme.IsConst => isConst(args.head).map(b => ConstantType(Constant(b)))
case _ => None
} else if (owner == defn.CompiletimeOpsIntModuleClass) name match {
case tpnme.Abs => constantFold1(intValue, _.abs)
Expand Down
9 changes: 9 additions & 0 deletions library/src/scala/compiletime/ops/any.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ object any:
* val c1: IsConst[1] = true
* val c2: IsConst["hi"] = true
* val c3: IsConst[false] = true
* val c4: IsConst[Any] = false
* ```
* If the type is not yet known, then `IsConst` remains unevaluated, and
* will be evaluated only at its concrete type application. E.g.:
* ```scala
* //def `isConst`` returns the type `IsConst[X]`, since `X` is not yet known.
* def isConst[X] : IsConst[X] = ???
* val c5 : true = isConst[1] //now the type is known to be a constant
* val c6 : false = isConst[Any] //now the type is known to be not a constant
* ```
* @syntax markdown
*/
Expand Down
8 changes: 7 additions & 1 deletion tests/neg/singleton-ops-any.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,11 @@ object Test {
final val two = 2
val t47 : IsConst[two.type] = true
val t48: IsConst[Any] = true // error

def isConst[X] : IsConst[X] = ???
val t49 : true = isConst[1]
val t50 : false = isConst[one.type]
def isConst2[X <: Int, Y <: Int] : IsConst[X == Y] = ???
val t51 : true = isConst2[1, 1]
val t52 : false = isConst2[1, one.type]
val t53 : true = isConst2[1, two.type]
}

0 comments on commit e8d00f8

Please sign in to comment.