diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 2cfdd08128ce..f309ab346610 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -842,6 +842,7 @@ 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 @@ -849,4 +850,91 @@ object TypeOps: def stripTypeVars(tp: Type)(using Context): Type = new StripTypeVarsMap().apply(tp) + /** Converts the type into a form reachable from with the given `prefix` */ + def healPrefix(tpe: Type, prefix: Type)(using Context): Either[String, Type] = + + final class HealPrefixUnwind(val error: String) extends scala.util.control.ControlThrowable + + final class HealPrefixMap(splice: PrefixSplice)(using Context) extends TypeMap { + def apply(tpe: Type): Type = healPrefixImpl(tpe, splice, this)(using mapCtx) + } + + case class PrefixSplice(val prefix: Type): + def spliceThisType(cls: Symbol): Option[Type] = PrefixSplice.loopThisType(cls, prefix) + def isEmpty: Boolean = prefix eq NoPrefix + + object PrefixSplice: + + private def loopThisType(cls: Symbol, pre: Type): Option[Type] = pre match + case outer: ThisType => + if outer.cls.isSubClass(cls) then Some(pre) + else loopThisType(cls, outer.tref.prefix) + + case term: TermRef => + if term.widen.classSymbol eq cls then Some(pre) + else loopThisType(cls, term.prefix) + + case supTpe: SuperType => + if supTpe.thistpe.classSymbol.isSubClass(cls) then Some(pre) + else None + + case _ => None + + end PrefixSplice + + def healPrefixImpl(tpe: Type, splice: PrefixSplice, theMap: HealPrefixMap | Null)(using Context): Type = tpe match { + case tpe: ThisType => + val cls = tpe.cls + splice.spliceThisType(cls) match + case Some(spliced) => + spliced + case _ if cls.is(Module) => + val pre = healPrefixImpl(tpe.tref.prefix, splice, theMap) + TermRef(pre, cls.sourceModule) + case _ if cls.is(Package) || splice.isEmpty => + tpe + case _ => + throw HealPrefixUnwind( + i"prefix $tpe references a non-enclosing class, can not be healed to ${splice.prefix}") + case tpe => + (if (theMap != null) theMap else new HealPrefixMap(splice)) + .mapOver(tpe) + } + + try + Right(healPrefixImpl(tpe, PrefixSplice(prefix), null)) + catch + case unwind: HealPrefixUnwind => Left(unwind.error) + + end healPrefix + + /** lift the prefix of a type */ + def extractPrefix(tpe: Type)(using Context): Type = tpe match + case tpe: TypeRef => + val tryDealias = tpe.dealias + if tryDealias ne tpe then extractPrefix(tryDealias) + else tpe.prefix + case tpe: TermRef => tpe.prefix + case tpe: ThisType => extractPrefix(tpe.tref) + case tpe: SingletonType => NoPrefix + case tpe: TypeProxy => extractPrefix(tpe.underlying) + case tpe: AndOrType => + val p1 = extractPrefix(tpe.tp1) + if p1.exists then + val p2 = extractPrefix(tpe.tp2) + if p2.exists then + if p1.frozen_=:=(p2) then p1 + else + def history(pre: Type, acc: List[Type]): List[Type] = pre match + case tpe: NamedType => history(tpe.prefix, tpe :: acc) + case tpe: ThisType => history(tpe.tref, tpe :: acc) + case tpe => tpe :: acc + val h1 = history(p1, Nil) + val h2 = history(p2, Nil) + val (common, _) = h1.lazyZip(h2).takeWhile((p1, p2) => p1.frozen_=:=(p2)).last + common + else NoType + else NoType + case _ => NoType + end TypeOps diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 32d1440ac3cb..d6b1479e5c42 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -81,14 +81,16 @@ object SymUtils: * Excluded are value classes, abstract classes and case classes with more than one * parameter section. */ - def whyNotGenericProduct(using Context): String = + def whyNotGenericProduct(pre: Type)(using Context): String = if (!self.is(CaseClass)) "it is not a case class" else if (self.is(Abstract)) "it is an abstract class" else if (self.primaryConstructor.info.paramInfoss.length != 1) "it takes more than one parameter list" else if (isDerivedValueClass(self)) "it is a value class" + else if (self.is(Scala2x) && !(pre == NoPrefix || pre.typeSymbol.isStaticOwner)) then + "it is not accessible in a static context" else "" - def isGenericProduct(using Context): Boolean = whyNotGenericProduct.isEmpty + def isGenericProduct(pre: Type)(using Context): Boolean = whyNotGenericProduct(pre: Type).isEmpty /** Is this an old style implicit conversion? * @param directOnly only consider explicitly written methods @@ -145,7 +147,7 @@ object SymUtils: * and also the location of the generated mirror. * - all of its children are generic products, singletons, or generic sums themselves. */ - def whyNotGenericSum(declScope: Symbol)(using Context): String = + def whyNotGenericSum(pre: Type, declScope: Symbol)(using Context): String = if (!self.is(Sealed)) s"it is not a sealed ${self.kindString}" else if (!self.isOneOf(AbstractOrTrait)) @@ -157,17 +159,19 @@ object SymUtils: def problem(child: Symbol) = { def isAccessible(sym: Symbol): Boolean = - (self.isContainedIn(sym) && (companionMirror || declScope.isContainedIn(sym))) + def declRef = if declScope.isTerm then declScope.termRef else declScope.typeRef + self.isContainedIn(sym) && + (companionMirror || sym.isAccessibleFrom(declRef)) || sym.is(Module) && isAccessible(sym.owner) if (child == self) "it has anonymous or inaccessible subclasses" else if (!isAccessible(child.owner)) i"its child $child is not accessible" else if (!child.isClass) "" else { - val s = child.whyNotGenericProduct + val s = child.whyNotGenericProduct(pre) if (s.isEmpty) s else if (child.is(Sealed)) { - val s = child.whyNotGenericSum(if child.useCompanionAsSumMirror then child.linkedClass else ctx.owner) + val s = child.whyNotGenericSum(pre, if child.useCompanionAsSumMirror then child.linkedClass else ctx.owner) if (s.isEmpty) s else i"its child $child is not a generic sum because $s" } else i"its child $child is not a generic product because $s" @@ -177,7 +181,8 @@ object SymUtils: else children.map(problem).find(!_.isEmpty).getOrElse("") } - def isGenericSum(declScope: Symbol)(using Context): Boolean = whyNotGenericSum(declScope).isEmpty + def isGenericSum(pre: Type, declScope: Symbol)(using Context): Boolean = + whyNotGenericSum(pre, declScope).isEmpty /** If this is a constructor, its owner: otherwise this. */ final def skipConstructor(using Context): Symbol = @@ -286,6 +291,9 @@ object SymUtils: def reachableRawTypeRef(using Context) = self.reachableTypeRef.appliedTo(self.typeParams.map(_ => TypeBounds.emptyPolyKind)) + def rawTypeRef(using Context) = + self.typeRef.appliedTo(self.typeParams.map(_ => TypeBounds.emptyPolyKind)) + /** Is symbol a quote operation? */ def isQuote(using Context): Boolean = self == defn.QuotedRuntime_exprQuote || self == defn.QuotedTypeModule_of diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 32256e43d242..c0e197e3edc3 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -15,6 +15,7 @@ import SymUtils._ import util.Property import config.Printers.derive import NullOpsDecorator._ +import dotty.tools.dotc.util.SrcPos object SyntheticMembers { @@ -26,6 +27,8 @@ object SyntheticMembers { /** Attachment recording that an anonymous class should extend Mirror.Sum */ val ExtendsSumMirror: Property.StickyKey[Unit] = new Property.StickyKey + + val AnonymousMirrorPrefix: Property.StickyKey[Type] = new Property.StickyKey } /** Synthetic method implementations for case classes, case objects, @@ -526,12 +529,16 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * a wildcard for each type parameter. The normalized type of an object * O is O.type. */ - def ordinalBody(cls: Symbol, param: Tree)(using Context): Tree = + def ordinalBody(cls: Symbol, param: Tree, pre: Type, pos: SrcPos)(using Context): Tree = if (cls.is(Enum)) param.select(nme.ordinal).ensureApplied else { val cases = for ((child, idx) <- cls.children.zipWithIndex) yield { - val patType = if (child.isTerm) child.reachableTermRef else child.reachableRawTypeRef + val patType0 = if child.isTerm then child.termRef else child.rawTypeRef + def toErr(err: String) = + report.error(err, pos) + ErrorType(err) + val patType: Type = TypeOps.healPrefix(patType0, pre).fold(toErr, identity) val pat = Typed(untpd.Ident(nme.WILDCARD).withType(patType), TypeTree(patType)) CaseDef(pat, EmptyTree, Literal(Constant(idx))) } @@ -568,12 +575,20 @@ class SyntheticMembers(thisPhase: DenotTransformer) { } } val linked = clazz.linkedClass + + lazy val pre = impl.removeAttachment(AnonymousMirrorPrefix).getOrElse(NoPrefix) + lazy val monoType = { val existing = clazz.info.member(tpnme.MirroredMonoType).symbol if (existing.exists && !existing.is(Deferred)) existing else { val monoType = - newSymbol(clazz, tpnme.MirroredMonoType, Synthetic, TypeAlias(linked.reachableRawTypeRef), coord = clazz.coord) + val rawRef = linked.rawTypeRef + def toErr(err: String) = + report.error(err, impl.srcPos) + ErrorType(err) + val monoType = TypeOps.healPrefix(rawRef, pre).fold(toErr, identity) + newSymbol(clazz, tpnme.MirroredMonoType, Synthetic, TypeAlias(monoType), coord = clazz.coord) newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus) monoType.enteredAfter(thisPhase) } @@ -585,25 +600,26 @@ class SyntheticMembers(thisPhase: DenotTransformer) { addMethod(nme.fromProduct, MethodType(defn.ProductClass.typeRef :: Nil, monoType.typeRef), cls, fromProductBody(_, _).ensureConforms(monoType.typeRef)) // t4758.scala or i3381.scala are examples where a cast is needed } - def makeSumMirror(cls: Symbol) = { + def makeSumMirror(cls: Symbol, pre: Type, pos: SrcPos) = { addParent(defn.Mirror_SumClass.typeRef) addMethod(nme.ordinal, MethodType(monoType.typeRef :: Nil, defn.IntType), cls, - ordinalBody(_, _)) + ordinalBody(_, _, pre, pos)) } if (clazz.is(Module)) { if (clazz.is(Case)) makeSingletonMirror() - else if (linked.isGenericProduct) makeProductMirror(linked) - else if (linked.isGenericSum(clazz)) makeSumMirror(linked) - else if (linked.is(Sealed)) - derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum(clazz)}") + else if linked.exists then + if (linked.isGenericProduct(pre)) makeProductMirror(linked) + else if (linked.isGenericSum(pre, clazz)) makeSumMirror(linked, pre, impl.srcPos) + else if (linked.is(Sealed)) + derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum(pre, clazz)}") } else if (impl.removeAttachment(ExtendsSingletonMirror).isDefined) makeSingletonMirror() else if (impl.removeAttachment(ExtendsProductMirror).isDefined) makeProductMirror(monoType.typeRef.dealias.classSymbol) else if (impl.removeAttachment(ExtendsSumMirror).isDefined) - makeSumMirror(monoType.typeRef.dealias.classSymbol) + makeSumMirror(monoType.typeRef.dealias.classSymbol, pre, impl.srcPos) cpy.Template(impl)(parents = newParents, body = newBody) } diff --git a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala index 709635630254..c645f17945fd 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -92,6 +92,11 @@ object TypeUtils { val r2 = tp2.mirrorCompanionRef assert(r1.symbol == r2.symbol, em"mirrorCompanionRef mismatch for $self: $r1, $r2 did not have the same symbol") r1 + case AndType(tp1, tp2) => + val c1 = tp1.classSymbol + val c2 = tp2.classSymbol + if c1.isSubClass(c2) then tp1.mirrorCompanionRef + else tp2.mirrorCompanionRef // precondition: the parts of the AndType have already been checked to be non-overlapping case self @ TypeRef(prefix, _) if self.symbol.isClass => prefix.select(self.symbol.companionModule).asInstanceOf[TermRef] case self: TypeProxy => diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index e8a993abd0b6..b9c140f4dcd5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -26,7 +26,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): /** Handlers to synthesize implicits for special types */ type SpecialHandler = (Type, Span) => Context ?=> TreeWithErrors private type SpecialHandlers = List[(ClassSymbol, SpecialHandler)] - + val synthesizedClassTag: SpecialHandler = (formal, span) => formal.argInfos match case arg :: Nil => @@ -223,16 +223,16 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): /** Create an anonymous class `new Object { type MirroredMonoType = ... }` * and mark it with given attachment so that it is made into a mirror at PostTyper. */ - private def anonymousMirror(monoType: Type, attachment: Property.StickyKey[Unit], span: Span)(using Context) = + private def anonymousMirror(monoType: Type, mirroredType: Type, attachment: Property.StickyKey[Unit], pre: Type, span: Span)(using Context) = if ctx.isAfterTyper then ctx.compilationUnit.needsMirrorSupport = true val monoTypeDef = untpd.TypeDef(tpnme.MirroredMonoType, untpd.TypeTree(monoType)) - val newImpl = untpd.Template( + var newImpl = untpd.Template( constr = untpd.emptyConstructor, parents = untpd.TypeTree(defn.ObjectType) :: Nil, derived = Nil, self = EmptyValDef, body = monoTypeDef :: Nil - ).withAttachment(attachment, ()) + ).withAttachment(attachment, ()).withAttachment(AnonymousMirrorPrefix, pre) typer.typed(untpd.New(newImpl).withSpan(span)) /** The mirror type @@ -250,9 +250,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): .refinedWith(tpnme.MirroredLabel, TypeAlias(ConstantType(Constant(label.toString)))) /** A path referencing the companion of class type `clsType` */ - private def companionPath(clsType: Type, span: Span)(using Context) = - val ref = pathFor(clsType.mirrorCompanionRef) - assert(ref.symbol.is(Module) && (clsType.classSymbol.is(ModuleClass) || (ref.symbol.companionClass == clsType.classSymbol))) + private def companionPath(mirroredType: Type, cls: Symbol, span: Span)(using Context) = + val clsType = mirroredType.mirrorCompanionRef + val ref = pathFor(clsType) + assert(ref.symbol.is(Module) && (cls.is(ModuleClass) || (ref.symbol.companionClass == cls))) ref.withSpan(span) private def checkFormal(formal: Type)(using Context): Boolean = @@ -277,6 +278,9 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): monoMap(mirroredType.resultType) private def productMirror(mirroredType: Type, formal: Type, span: Span)(using Context): TreeWithErrors = + val pre = TypeOps.extractPrefix(mirroredType) match + case pre if pre.exists => pre + case _ => return EmptyTreeNoError /** do all parts match the class symbol? */ def acceptable(tp: Type, cls: Symbol): Boolean = tp match @@ -318,17 +322,19 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): .refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType)) .refinedWith(tpnme.MirroredElemLabels, TypeAlias(elemsLabels)) val mirrorRef = - if (genAnonyousMirror(cls)) anonymousMirror(monoType, ExtendsProductMirror, span) - else companionPath(mirroredType, span) + if (genAnonyousMirror(cls)) + // TODO: scan for problematic prefix + anonymousMirror(monoType, mirroredType, ExtendsProductMirror, pre, span) + else companionPath(mirroredType, cls, span) withNoErrors(mirrorRef.cast(mirrorType)) end makeProductMirror - def getError(cls: Symbol): String = - val reason = if !cls.isGenericProduct then - i"because ${cls.whyNotGenericProduct}" + def getError(cls: Symbol): String = + val reason = if !cls.isGenericProduct(pre) then + i"because ${cls.whyNotGenericProduct(pre)}" else if !canAccessCtor(cls) then i"because the constructor of $cls is innaccessible from the calling scope." - else + else "" i"$cls is not a generic product $reason" end getError @@ -350,7 +356,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): else val cls = mirroredType.classSymbol if acceptable(mirroredType, cls) - && cls.isGenericProduct + && cls.isGenericProduct(pre) && canAccessCtor(cls) then makeProductMirror(cls) @@ -359,11 +365,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): end productMirror private def sumMirror(mirroredType: Type, formal: Type, span: Span)(using Context): TreeWithErrors = - + val pre = TypeOps.extractPrefix(mirroredType) match + case pre if pre.exists => pre + case _ => return EmptyTreeNoError val cls = mirroredType.classSymbol val useCompanion = cls.useCompanionAsSumMirror val declScope = if useCompanion then cls.linkedClass else ctx.owner - val clsIsGenericSum = cls.isGenericSum(declScope) def acceptable(tp: Type): Boolean = tp match case tp: TermRef => false @@ -372,63 +379,79 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): case OrType(tp1, tp2) => acceptable(tp1) && acceptable(tp2) case _ => tp.classSymbol eq cls - if acceptable(mirroredType) && clsIsGenericSum then + if acceptable(mirroredType) && cls.isGenericSum(pre, if useCompanion then cls.linkedClass else ctx.owner) then val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString))) - def solve(sym: Symbol): Type = sym match - case childClass: ClassSymbol => - assert(childClass.isOneOf(Case | Sealed)) - if childClass.is(Module) then - childClass.sourceModule.termRef - else - childClass.primaryConstructor.info match - case info: PolyType => - // Compute the the full child type by solving the subtype constraint - // `C[X1, ..., Xn] <: P`, where - // - // - P is the current `mirroredType` - // - C is the child class, with type parameters X1, ..., Xn - // - // Contravariant type parameters are minimized, all other type parameters are maximized. - def instantiate(using Context) = - val poly = constrained(info, untpd.EmptyTree)._1 - val resType = poly.finalResultType - val target = mirroredType match - case tp: HKTypeLambda => tp.resultType - case tp => tp - resType <:< target - val tparams = poly.paramRefs - val variances = childClass.typeParams.map(_.paramVarianceSign) - val instanceTypes = tparams.lazyZip(variances).map((tparam, variance) => - TypeComparer.instanceType(tparam, fromBelow = variance < 0)) - resType.substParams(poly, instanceTypes) - instantiate(using ctx.fresh.setExploreTyperState().setOwner(childClass)) - case _ => - childClass.typeRef - case child => child.termRef - end solve - - val (monoType, elemsType) = mirroredType match - case mirroredType: HKTypeLambda => - val elems = mirroredType.derivedLambdaType( - resType = TypeOps.nestedPairs(cls.children.map(solve)) - ) - (mkMirroredMonoType(mirroredType), elems) - case _ => - val elems = TypeOps.nestedPairs(cls.children.map(solve)) - (mirroredType, elems) - - val mirrorType = - mirrorCore(defn.Mirror_SumClass, monoType, mirroredType, cls.name, formal) - .refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType)) - .refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) - val mirrorRef = - if useCompanion then companionPath(mirroredType, span) - else anonymousMirror(monoType, ExtendsSumMirror, span) - withNoErrors(mirrorRef.cast(mirrorType)) - else if !clsIsGenericSum then - (EmptyTree, List(i"$cls is not a generic sum because ${cls.whyNotGenericSum(declScope)}")) - else + // def solveInner(sym: Symbol): Type = sym match + // case childClass: ClassSymbol => + // assert(childClass.isOneOf(Case | Sealed)) + // if childClass.is(Module) then + // childClass.sourceModule.termRef + // else + // childClass.primaryConstructor.info match + // case info: PolyType => + // // Compute the the full child type by solving the subtype constraint + // // `C[X1, ..., Xn] <: P`, where + // // + // // - P is the current `mirroredType` + // // - C is the child class, with type parameters X1, ..., Xn + // // + // // Contravariant type parameters are minimized, all other type parameters are maximized. + // def instantiate(using Context) = + // val poly = constrained(info, untpd.EmptyTree)._1 + // val resType = poly.finalResultType + // val target = mirroredType match + // case tp: HKTypeLambda => tp.resultType + // case tp => tp + // resType <:< target + // val tparams = poly.paramRefs + // val variances = childClass.typeParams.map(_.paramVarianceSign) + // val instanceTypes = tparams.lazyZip(variances).map((tparam, variance) => + // TypeComparer.instanceType(tparam, fromBelow = variance < 0)) + // resType.substParams(poly, instanceTypes) + // instantiate(using ctx.fresh.setExploreTyperState().setOwner(childClass)) + // case _ => + // childClass.typeRef + // case child => child.termRef + // end solveInner + + def solve(sym: Symbol): Either[String, Type] = + // val typeAtDefinition = solveInner(sym) + // val Right(mine) = TypeOps.healPrefix(typeAtDefinition, pre): @unchecked + + val refinedType = TypeOps.refineUsingParent(mirroredType, sym) + // if !(mine =:= refinedType) then + // report.error(i"wow: ${mirroredType}: pre: $pre [raw: ${typeAtDefinition}, mine: $mine, alt: ${refinedType}]") + if refinedType.exists && refinedType <:< mirroredType then Right(refinedType) // TODO: refine isGenericSum so we don't use `<:<` + else Left(i"could not instantiate child $sym at $mirroredType") + + + val (errors, resolvedChildren) = cls.children.map(solve).partitionMap(identity) + if errors.nonEmpty then + (EmptyTree, errors) + else + val (monoType, elemsType) = mirroredType match + case mirroredType: HKTypeLambda => + val elems = mirroredType.derivedLambdaType( + resType = TypeOps.nestedPairs(resolvedChildren) + ) + (mkMirroredMonoType(mirroredType), elems) + case _ => + val elems = TypeOps.nestedPairs(resolvedChildren) + (mirroredType, elems) + + val mirrorType = + mirrorCore(defn.Mirror_SumClass, monoType, mirroredType, cls.name, formal) + .refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType)) + .refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) + val mirrorRef = + if useCompanion then companionPath(mirroredType, cls, span) + else anonymousMirror(monoType, mirroredType, ExtendsSumMirror, pre, span) + withNoErrors(mirrorRef.cast(mirrorType)) + else if !cls.isGenericSum(pre, if useCompanion then cls.linkedClass else ctx.owner) then + val whyNot = cls.whyNotGenericSum(pre, if useCompanion then cls.linkedClass else ctx.owner) + (EmptyTree, List(em"$cls is not a generic sum because $whyNot")) + else EmptyTreeNoError end sumMirror @@ -595,7 +618,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): tp.baseType(cls) val base = baseWithRefinements(formal) val result = - if (base <:< formal.widenExpr) + if (base <:< formal.widenExpr) // With the subtype test we enforce that the searched type `formal` is of the right form handler(base, span) else EmptyTreeNoError @@ -609,19 +632,19 @@ end Synthesizer object Synthesizer: - /** Tuple used to store the synthesis result with a list of errors. */ + /** Tuple used to store the synthesis result with a list of errors. */ type TreeWithErrors = (Tree, List[String]) private def withNoErrors(tree: Tree): TreeWithErrors = (tree, List.empty) private val EmptyTreeNoError: TreeWithErrors = withNoErrors(EmptyTree) private def orElse(treeWithErrors1: TreeWithErrors, treeWithErrors2: => TreeWithErrors): TreeWithErrors = treeWithErrors1 match - case (tree, errors) if tree eq genericEmptyTree => + case (tree, errors) if tree eq genericEmptyTree => val (tree2, errors2) = treeWithErrors2 (tree2, errors ::: errors2) case _ => treeWithErrors1 - private def clearErrorsIfNotEmpty(treeWithErrors: TreeWithErrors) = treeWithErrors match + private def clearErrorsIfNotEmpty(treeWithErrors: TreeWithErrors) = treeWithErrors match case (tree, _) if tree eq genericEmptyTree => treeWithErrors case (tree, _) => withNoErrors(tree) diff --git a/sbt-test/scala2-compat/i13332-fail/app/App.scala b/sbt-test/scala2-compat/i13332-fail/app/App.scala new file mode 100644 index 000000000000..2809e05febc9 --- /dev/null +++ b/sbt-test/scala2-compat/i13332-fail/app/App.scala @@ -0,0 +1,17 @@ +import collectionstrawman.* + +import scala.deriving.Mirror + +def syntheticSumMirror = { + val m = new ListModule() + + val mIList = summon[Mirror.Of[m.IList[Int]]] // error: can't summon + type derivedICons = Tuple.Head[mIList.MirroredElemTypes] + val mICons = summon[Mirror.Of[derivedICons]] // error: can't summon +} + +def syntheticProductMirror = { + val m = new ListModule() + + val mIPair = summon[Mirror.Of[m.IPair[Int, String]]] // error: can't summon +} diff --git a/sbt-test/scala2-compat/i13332-fail/build.sbt b/sbt-test/scala2-compat/i13332-fail/build.sbt new file mode 100644 index 000000000000..433a5e8baddf --- /dev/null +++ b/sbt-test/scala2-compat/i13332-fail/build.sbt @@ -0,0 +1,13 @@ +val scala3Version = sys.props("plugin.scalaVersion") +val scala2Version = sys.props("plugin.scala2Version") + +lazy val lib = project.in(file("lib")) + .settings( + scalaVersion := scala2Version + ) + +lazy val app = project.in(file("app")) + .dependsOn(lib) + .settings( + scalaVersion := scala3Version + ) diff --git a/sbt-test/scala2-compat/i13332-fail/lib/Lib.scala b/sbt-test/scala2-compat/i13332-fail/lib/Lib.scala new file mode 100644 index 000000000000..762ab2118282 --- /dev/null +++ b/sbt-test/scala2-compat/i13332-fail/lib/Lib.scala @@ -0,0 +1,13 @@ +package collectionstrawman + +class ListModule { + + sealed trait IList[+A] + case class ICons[+A](head: A, next: IList[A]) extends IList[A] + case object INil extends IList[Nothing] + + case class IPair[+A, +B](a: A, b: B) + + case object IUnit + +} diff --git a/sbt-test/scala2-compat/i13332-fail/test b/sbt-test/scala2-compat/i13332-fail/test new file mode 100644 index 000000000000..afe851be63dd --- /dev/null +++ b/sbt-test/scala2-compat/i13332-fail/test @@ -0,0 +1 @@ +-> app/compile diff --git a/sbt-test/scala2-compat/i13332/app/App.scala b/sbt-test/scala2-compat/i13332/app/App.scala new file mode 100644 index 000000000000..4559ec74d2cd --- /dev/null +++ b/sbt-test/scala2-compat/i13332/app/App.scala @@ -0,0 +1,35 @@ +import collectionstrawman.* + +import scala.deriving.Mirror + +def syntheticSumMirror_STATIC = { + val mIList = summon[Mirror.Of[ListStatic.IList[Int]]] + type derivedICons = Tuple.Head[mIList.MirroredElemTypes] + val mICons = summon[Mirror.Of[derivedICons]] + assert(mICons.fromProduct((1, ListStatic.INil)) == ListStatic.ICons(1, ListStatic.INil)) + assert(mIList.ordinal(ListStatic.ICons(1, ListStatic.INil)) == 0) + assert(mIList.ordinal(ListStatic.INil) == 1) +} + +def syntheticProductMirror_STATIC = { + val mIPair = summon[Mirror.Of[ListStatic.IPair[Int, String]]] + assert(mIPair.fromProduct((1, "foo")) == ListStatic.IPair(1, "foo")) +} + +def syntheticSingletonMirror_STATIC = { + val mIUnit = summon[Mirror.Of[ListStatic.IUnit.type]] + assert(mIUnit.fromProduct(EmptyTuple) eq ListStatic.IUnit) +} + +def syntheticSingletonMirror_PATH_DEPENDENT = { + val m = new ListModule() + + val mIUnit = summon[Mirror.Of[m.IUnit.type]] + assert(mIUnit.fromProduct(EmptyTuple) eq m.IUnit) +} + +@main def Test = + syntheticSumMirror_STATIC + syntheticProductMirror_STATIC + syntheticSingletonMirror_STATIC + syntheticSingletonMirror_PATH_DEPENDENT diff --git a/sbt-test/scala2-compat/i13332/build.sbt b/sbt-test/scala2-compat/i13332/build.sbt new file mode 100644 index 000000000000..433a5e8baddf --- /dev/null +++ b/sbt-test/scala2-compat/i13332/build.sbt @@ -0,0 +1,13 @@ +val scala3Version = sys.props("plugin.scalaVersion") +val scala2Version = sys.props("plugin.scala2Version") + +lazy val lib = project.in(file("lib")) + .settings( + scalaVersion := scala2Version + ) + +lazy val app = project.in(file("app")) + .dependsOn(lib) + .settings( + scalaVersion := scala3Version + ) diff --git a/sbt-test/scala2-compat/i13332/lib/Lib.scala b/sbt-test/scala2-compat/i13332/lib/Lib.scala new file mode 100644 index 000000000000..bf9ab97148d3 --- /dev/null +++ b/sbt-test/scala2-compat/i13332/lib/Lib.scala @@ -0,0 +1,25 @@ +package collectionstrawman + +class ListModule { + + sealed trait IList[+A] + case class ICons[+A](head: A, next: IList[A]) extends IList[A] + case object INil extends IList[Nothing] + + case class IPair[+A, +B](a: A, b: B) + + case object IUnit + +} + +object ListStatic { + + sealed trait IList[+A] + case class ICons[+A](head: A, next: IList[A]) extends IList[A] + case object INil extends IList[Nothing] + + case class IPair[+A, +B](a: A, b: B) + + case object IUnit + +} diff --git a/sbt-test/scala2-compat/i13332/test b/sbt-test/scala2-compat/i13332/test new file mode 100644 index 000000000000..069add38e899 --- /dev/null +++ b/sbt-test/scala2-compat/i13332/test @@ -0,0 +1,2 @@ +> app/compile +> app/run diff --git a/tests/neg/anon-mirror-gen-inner-a.scala b/tests/neg/anon-mirror-gen-inner-a.scala new file mode 100644 index 000000000000..a0e15151c072 --- /dev/null +++ b/tests/neg/anon-mirror-gen-inner-a.scala @@ -0,0 +1,26 @@ +import scala.deriving.Mirror + +class Outer { + + sealed trait Item + + object Wrapper { + case object A extends Item + case object B extends Item + } + + class Inaccessible { + case object C extends Item + } + +} + +@main def hello: Unit = { + + val o = new Outer() + + val mItem = summon[Mirror.Of[o.Item]] // error + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] // error + +} diff --git a/tests/neg/anon-mirror-gen-inner-b.scala b/tests/neg/anon-mirror-gen-inner-b.scala new file mode 100644 index 000000000000..e80408262938 --- /dev/null +++ b/tests/neg/anon-mirror-gen-inner-b.scala @@ -0,0 +1,16 @@ +import scala.deriving.Mirror + +class Outer { + + sealed trait Item + + object Wrapper { + case object A extends Item + case object B extends Item + } + +} + +@main def Test = { + val mItem = summon[Mirror.Of[Outer#Item]] // error +} diff --git a/tests/neg/anon-mirror-gen-this-a.scala b/tests/neg/anon-mirror-gen-this-a.scala new file mode 100644 index 000000000000..5e8cf1c15d67 --- /dev/null +++ b/tests/neg/anon-mirror-gen-this-a.scala @@ -0,0 +1,16 @@ +import scala.deriving.Mirror + +class Outer { self => + + object Inner { + sealed trait Item { thisItem => // both children and parent share a common sub-prefix + + val mItem = summon[Mirror.Of[thisItem.type]] // error: Outer.Inner.A.type is not a subtype of thisItem.type + + } + + case object A extends self.Inner.Item + case object B extends self.Inner.Item + } + +} diff --git a/tests/neg/anon-mirror-gen-this-b.scala b/tests/neg/anon-mirror-gen-this-b.scala new file mode 100644 index 000000000000..d39348198de0 --- /dev/null +++ b/tests/neg/anon-mirror-gen-this-b.scala @@ -0,0 +1,20 @@ +import scala.deriving.Mirror + +@main def Test = { + + object Bar { + case object A extends Foo.Item + } + + object Foo { + + sealed trait Item { + + val mItem = summon[Mirror.Of[Item.this.type]] // error: Bar.A.type is not a subtype of thisItem.type + + } + } + + assert(Bar.A != null) + +} diff --git a/tests/neg/i10997.scala b/tests/neg/i10997.scala index 6d37587d6b10..399d9521b60a 100644 --- a/tests/neg/i10997.scala +++ b/tests/neg/i10997.scala @@ -1,3 +1,4 @@ + sealed trait Parent trait Wrapper { @@ -15,4 +16,4 @@ class ClassWrapper { @main def Test = val cw = new ClassWrapper() - val mirrorParent = summon[deriving.Mirror.Of[cw.Base]] // error: code gen for Mirror can not access each case + val mirrorParent = summon[deriving.Mirror.Of[cw.Base]] // ok diff --git a/tests/pos/i10997.scala b/tests/pos/i10997.scala index fd9562f77d9e..0c136eb253fa 100644 --- a/tests/pos/i10997.scala +++ b/tests/pos/i10997.scala @@ -22,3 +22,8 @@ object Test2 { } } + +object Test3 { + val cw = new Test() + val mirrorParent = summon[deriving.Mirror.Of[cw.Parent]] +} diff --git a/tests/pos/i11174.scala b/tests/pos/i11174.scala new file mode 100644 index 000000000000..10b771b929e6 --- /dev/null +++ b/tests/pos/i11174.scala @@ -0,0 +1,75 @@ +import scala.compiletime.{summonFrom, summonInline, erasedValue} +import scala.deriving.Mirror + +object EnumerateNames { + + inline def summonNext[T] = + summonFrom { + case en: EnumerateNames[T] => en + } + + inline def walkThrough[Types <: Tuple]: List[String] = + inline erasedValue[Types] match + case _: (tpe *: tpes) => + summonNext[tpe].apply +: walkThrough[tpes] + case _: EmptyTuple => + Nil + + + inline def derived[T]: EnumerateNames[T] = + summonFrom { + case ev: Mirror.Of[T] => + new EnumerateNames[T] { + def apply = + inline ev match + case m: Mirror.ProductOf[T] => walkThrough[m.MirroredElemTypes].mkString(", ") + case m: Mirror.SumOf[T] => walkThrough[m.MirroredElemTypes].mkString(", ") + } + } +} + +trait EnumerateNames[T] { + def apply: String +} + +class MainClass { + enum Shape: + case Square(width: Int, height: Int) extends Shape + case Circle(radius: Int) extends Shape + + given EnumerateNames[Int] with { + def apply: String = "int" + } + inline given auto[T]:EnumerateNames[T] = EnumerateNames.derived + def deriveEnumerateNames[T](using en: EnumerateNames[T]) = en.apply + def run: Unit = println( deriveEnumerateNames[Shape] ) +} + +class MainClass2 { + given EnumerateNames[Int] with { + def apply: String = "int" + } + inline given auto[T]: EnumerateNames[T] = EnumerateNames.derived + def deriveEnumerateNames[T](using en: EnumerateNames[T]) = en.apply + + def run = { + enum Shape: + case Square(width: Int, height: Int) extends Shape + case Circle(radius: Int) extends Shape + + println( deriveEnumerateNames[Shape] ) + } +} + + +object Main { + def main(args: Array[String]) = { + new MainClass().run + } +} + +object Main2 { + def main(args: Array[String]) = { + new MainClass2().run + } +} diff --git a/tests/pos/i11174a.scala b/tests/pos/i11174a.scala new file mode 100644 index 000000000000..7164a0778dd5 --- /dev/null +++ b/tests/pos/i11174a.scala @@ -0,0 +1,25 @@ +import scala.compiletime.{summonFrom, summonInline, erasedValue} +import scala.deriving.Mirror + +trait EnumerateNames[T] + +object EnumerateNames { + inline def derived[T]: EnumerateNames[T] = + summonFrom { + case ev: Mirror.Of[T] => + inline ev match + case m: Mirror.ProductOf[T] => ??? + case m: Mirror.SumOf[T] => + inline erasedValue[m.MirroredElemTypes] match + case _: (tpe *: _) => summonInline[EnumerateNames[tpe]] + case _: EmptyTuple => + ??? + } +} + +class MainClass { + enum Shape: + case Point + inline given auto[T]: EnumerateNames[T] = EnumerateNames.derived[T] + def shapeNames: EnumerateNames[Shape] = EnumerateNames.derived[Shape] +} diff --git a/tests/pos/i12328.scala b/tests/pos/i12328.scala new file mode 100644 index 000000000000..11dc36a6fd66 --- /dev/null +++ b/tests/pos/i12328.scala @@ -0,0 +1,35 @@ +import scala.deriving.Mirror +import scala.compiletime._ + +trait Schema[T] + +object Schema { + inline def recurse[A <: Tuple]: List[Schema[Any]] = + inline erasedValue[A] match { + case _: (t *: ts) => summonInline[Schema[t]].asInstanceOf[Schema[Any]] :: recurse[ts] + case EmptyTuple => Nil + } + + inline def derived[T]: Schema[T] = + inline summonInline[Mirror.Of[T]] match { + case m: Mirror.SumOf[T] => + val subTypes = recurse[m.MirroredElemTypes] + new Schema[T] { } + case m: Mirror.ProductOf[T] => + val fields = recurse[m.MirroredElemTypes] + new Schema[T] { } + } + + inline given gen[T]: Schema[T] = derived +} + +@main def hello: Unit = { + + sealed trait Item + object Item { + case object A extends Item + case object B extends Item + } + + summon[Schema[Item]] +} diff --git a/tests/pos/i13332.scala b/tests/pos/i13332.scala new file mode 100644 index 000000000000..d6611e715e7c --- /dev/null +++ b/tests/pos/i13332.scala @@ -0,0 +1,28 @@ +import scala.deriving.Mirror + +trait IListDefn { + sealed trait IList[A] + case class INil[A]() extends IList[A] + case class ICons[A](h: A, t: IList[A]) extends IList[A] +} + +class ZListDefn { + sealed trait ZList + case class ZNil() extends ZList + case class ZCons(h: Boolean, t: ZList) extends ZList +} + +class Scope extends IListDefn { + + type Of = Mirror { type MirroredType[X] = IList[X]; type MirroredMonoType = IList[Any] ; type MirroredElemTypes[_] <: Tuple } + + val M = summon[Of] +} + +class Scope2 extends ZListDefn { + + type Of = Mirror { type MirroredType = ZList; type MirroredMonoType = ZList ; type MirroredElemTypes <: Tuple } + + val N = summon[Of] + +} diff --git a/tests/run/anon-mirror-gen-dep-a.scala b/tests/run/anon-mirror-gen-dep-a.scala new file mode 100644 index 000000000000..1425379c8aec --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-a.scala @@ -0,0 +1,22 @@ +import scala.deriving.Mirror + +class Outer { + + sealed trait Item // children have identical prefix to parent + case object A extends Item + case object B extends Item + +} + +@main def Test: Unit = { + + val o = new Outer() + + val mItem = summon[Mirror.Of[o.Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(o.A) == 0) + assert(mA.fromProduct(EmptyTuple) == o.A) + +} diff --git a/tests/run/anon-mirror-gen-dep-b.scala b/tests/run/anon-mirror-gen-dep-b.scala new file mode 100644 index 000000000000..66fd12ca5f34 --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-b.scala @@ -0,0 +1,25 @@ +import scala.deriving.Mirror + +class Outer { + + sealed trait Item // start of children's prefix is identical to parent prefix (companion mirror) + + object Item { + case object A extends Item + case object B extends Item + } + +} + +@main def Test: Unit = { + + val o = new Outer() + + val mItem = summon[Mirror.Of[o.Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(o.Item.A) == 0) + assert(mA.fromProduct(EmptyTuple) == o.Item.A) + +} diff --git a/tests/run/anon-mirror-gen-dep-c.scala b/tests/run/anon-mirror-gen-dep-c.scala new file mode 100644 index 000000000000..357466e1b520 --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-c.scala @@ -0,0 +1,25 @@ +import scala.deriving.Mirror + +class Outer { + + sealed trait Item // start of children's prefix is identical to parent prefix (anon mirror) + + object Wrapper { + case object A extends Item + case object B extends Item + } + +} + +@main def Test: Unit = { + + val o = new Outer() + + val mItem = summon[Mirror.Of[o.Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(o.Wrapper.A) == 0) + assert(mA.fromProduct(EmptyTuple) == o.Wrapper.A) + +} diff --git a/tests/run/anon-mirror-gen-dep-d.scala b/tests/run/anon-mirror-gen-dep-d.scala new file mode 100644 index 000000000000..088f6c52a187 --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-d.scala @@ -0,0 +1,27 @@ +import scala.deriving.Mirror + +class Outer { outer => + + object Inner { + sealed trait Item // both children and parent share a common sub-prefix + } + + object Wrapper { + case object A extends outer.Inner.Item + case object B extends outer.Inner.Item + } + +} + +@main def Test: Unit = { + + val o = new Outer() + + val mItem = summon[Mirror.Of[o.Inner.Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(o.Wrapper.A) == 0) + assert(mA.fromProduct(EmptyTuple) == o.Wrapper.A) + +} diff --git a/tests/run/anon-mirror-gen-dep-e.scala b/tests/run/anon-mirror-gen-dep-e.scala new file mode 100644 index 000000000000..cea2a1474f67 --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-e.scala @@ -0,0 +1,29 @@ +import scala.deriving.Mirror + +class Outer { + + object Inner { + + sealed trait Item + + object Wrapper { + case object A extends Item + case object B extends Item + } + + } + +} + +@main def Test: Unit = { + + val o = new Outer() + + val mItem = summon[Mirror.Of[o.Inner.Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(o.Inner.Wrapper.A) == 0) + assert(mA.fromProduct(EmptyTuple) == o.Inner.Wrapper.A) + +} diff --git a/tests/run/anon-mirror-gen-dep-f.scala b/tests/run/anon-mirror-gen-dep-f.scala new file mode 100644 index 000000000000..d5056c7a7c52 --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-f.scala @@ -0,0 +1,22 @@ +import scala.deriving.Mirror + +class Outer { + + enum Item { + case A, B + } + +} + +@main def Test: Unit = { + + val o = new Outer() + + val mItem = summon[Mirror.Of[o.Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(o.Item.A) == 0) + assert(mA.fromProduct(EmptyTuple) == o.Item.A) + +} diff --git a/tests/run/anon-mirror-gen-dep-g.scala b/tests/run/anon-mirror-gen-dep-g.scala new file mode 100644 index 000000000000..e5ccbfa8bb28 --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-g.scala @@ -0,0 +1,27 @@ +import scala.deriving.Mirror + +class Outer { + + sealed trait Item + case object A extends Item + case object B extends Item + + lazy val o = new Outer() // infinite init + + def hello: Unit = { + + val mItem = summon[Mirror.Of[o.Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(o.A) == 0) + assert(mA.fromProduct(EmptyTuple) == o.A) + + } + +} + +@main def Test = { + val o = new Outer() + o.hello +} diff --git a/tests/run/anon-mirror-gen-dep-h.scala b/tests/run/anon-mirror-gen-dep-h.scala new file mode 100644 index 000000000000..b1fe737962af --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-h.scala @@ -0,0 +1,30 @@ +import scala.deriving.Mirror + +class Outer { self => + + sealed trait Item + object Item { + sealed trait Fruit extends Item + object Fruit { + case object Apple extends Item with Fruit + case object Orange extends Item with Fruit + } + } + + lazy val o = new Outer() // infinite init + + def hello: Unit = { + val mFruit = summon[Mirror.Of[o.Item & o.Item.Fruit]] + type derivedApple = Tuple.Head[mFruit.MirroredElemTypes] + val mApple = summon[Mirror.Of[derivedApple]] + + assert(mFruit.ordinal(o.Item.Fruit.Apple) == 0) + assert(mApple.fromProduct(EmptyTuple) == o.Item.Fruit.Apple) + } + +} + +@main def Test = { + val o = new Outer() + o.hello +} diff --git a/tests/run/anon-mirror-gen-dep-i.scala b/tests/run/anon-mirror-gen-dep-i.scala new file mode 100644 index 000000000000..727320025eda --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-i.scala @@ -0,0 +1,29 @@ +import scala.deriving.Mirror +import scala.CanEqual.derived + +class Outer { + + sealed trait Item + object Item { + case class Fruit(seed: Seed) extends Item + case class Seed() extends Item + } + + final lazy val o = new Outer() // infinite init + + def hello: Unit = { + + val mFruit = summon[Mirror.Of[o.Item & o.Item.Fruit]] + type derivedSeed = Tuple.Head[mFruit.MirroredElemTypes] + val mSeed = summon[Mirror.Of[derivedSeed]] + + assert(mFruit.fromProduct(Tuple(o.Item.Seed())) == o.Item.Fruit(o.Item.Seed())) + assert(mSeed.fromProduct(EmptyTuple) == o.Item.Seed()) // careful to ensure that correct outer is captured + } + +} + +@main def Test = { + val o = new Outer() + o.hello +} diff --git a/tests/run/anon-mirror-gen-dep-j.scala b/tests/run/anon-mirror-gen-dep-j.scala new file mode 100644 index 000000000000..e08177a9dce8 --- /dev/null +++ b/tests/run/anon-mirror-gen-dep-j.scala @@ -0,0 +1,27 @@ +import scala.deriving.Mirror +import scala.CanEqual.derived + +class Outer { + + sealed trait Item + case class Fruit(seed: Seed) extends Item + case class Seed() extends Item + + final lazy val o = new Outer() // infinite init + + def hello: Unit = { + + val mFruit = summon[Mirror.Of[o.Item & o.Fruit]] + type derivedSeed = Tuple.Head[mFruit.MirroredElemTypes] + val mSeed = summon[Mirror.Of[derivedSeed]] + + assert(mFruit.fromProduct(Tuple(o.Seed())) == o.Fruit(o.Seed())) + assert(mSeed.fromProduct(EmptyTuple) == o.Seed()) // careful to ensure that correct outer is captured + } + +} + +@main def Test = { + val o = new Outer() + o.hello +} diff --git a/tests/run/anon-mirror-gen-inner-a.scala b/tests/run/anon-mirror-gen-inner-a.scala new file mode 100644 index 000000000000..c0460a779bdf --- /dev/null +++ b/tests/run/anon-mirror-gen-inner-a.scala @@ -0,0 +1,25 @@ +import scala.deriving.Mirror + +class Outer { self => + + object Inner { + sealed trait Item // both children and parent share a common sub-prefix + } + + object Wrapper { + case object A extends self.Inner.Item + case object B extends self.Inner.Item + } + + val mItem = summon[Mirror.Of[self.Inner.Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(self.Wrapper.A) == 0) + assert(mA.fromProduct(EmptyTuple) == self.Wrapper.A) + +} + +@main def Test = { + new Outer() +} diff --git a/tests/run/anon-mirror-gen-local-a.scala b/tests/run/anon-mirror-gen-local-a.scala new file mode 100644 index 000000000000..732f93fd8b1f --- /dev/null +++ b/tests/run/anon-mirror-gen-local-a.scala @@ -0,0 +1,19 @@ +import scala.deriving.Mirror + +@main def Test: Unit = { + + sealed trait Item + + object Item { + case object A extends Item + case object B extends Item + } + + val mItem = summon[Mirror.Of[Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(Item.A) == 0) + assert(mA.fromProduct(EmptyTuple) == Item.A) + +} diff --git a/tests/run/anon-mirror-gen-local-b.scala b/tests/run/anon-mirror-gen-local-b.scala new file mode 100644 index 000000000000..3a5eb59466a4 --- /dev/null +++ b/tests/run/anon-mirror-gen-local-b.scala @@ -0,0 +1,19 @@ +import scala.deriving.Mirror + +@main def Test: Unit = { + + sealed trait Item + + object Wrapper { + case object A extends Item + case object B extends Item + } + + val mItem = summon[Mirror.Of[Item]] + type derivedA = Tuple.Head[mItem.MirroredElemTypes] + val mA = summon[Mirror.Of[derivedA]] + + assert(mItem.ordinal(Wrapper.A) == 0) + assert(mA.fromProduct(EmptyTuple) == Wrapper.A) + +} diff --git a/tests/run/anon-mirror-gen-super-a.scala b/tests/run/anon-mirror-gen-super-a.scala new file mode 100644 index 000000000000..61c0edb5bef7 --- /dev/null +++ b/tests/run/anon-mirror-gen-super-a.scala @@ -0,0 +1,15 @@ +import scala.deriving.Mirror + +@main def Test = { + + class Foo { + final val foo: Qux.type = Qux + case object Qux + } + + class Bar extends Foo { + val mQux = summon[Mirror.Of[Bar.super.foo.type]] + assert(mQux.fromProduct(EmptyTuple) == Qux) + } + +}