From 9c3c33242dde493d28038e8c1fdb64779b35f498 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Sat, 11 Apr 2026 11:10:16 +0000 Subject: [PATCH] Use wildcard type instead of whitebox cast in IsoFields Replace the `whitebox` cast (`asInstanceOf[Expr[Iso[S, Tuple]]]`) with a proper wildcard return type using `PIso[S, S, ? <: Tuple, ? <: Tuple]`. Since `IsoFields.apply` is a `transparent inline` method, the compiler still infers the precise tuple type at call sites. This avoids the unsafe cast and prepares for potential stricter macro type checking in future Scala versions. The motivation is to prepare for future stricter checks in the Scala 3 compiler. The current implementation exploits a missing check to generate an unsound cast. Note: we use `PIso` (a trait) directly instead of the `Iso` type alias (defined as `type Iso[S, A] = PIso[S, S, A, A]`) because Scala 3 cannot reduce higher-kinded type aliases applied to wildcard arguments. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../main/scala-3/monocle/internal/IsoFields.scala | 15 +++++---------- .../main/scala-3/monocle/syntax/MacroSyntax.scala | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/core/shared/src/main/scala-3/monocle/internal/IsoFields.scala b/core/shared/src/main/scala-3/monocle/internal/IsoFields.scala index 170e51622..baab33697 100644 --- a/core/shared/src/main/scala-3/monocle/internal/IsoFields.scala +++ b/core/shared/src/main/scala-3/monocle/internal/IsoFields.scala @@ -1,28 +1,23 @@ package monocle.internal -import monocle.Iso +import monocle.{Iso, PIso} import scala.quoted.{quotes, Expr, Quotes, Type} import scala.deriving.Mirror object IsoFields { - transparent inline def apply[S <: Product](using mirror: Mirror.ProductOf[S]): Iso[S, Tuple] = + transparent inline def apply[S <: Product](using mirror: Mirror.ProductOf[S]): PIso[S, S, ? <: Tuple, ? <: Tuple] = ${ IsoFieldsImpl.apply[S]('mirror) } } private[monocle] object IsoFieldsImpl { - def apply[S <: Product](mirror: Expr[Mirror.ProductOf[S]])(using Quotes, Type[S]): Expr[Iso[S, Tuple]] = { - - def whitebox[A <: Tuple](e: Expr[Iso[S, A]]): Expr[Iso[S, Tuple]] = - e.asInstanceOf[Expr[Iso[S, Tuple]]] - + def apply[S <: Product](mirror: Expr[Mirror.ProductOf[S]])(using Quotes, Type[S]): Expr[PIso[S, S, ? <: Tuple, ? <: Tuple]] = mirror match { case '{ type a <: Tuple; $m: Mirror.ProductOf[S] { type MirroredElemTypes = `a` } } => - whitebox('{ + '{ val f: S => a = Tuple.fromProductTyped(_)(using $m) val g: a => S = $m.fromProduct(_) Iso[S, a](f)(g) - }) + } } - } } diff --git a/core/shared/src/main/scala-3/monocle/syntax/MacroSyntax.scala b/core/shared/src/main/scala-3/monocle/syntax/MacroSyntax.scala index e4a8492d7..b44a819e9 100644 --- a/core/shared/src/main/scala-3/monocle/syntax/MacroSyntax.scala +++ b/core/shared/src/main/scala-3/monocle/syntax/MacroSyntax.scala @@ -13,7 +13,7 @@ trait MacroSyntax { * Case classes with 0 fields will correspond with `EmptyTuple`, 1 with `Tuple1[field type]`, 2 or more with a * tuple of all field types in the same order as the fields themselves. */ - transparent inline def fields[S <: Product: Mirror.ProductOf]: Iso[S, Tuple] = + transparent inline def fields[S <: Product: Mirror.ProductOf]: PIso[S, S, ? <: Tuple, ? <: Tuple] = IsoFields[S] }