diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 6a5145ffd202..451b567dc771 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -829,4 +829,11 @@ object TypeOps: def nestedPairs(ts: List[Type])(using Context): Type = ts.foldRight(defn.EmptyTupleModule.termRef: Type)(defn.PairClass.typeRef.appliedTo(_, _)) + class StripTypeVarsMap(using Context) extends TypeMap: + def apply(tp: Type) = mapOver(tp).stripTypeVar + + /** Apply [[Type.stripTypeVar]] recursively. */ + def stripTypeVars(tp: Type)(using Context): Type = + new StripTypeVarsMap().apply(tp) + end TypeOps diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 44351b9a213c..31694c23a8e0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -249,21 +249,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val cls = mirroredType.classSymbol val accessors = cls.caseAccessors.filterNot(_.isAllOf(PrivateLocal)) val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString))) + val nestedPairs = TypeOps.nestedPairs(accessors.map(mirroredType.resultType.memberInfo(_).widenExpr)) val (monoType, elemsType) = mirroredType match case mirroredType: HKTypeLambda => - def accessorType(acc: Symbol) = - if cls.typeParams.hasSameLengthAs(mirroredType.paramRefs) then - acc.info.subst(cls.typeParams, mirroredType.paramRefs) - else - acc.info - val elems = - mirroredType.derivedLambdaType( - resType = TypeOps.nestedPairs(accessors.map(accessorType)) - ) - (mkMirroredMonoType(mirroredType), elems) + (mkMirroredMonoType(mirroredType), mirroredType.derivedLambdaType(resType = nestedPairs)) case _ => - val elems = TypeOps.nestedPairs(accessors.map(mirroredType.memberInfo(_).widenExpr)) - (mirroredType, elems) + (mirroredType, nestedPairs) val elemsLabels = TypeOps.nestedPairs(elemLabels) checkRefinement(formal, tpnme.MirroredElemTypes, elemsType, span) checkRefinement(formal, tpnme.MirroredElemLabels, elemsLabels, span) @@ -344,7 +335,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): (using Context): Tree = if checkFormal(formal) then formal.member(tpnme.MirroredType).info match - case TypeBounds(mirroredType, _) => synth(mirroredType.stripTypeVar, formal, span) + case TypeBounds(mirroredType, _) => synth(TypeOps.stripTypeVars(mirroredType), formal, span) case other => EmptyTree else EmptyTree diff --git a/tests/pos/i13859.scala b/tests/pos/i13859.scala new file mode 100644 index 000000000000..4092de52fd94 --- /dev/null +++ b/tests/pos/i13859.scala @@ -0,0 +1,31 @@ +import scala.deriving.* + +object Test: + type Kind1[C, O[_]] = C { + type MirroredType[X] = O[X] + type MirroredMonoType = O[Any] + type MirroredElemTypes[_] <: Tuple + } + + type Kind2[C, O[_, _]] = C { + type MirroredType[X, Y] = O[X, Y] + type MirroredMonoType = O[Any, Any] + type MirroredElemTypes[_, _] <: Tuple + } + + type Test[X] = (X, Boolean) + type Swap[X, Y] = (Y, X) + + locally { + val x = summon[Kind1[Mirror.Product, Test]] + x: Mirror.Product { + type MirroredElemTypes[X] = (X, Boolean) + } + } + + locally { + val x = summon[Kind2[Mirror.Product, Swap]] + x: Mirror.Product { + type MirroredElemTypes[X, Y] = (Y, X) + } + }