diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 45606b0dbef5..269539ca546c 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -498,53 +498,64 @@ class SyntheticMembers(thisPhase: DenotTransformer) { /** The class * * ``` - * case class C[T <: U](x: T, y: String*) + * trait U: + * type Elem + * + * case class C[T <: U](a: T, b: a.Elem, c: String*) * ``` * * gets the `fromProduct` method: * * ``` * def fromProduct(x$0: Product): MirroredMonoType = - * new C[U]( - * x$0.productElement(0).asInstanceOf[U], - * x$0.productElement(1).asInstanceOf[Seq[String]]: _*) + * val a$1 = x$0.productElement(0).asInstanceOf[U] + * val b$1 = x$0.productElement(1).asInstanceOf[a$1.Elem] + * val c$1 = x$0.productElement(2).asInstanceOf[Seq[String]] + * new C[U](a$1, b$1, c$1*) * ``` * where * ``` * type MirroredMonoType = C[?] * ``` */ - def fromProductBody(caseClass: Symbol, param: Tree, optInfo: Option[MirrorImpl.OfProduct])(using Context): Tree = - def extractParams(tpe: Type): List[Type] = - tpe.asInstanceOf[MethodType].paramInfos - - def computeFromCaseClass: (Type, List[Type]) = - val (baseRef, baseInfo) = - val rawRef = caseClass.typeRef - val rawInfo = caseClass.primaryConstructor.info - optInfo match - case Some(info) => - (rawRef.asSeenFrom(info.pre, caseClass.owner), rawInfo.asSeenFrom(info.pre, caseClass.owner)) - case _ => - (rawRef, rawInfo) - baseInfo match + def fromProductBody(caseClass: Symbol, productParam: Tree, optInfo: Option[MirrorImpl.OfProduct])(using Context): Tree = + val classRef = optInfo match + case Some(info) => TypeRef(info.pre, caseClass) + case _ => caseClass.typeRef + val (newPrefix, constrMeth) = + val constr = TermRef(classRef, caseClass.primaryConstructor) + (constr.info: @unchecked) match case tl: PolyType => val tvars = constrained(tl) val targs = for tvar <- tvars yield tvar.instantiate(fromBelow = false) - (baseRef.appliedTo(targs), extractParams(tl.instantiate(targs))) - case methTpe => - (baseRef, extractParams(methTpe)) - end computeFromCaseClass - - val (classRefApplied, paramInfos) = computeFromCaseClass - val elems = - for ((formal, idx) <- paramInfos.zipWithIndex) yield - val elem = - param.select(defn.Product_productElement).appliedTo(Literal(Constant(idx))) - .ensureConforms(formal.translateFromRepeated(toArray = false)) - if (formal.isRepeatedParam) ctx.typer.seqToRepeated(elem) else elem - New(classRefApplied, elems) + (AppliedType(classRef, targs), tl.instantiate(targs).asInstanceOf[MethodType]) + case mt: MethodType => + (classRef, mt) + + // Create symbols for the vals corresponding to each parameter + // If there are dependent parameters, the infos won't be correct yet. + val bindingSyms = constrMeth.paramRefs.map: pref => + newSymbol(ctx.owner, pref.paramName.freshened, Synthetic, + pref.underlying.translateFromRepeated(toArray = false), coord = caseClass.coord) + val bindingRefs = bindingSyms.map(TermRef(NoPrefix, _)) + // Fix the info for dependent parameters + if constrMeth.isParamDependent then + bindingSyms.foreach: bindingSym => + bindingSym.info = bindingSym.info.substParams(constrMeth, bindingRefs) + + val bindingDefs = bindingSyms.zipWithIndex.map: (bindingSym, idx) => + ValDef(bindingSym, + productParam.select(defn.Product_productElement).appliedTo(Literal(Constant(idx))) + .ensureConforms(bindingSym.info)) + + val newArgs = bindingRefs.lazyZip(constrMeth.paramInfos).map: (bindingRef, paramInfo) => + val refTree = ref(bindingRef) + if paramInfo.isRepeatedParam then ctx.typer.seqToRepeated(refTree) else refTree + Block( + bindingDefs, + New(newPrefix, newArgs) + ) end fromProductBody /** For an enum T: diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6167db62fbe0..a02b9c0043f1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1925,9 +1925,7 @@ class Namer { typer: Typer => if isConstructor then // set result type tree to unit, but take the current class as result type of the symbol typedAheadType(ddef.tpt, defn.UnitType) - val mt = wrapMethType(effectiveResultType(sym, paramSymss)) - if sym.isPrimaryConstructor then checkCaseClassParamDependencies(mt, sym.owner) - mt + wrapMethType(effectiveResultType(sym, paramSymss)) else if sym.isAllOf(Given | Method) && Feature.enabled(modularity) then // set every context bound evidence parameter of a given companion method // to be tracked, provided it has a type that has an abstract type member. @@ -1976,16 +1974,6 @@ class Namer { typer: Typer => ddef.trailingParamss.foreach(completeParams) end completeTrailingParamss - /** Checks an implementation restriction on case classes. */ - def checkCaseClassParamDependencies(mt: Type, cls: Symbol)(using Context): Unit = - mt.stripPoly match - case mt: MethodType if cls.is(Case) && mt.isParamDependent => - // See issue #8073 for background - report.error( - em"""Implementation restriction: case classes cannot have dependencies between parameters""", - cls.srcPos) - case _ => - /** Under x.modularity, we add `tracked` to context bound witnesses * that have abstract type members */ diff --git a/tests/run/i8073.scala b/tests/run/i8073.scala new file mode 100644 index 000000000000..6b5bfc3b9832 --- /dev/null +++ b/tests/run/i8073.scala @@ -0,0 +1,86 @@ +import scala.deriving.Mirror + +trait A: + type B + +object Test: + case class CC(a: A, b: a.B) + + def test1(): Unit = + val generic = summon[Mirror.Of[CC]] + // No language syntax for type projection of a singleton type + // summon[generic.MirroredElemTypes =:= (A, CC#a.B)] + + val aa: A { type B = Int } = new A { type B = Int } + val x: CC { val a: aa.type } = CC(aa, 1).asInstanceOf[CC { val a: aa.type }] // manual `tracked` + + val dependent = summon[Mirror.Of[x.type]] + summon[dependent.MirroredElemTypes =:= (A, x.a.B)] + + assert(CC(aa, 1) == generic.fromProduct((aa, 1))) + assert(CC(aa, 1) == dependent.fromProduct((aa, 1))) + + x match + case CC(a, b) => + val a1: A = a + // Dependent pattern matching is not currently supported + // val b1: a1.B = b + val b1 = b // Type is CC#a.B + + end test1 + + case class CCPoly[T <: A](a: T, b: a.B) + + def test2(): Unit = + val generic = summon[Mirror.Of[CCPoly[A]]] + // No language syntax for type projection of a singleton type + // summon[generic.MirroredElemTypes =:= (A, CCPoly[A]#a.B)] + + val aa: A { type B = Int } = new A { type B = Int } + val x: CCPoly[aa.type] = CCPoly(aa, 1) + + val dependent = summon[Mirror.Of[x.type]] + summon[dependent.MirroredElemTypes =:= (aa.type, x.a.B)] + + assert(CCPoly[A](aa, 1) == generic.fromProduct((aa, 1))) + assert(CCPoly[A](aa, 1) == dependent.fromProduct((aa, 1))) + + x match + case CCPoly(a, b) => + val a1: A = a + // Dependent pattern matching is not currently supported + // val b1: a1.B = b + val b1 = b // Type is CC#a.B + + end test2 + + enum Enum: + case EC(a: A, b: a.B) + + def test3(): Unit = + val generic = summon[Mirror.Of[Enum.EC]] + // No language syntax for type projection of a singleton type + // summon[generic.MirroredElemTypes =:= (A, Enum.EC#a.B)] + + val aa: A { type B = Int } = new A { type B = Int } + val x: Enum.EC { val a: aa.type } = Enum.EC(aa, 1).asInstanceOf[Enum.EC { val a: aa.type }] // manual `tracked` + + val dependent = summon[Mirror.Of[x.type]] + summon[dependent.MirroredElemTypes =:= (A, x.a.B)] + + assert(Enum.EC(aa, 1) == generic.fromProduct((aa, 1))) + assert(Enum.EC(aa, 1) == dependent.fromProduct((aa, 1))) + + x match + case Enum.EC(a, b) => + val a1: A = a + // Dependent pattern matching is not currently supported + // val b1: a1.B = b + val b1 = b // Type is Enum.EC#a.B + + end test3 + + def main(args: Array[String]): Unit = + test1() + test2() + test3() diff --git a/tests/run/i8073b.scala b/tests/run/i8073b.scala new file mode 100644 index 000000000000..cc85731d01df --- /dev/null +++ b/tests/run/i8073b.scala @@ -0,0 +1,86 @@ +import scala.deriving.Mirror + +trait A: + type B + +// Test local mirrors +@main def Test = + case class CC(a: A, b: a.B) + + def test1(): Unit = + val generic = summon[Mirror.Of[CC]] + // No language syntax for type projection of a singleton type + // summon[generic.MirroredElemTypes =:= (A, CC#a.B)] + + val aa: A { type B = Int } = new A { type B = Int } + val x: CC { val a: aa.type } = CC(aa, 1).asInstanceOf[CC { val a: aa.type }] // manual `tracked` + + val dependent = summon[Mirror.Of[x.type]] + summon[dependent.MirroredElemTypes =:= (A, x.a.B)] + + assert(CC(aa, 1) == generic.fromProduct((aa, 1))) + assert(CC(aa, 1) == dependent.fromProduct((aa, 1))) + + x match + case CC(a, b) => + val a1: A = a + // Dependent pattern matching is not currently supported + // val b1: a1.B = b + val b1 = b // Type is CC#a.B + + end test1 + + case class CCPoly[T <: A](a: T, b: a.B) + + def test2(): Unit = + val generic = summon[Mirror.Of[CCPoly[A]]] + // No language syntax for type projection of a singleton type + // summon[generic.MirroredElemTypes =:= (A, CCPoly[A]#a.B)] + + val aa: A { type B = Int } = new A { type B = Int } + val x: CCPoly[aa.type] = CCPoly(aa, 1) + + val dependent = summon[Mirror.Of[x.type]] + summon[dependent.MirroredElemTypes =:= (aa.type, x.a.B)] + + assert(CCPoly[A](aa, 1) == generic.fromProduct((aa, 1))) + assert(CCPoly[A](aa, 1) == dependent.fromProduct((aa, 1))) + + x match + case CCPoly(a, b) => + val a1: A = a + // Dependent pattern matching is not currently supported + // val b1: a1.B = b + val b1 = b // Type is CC#a.B + + end test2 + + enum Enum: + case EC(a: A, b: a.B) + + def test3(): Unit = + val generic = summon[Mirror.Of[Enum.EC]] + // No language syntax for type projection of a singleton type + // summon[generic.MirroredElemTypes =:= (A, Enum.EC#a.B)] + + val aa: A { type B = Int } = new A { type B = Int } + val x: Enum.EC { val a: aa.type } = Enum.EC(aa, 1).asInstanceOf[Enum.EC { val a: aa.type }] // manual `tracked` + + val dependent = summon[Mirror.Of[x.type]] + summon[dependent.MirroredElemTypes =:= (A, x.a.B)] + + assert(Enum.EC(aa, 1) == generic.fromProduct((aa, 1))) + assert(Enum.EC(aa, 1) == dependent.fromProduct((aa, 1))) + + x match + case Enum.EC(a, b) => + val a1: A = a + // Dependent pattern matching is not currently supported + // val b1: a1.B = b + val b1 = b // Type is Enum.EC#a.B + + end test3 + + test1() + test2() + test3()