Skip to content

Commit

Permalink
Add missing span to synthesized product mirror
Browse files Browse the repository at this point in the history
Fixes #18353

[Cherry-picked ea5d7d3]
  • Loading branch information
nicolasstucki authored and WojciechMazur committed Jun 19, 2024
1 parent a5060ab commit 1a50455
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
if cls.useCompanionAsProductMirror then companionPath(mirroredType, span)
else if defn.isTupleClass(cls) then newTupleMirror(typeElems.size) // TODO: cls == defn.PairClass when > 22
else anonymousMirror(monoType, MirrorImpl.OfProduct(pre), span)
withNoErrors(mirrorRef.cast(mirrorType))
withNoErrors(mirrorRef.cast(mirrorType).withSpan(span))
end makeProductMirror

MirrorSource.reduce(mirroredType) match
Expand All @@ -442,12 +442,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, singleton.name)
}
val mirrorRef = New(defn.Mirror_SingletonProxyClass.typeRef, singletonPath :: Nil)
withNoErrors(mirrorRef.cast(mirrorType))
withNoErrors(mirrorRef.cast(mirrorType).withSpan(span))
else
val mirrorType = formal.constrained_& {
mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, singleton.name)
}
withNoErrors(singletonPath.cast(mirrorType))
withNoErrors(singletonPath.cast(mirrorType).withSpan(span))
case MirrorSource.GenericTuple(tps) =>
val maxArity = Definitions.MaxTupleArity
val arity = tps.size
Expand Down
64 changes: 64 additions & 0 deletions tests/pos-macros/i18353/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import scala.compiletime.*
import scala.deriving.*
import scala.quoted.*

trait Getter[S, A]:
def view: S => A

trait Lens[S, A] extends Getter[S, A]:
def set: S => A => S

object Lens {
inline def apply[S, A](_view: S => A)(_set: S => A => S): Lens[S, A] =
new Lens[S, A]:
def view: S => A = _view
def set: S => A => S = _set

inline given derived[T <: Product, A]: Lens[T, A] = ${
ProductMacros.genLens[T, A]
}
}

object ProductMacros {
private def indexOf[T: Type, A: Type](using Quotes): Int =
indexOf0[T, A](0)

private def indexOf0[T: Type, A: Type](acc: Int)(using Quotes): Int =
Type.of[T] match
case '[EmptyTuple] => -1
case '[A *: tpes] => acc
case '[tpe *: tpes] => indexOf0[tpes, A](acc + 1)

def genLens[T <: Product: Type, A: Type](using
q: Quotes
): Expr[Lens[T, A]] = {
import quotes.reflect.*

Expr
.summon[Mirror.ProductOf[T]]
.map {
case '{
$m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }
} =>
val i = indexOf[elementTypes, A]
if i < 0 then
report.errorAndAbort(s"has no the field of ${Type.show[A]}")
else
val ii: Expr[Int] = Expr(i)
val view: Expr[T => A] = '{ t =>
t.productElement($ii).asInstanceOf[A]
}
val set: Expr[T => A => T] = '{ t => a =>
val arr = Tuple.fromProduct(t).toArray
arr($ii) = a.asInstanceOf[Object]
// Check-macros fails here probably
$m.fromTuple(Tuple.fromArray(arr).asInstanceOf[elementTypes])
}
'{ Lens[T, A]($view)($set) }
}
.getOrElse(
report.errorAndAbort(s"${Type.show[T]} is not a product type")
)

}
}
7 changes: 7 additions & 0 deletions tests/pos-macros/i18353/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
def Test = {

type TupleConfig = (Int, String)

val tConfig = (1, "string")
val fails = summon[Lens[TupleConfig, Int]].view(tConfig)
}

0 comments on commit 1a50455

Please sign in to comment.