Skip to content

Commit e90dc31

Browse files
authored
Add missing span to synthesized product mirror (#18354)
Fixes #18353
2 parents e2b64e9 + ea5d7d3 commit e90dc31

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
431431
if cls.useCompanionAsProductMirror then companionPath(mirroredType, span)
432432
else if defn.isTupleClass(cls) then newTupleMirror(typeElems.size) // TODO: cls == defn.PairClass when > 22
433433
else anonymousMirror(monoType, MirrorImpl.OfProduct(pre), span)
434-
withNoErrors(mirrorRef.cast(mirrorType))
434+
withNoErrors(mirrorRef.cast(mirrorType).withSpan(span))
435435
end makeProductMirror
436436

437437
MirrorSource.reduce(mirroredType) match
@@ -444,12 +444,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
444444
mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, singleton.name)
445445
}
446446
val mirrorRef = New(defn.Mirror_SingletonProxyClass.typeRef, singletonPath :: Nil)
447-
withNoErrors(mirrorRef.cast(mirrorType))
447+
withNoErrors(mirrorRef.cast(mirrorType).withSpan(span))
448448
else
449449
val mirrorType = formal.constrained_& {
450450
mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, singleton.name)
451451
}
452-
withNoErrors(singletonPath.cast(mirrorType))
452+
withNoErrors(singletonPath.cast(mirrorType).withSpan(span))
453453
case MirrorSource.GenericTuple(tps) =>
454454
val maxArity = Definitions.MaxTupleArity
455455
val arity = tps.size

tests/pos-macros/i18353/Macro_1.scala

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import scala.compiletime.*
2+
import scala.deriving.*
3+
import scala.quoted.*
4+
5+
trait Getter[S, A]:
6+
def view: S => A
7+
8+
trait Lens[S, A] extends Getter[S, A]:
9+
def set: S => A => S
10+
11+
object Lens {
12+
inline def apply[S, A](_view: S => A)(_set: S => A => S): Lens[S, A] =
13+
new Lens[S, A]:
14+
def view: S => A = _view
15+
def set: S => A => S = _set
16+
17+
inline given derived[T <: Product, A]: Lens[T, A] = ${
18+
ProductMacros.genLens[T, A]
19+
}
20+
}
21+
22+
object ProductMacros {
23+
private def indexOf[T: Type, A: Type](using Quotes): Int =
24+
indexOf0[T, A](0)
25+
26+
private def indexOf0[T: Type, A: Type](acc: Int)(using Quotes): Int =
27+
Type.of[T] match
28+
case '[EmptyTuple] => -1
29+
case '[A *: tpes] => acc
30+
case '[tpe *: tpes] => indexOf0[tpes, A](acc + 1)
31+
32+
def genLens[T <: Product: Type, A: Type](using
33+
q: Quotes
34+
): Expr[Lens[T, A]] = {
35+
import quotes.reflect.*
36+
37+
Expr
38+
.summon[Mirror.ProductOf[T]]
39+
.map {
40+
case '{
41+
$m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }
42+
} =>
43+
val i = indexOf[elementTypes, A]
44+
if i < 0 then
45+
report.errorAndAbort(s"has no the field of ${Type.show[A]}")
46+
else
47+
val ii: Expr[Int] = Expr(i)
48+
val view: Expr[T => A] = '{ t =>
49+
t.productElement($ii).asInstanceOf[A]
50+
}
51+
val set: Expr[T => A => T] = '{ t => a =>
52+
val arr = Tuple.fromProduct(t).toArray
53+
arr($ii) = a.asInstanceOf[Object]
54+
// Check-macros fails here probably
55+
$m.fromTuple(Tuple.fromArray(arr).asInstanceOf[elementTypes])
56+
}
57+
'{ Lens[T, A]($view)($set) }
58+
}
59+
.getOrElse(
60+
report.errorAndAbort(s"${Type.show[T]} is not a product type")
61+
)
62+
63+
}
64+
}

tests/pos-macros/i18353/Test_2.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def Test = {
2+
3+
type TupleConfig = (Int, String)
4+
5+
val tConfig = (1, "string")
6+
val fails = summon[Lens[TupleConfig, Int]].view(tConfig)
7+
}

0 commit comments

Comments
 (0)