Skip to content

Missing ability to check whether inline argument is constant #11780

Closed
@japgolly

Description

@japgolly

I'd be happy to contribute a PR to fix if necessary.

The inline doc says

when called with a constant exponent n, the following method for power will be implemented by straight inline code without any loop or recursion.

and then goes on to show this example:

inline def power(x: Double, n: Int): Double =
   if n == 0 then 1.0
   else if n == 1 then x
   else
      val y = power(x, n / 2)
      if n % 2 == 0 then y * y else y * y * x

power(expr, 10)
// translates to
//
//    val x = expr
//    val y1 = x * x   // ^2
//    val y2 = y1 * y1 // ^4
//    val y3 = y2 * x  // ^5
//    y3 * y3          // ^10

So if I understand correctly,

  1. n == 0 and n == 1 occur at compile-time and only match constants. def p(n: Int) = power(expr,n); p(10) wouldn't translate to the example translation above because n would be a dynamic reference rather than a constant.
  2. scalac allows comparisons of arguments to constants like 1 and 0 because they're constants in the inline function body

All well and good but what if one wants to simply know whether n is a constant or not (regardless of the value)?

In my case I was trying to determine whether a String argument is constant or not.
This doesn't work:

  inline given blah(inline body: String): Name =
    inline match n
      case _: Singleton => Name.now(body)
      case _            => Name.lazily(body)

The methods in scala.compiletime don't seem to help because they're more about types.

My own workaround was to fallback to the quoting API like this:

  inline given blah(inline body: String): Name =
    ${ _blah('body) }

  private def _blah(expr: Expr[String])(using Quotes): Expr[Name] = {
    import quotes.reflect.*
    expr.asTerm match {
      case Inlined(_, _, Literal(StringConstant(s))) => '{ Name.now($expr) }
      case _                                         => '{ Name.lazily($expr) }
    }
  }

It'd be nice to be able to make this work without falling back to quotes. This ability doesn't seem to exist yet, nor does it seem hard to write. I'd be happy to write a PR to add this. Maybe a bunch of methods like inline def stringConstantOpt(inline s: String): Option[String] for each constant type?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions