Skip to content

False positive unused given for circe / iron combination #24034

@WojciechMazur

Description

@WojciechMazur

Based on OpenCB failures in funktionalio/pillars and gvolpe/trading
Build logs: funktionalio/pillars / gvolpe/trading

Present in circe and iron 2.x combination. Problem not appears when using iron 3.x

Compiler version

Since 3.7.0
Probably related to #20894

Minimized code

// circe.scala 

package io.circe

import scala.compiletime.*
import scala.deriving.Mirror
import scala.quoted.*

trait Encoder[A]:
  def encode(value: A): String

object Encoder:
  trait AsObject[A] extends Encoder[A]
  given Encoder[String] = ???

trait Configuration
object Configuration:
  val default: Configuration = ???

object Default:
  given [A]: Default[A] = ???
trait Default[T]

trait Codec[A] extends Encoder[A]
object Codec:
  trait AsObject[A] extends Encoder.AsObject[A]
  object AsObject:
    inline final def derived[A](using inline A: Mirror.Of[A]): Codec.AsObject[A] =
      ConfiguredCodec.derived[A](using Configuration.default)
    inline final def derivedConfigured[A](using
        inline A: Mirror.Of[A],
        inline conf: Configuration
    ): Codec.AsObject[A] = ConfiguredCodec.derived[A]

trait ConfiguredEncoder[A](using conf: Configuration) extends Encoder.AsObject[A]
trait ConfiguredCodec[A] extends Codec.AsObject[A], ConfiguredEncoder[A]
object ConfiguredCodec:
  inline final def derive[A: Mirror.Of](): ConfiguredCodec[A] =
    derived[A](using Configuration.default)
  inline final def derived[A](using
      conf: Configuration,
      inline mirror: Mirror.Of[A]
  ): ConfiguredCodec[A] = ${ derivedImpl[A]('conf, 'mirror) }
  def ofProduct[A](
      encoders: => List[Encoder[?]]
  )(using Configuration, Default[A]): ConfiguredCodec[A] = ???
  def derivedImpl[A: Type](conf: Expr[Configuration], mirror: Expr[Mirror.Of[A]])(using
      q: Quotes
  ): Expr[ConfiguredCodec[A]] = {
    mirror match {
      case '{
            ${ _ }: Mirror.ProductOf[A] {
              type MirroredLabel = l
              type MirroredElemLabels = el
              type MirroredElemTypes = et
            }
          } =>
        '{
          ConfiguredCodec.ofProduct[A](
            derivation.summonEncoders[et & Tuple](false)(using $conf)
          )(using $conf)
        }
    }
  }

object derivation:
  sealed trait Inliner[A, Arg]:
    inline def apply[T](inline arg: Arg): A

  class EncoderNotDeriveSum(using config: Configuration) extends Inliner[Encoder[?], Unit]:
    inline def apply[T](inline arg: Unit): Encoder[?] = summonEncoder[T](false)

  inline final def loopUnrolled[A, Arg, T <: Tuple](f: Inliner[A, Arg], inline arg: Arg): List[A] =
    inline erasedValue[T] match
      case _: EmptyTuple => Nil
      case _: (h *: ts)  => f[h](arg) :: loopUnrolled[A, Arg, ts](f, arg)

  inline def loopUnrolledNoArg[A, T <: Tuple](f: Inliner[A, Unit]): List[A] =
    loopUnrolled[A, Unit, T](f, ())

  inline final def summonEncoders[T <: Tuple](inline derivingForSum: Boolean)(using
      Configuration
  ): List[Encoder[?]] =
    loopUnrolledNoArg[Encoder[?], T](
      inline if (derivingForSum) compiletime.error("unreachable")
      else new EncoderNotDeriveSum
    )

  private[circe] inline final def summonEncoder[A](
      inline derivingForSum: Boolean
  )(using Configuration): Encoder[A] = summonFrom {
      case encodeA: Encoder[A] => encodeA
      case _: Mirror.Of[A] =>
        inline if (derivingForSum) compiletime.error("unreachable")
        else error("Failed to find an instance of Encoder[]")
    }
// iron.scala

package iron

import io.circe.*

opaque type IronType[A, C] <: A = A
type :|[A, C] = IronType[A, C]
trait Constraint[A, C]

package constraint:
  object string:
    final class StartWith[V <: String]
    object StartWith:     
      inline given [V <: String]: Constraint[String, StartWith[V]] = ???

object circe:
  inline given [A, B](using inline encoder: Encoder[A]): Encoder[A :| B] = ???
  inline given [A, B](using inline encoder: Encoder[A], dummy: scala.util.NotGiven[DummyImplicit]): Encoder[A :| B] = ???
  // inline given [T](using mirror: RefinedTypeOps.Mirror[T], ev: Encoder[mirror.IronType]): Encoder[T] = ???

// trait RefinedTypeOps[A, C, T]:
//   inline given RefinedTypeOps.Mirror[T] = ???
// object RefinedTypeOps:
//   trait Mirror[T]:
//     type BaseType
//     type ConstraintType
//     type IronType = BaseType :| ConstraintType
//> using options -Wunused:all 

import io.circe.Codec

import iron.:|
import iron.circe.given
import iron.constraint.string.StartWith

case class Alien(name: String :| StartWith["alen"]) derives Codec.AsObject

Output

[warn] ./test.scala:6:19
[warn] unused import
[warn] import iron.circe.given
[warn]                   ^^^^^

When combined with -rewrite required import iron.circe.given is removed leading to compilation failures in next compilations

Expectation

Not reported as unused

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions