Skip to content

Internal error in extracting SemanticDB while parsing dependent function type with inner opaque type. #17607

Open
@markehammons

Description

@markehammons

Compiler version

3.3.0

Minimized code

trait Inst:
  opaque type InstrumentedFn[A] = A

class Opt2[F](
  instrumentation: Inst
)(
  f: (i: Inst) => i.InstrumentedFn[F]
)

Output

[warn] Internal error in extracting SemanticDB while compiling /home/mhammons/Projects/slinc/core/src/fr/hammons/slinc/jitc/Opt2.scala: Ignoring i of symbol val f, type MethodType(List(i), List(TypeRef(ThisType(TypeRef(NoPrefix,module class jitc)),trait Inst)), AppliedType(TypeRef(TermParamRef(i),type InstrumentedFn),List(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class jitc)),class Opt2)),type F))))

Expectation

This should hopefully not cause problems for Scala. The use case for this pattern, in my case, is that the Inst type is attesting that the resulting type F is a function that has been instrumented. The full code I'm using is more like this:

trait Instrumentation:
  def getCount(): Int
  protected def toInstrumented[A](a: A): Instrumented[A] = a
  opaque type Instrumented[A] = A
  opaque type InstrumentedFn[A] <: A = A

  def instrument[A](a: A): Instrumented[A]

  def apply[A, B <: Tuple, C, D, E](fn: A)(using
      Fn[A, B, C],
      C =:= Instrumented[D],
      Fn[E, B, D]
  ): InstrumentedFn[E] =
    fn.asInstanceOf[E]

class CountbasedInstrumentation extends Instrumentation:
  private val count = AtomicInteger(0)
  def getCount() = count.getAcquire()
  private def incrementCount(): Int =
    var succeeded = false
    var res = 0
    while !succeeded do
      res = count.getOpaque()
      succeeded = count.compareAndSet(res, res + 1)

    res + 1

  def instrument[A](a: A): Instrumented[A] =
    incrementCount()
    toInstrumented(a)

object IgnoreInstrumentation extends Instrumentation:
  def getCount() = 0
  def instrument[A](a: A): Instrumented[A] = toInstrumented(a)

class OptimizableFn[F](
    optimizer: JitCService,
    inst: Instrumentation = new CountbasedInstrumentation
)(
    f: (i: Instrumentation) => i.InstrumentedFn[F],
    limit: Int
)(optimized: JitCompiler => F):
  private val _fn: AtomicReference[F] = AtomicReference(f(inst))
  val uuid = UUID.randomUUID().nn
  println(s"created fn $uuid")
  private val _optFn: AtomicReference[F] = AtomicReference(
    if inst.getCount() >= limit then
      var opt: F | Null = null
      optimizer.jitC(uuid, jitCompiler => opt = optimized(jitCompiler))
      opt
    else null
  )

  def get: F =
    val optFn = _optFn.getOpaque()
    if optFn != null then optFn
    else
      if inst.getCount() >= limit then
        optimizer.jitC(
          uuid,
          jitCompiler =>
            val opt = optimized(jitCompiler)
            _fn.setOpaque(
              opt
            )
        )
      _fn.getOpaque().nn

object OptimizableFn:
  val modeSetting = "slinc.jitc.mode"
  val limitSetting = "slinc.jitc.jit-limit"
  def apply[F](optimized: JitCompiler => F)(
      unoptimizedFn: (i: Instrumentation) => i.InstrumentedFn[F]
  ) =
    val mode = sys.props.getOrElseUpdate("slinc.jitc.mode", "standard")
    mode match
      case "standard" =>
        val limit =
          sys.props.getOrElseUpdate("slinc.jitc.jit-limit", "10000").toIntOption
        limit match
          case None => throw Error("slinc.jitc.jit-limit should be an integer")
          case Some(value) =>
            new OptimizableFn[F](
              JitCService.standard,
              CountbasedInstrumentation()
            )(unoptimizedFn, value)(optimized)

      case "never" | "disabled" =>
        new OptimizableFn[F](JitCService.synchronous, IgnoreInstrumentation)(
          unoptimizedFn,
          1
        )(optimized)

      case "immediate" =>
        new OptimizableFn[F](JitCService.synchronous, IgnoreInstrumentation)(
          unoptimizedFn,
          0
        )(optimized)

  def standard[F](
      optimized: JitCompiler => F
  )(unoptimizedFn: (i: Instrumentation) => i.InstrumentedFn[F], limit: Int) =
    new OptimizableFn[F](JitCService.standard)(unoptimizedFn, limit)(optimized)

Where Fn is an evidence type that provides info like:

given [A,Z]: Fn[A => Z, Tuple1[A], Z] with {}

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