@@ -26,7 +26,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
2626 /** Handlers to synthesize implicits for special types */
2727 type SpecialHandler = (Type , Span ) => Context ?=> TreeWithErrors
2828 private type SpecialHandlers = List [(ClassSymbol , SpecialHandler )]
29-
29+
3030 val synthesizedClassTag : SpecialHandler = (formal, span) =>
3131 formal.argInfos match
3232 case arg :: Nil =>
@@ -278,28 +278,15 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
278278
279279 private def productMirror (mirroredType : Type , formal : Type , span : Span )(using Context ): TreeWithErrors =
280280
281- /** do all parts match the class symbol? */
282- def acceptable (tp : Type , cls : Symbol ): Boolean = tp match
283- case tp : HKTypeLambda if tp.resultType.isInstanceOf [HKTypeLambda ] => false
284- case tp : TypeProxy => acceptable(tp.underlying, cls)
285- case OrType (tp1, tp2) => acceptable(tp1, cls) && acceptable(tp2, cls)
286- case _ => tp.classSymbol eq cls
287-
288- /** for a case class, if it will have an anonymous mirror,
289- * check that its constructor can be accessed
290- * from the calling scope.
291- */
292- def canAccessCtor (cls : Symbol ): Boolean =
293- ! genAnonyousMirror(cls) || {
294- def isAccessible (sym : Symbol ): Boolean = ctx.owner.isContainedIn(sym)
295- def isSub (sym : Symbol ): Boolean = ctx.owner.ownersIterator.exists(_.derivesFrom(sym))
296- val ctor = cls.primaryConstructor
297- (! ctor.isOneOf(Private | Protected ) || isSub(cls)) // we cant access the ctor because we do not extend cls
298- && (! ctor.privateWithin.exists || isAccessible(ctor.privateWithin)) // check scope is compatible
299- }
300-
301- def genAnonyousMirror (cls : Symbol ): Boolean =
302- cls.is(Scala2x ) || cls.linkedClass.is(Case )
281+ def whyNotAcceptableType (tp : Type , cls : Symbol ): String = tp match
282+ case tp : HKTypeLambda if tp.resultType.isInstanceOf [HKTypeLambda ] =>
283+ i " its subpart $tp is not a supported kind (either `*` or `* -> *`) "
284+ case tp : TypeProxy => whyNotAcceptableType(tp.underlying, cls)
285+ case OrType (tp1, tp2) =>
286+ Seq (tp1, tp2).map(whyNotAcceptableType(_, cls)).find(_.nonEmpty).getOrElse(" " )
287+ case _ =>
288+ if tp.classSymbol eq cls then " "
289+ else i " a subpart reduces to the more precise ${tp.classSymbol}, expected $cls"
303290
304291 def makeProductMirror (cls : Symbol ): TreeWithErrors =
305292 val accessors = cls.caseAccessors.filterNot(_.isAllOf(PrivateLocal ))
@@ -318,61 +305,63 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
318305 .refinedWith(tpnme.MirroredElemTypes , TypeAlias (elemsType))
319306 .refinedWith(tpnme.MirroredElemLabels , TypeAlias (elemsLabels))
320307 val mirrorRef =
321- if (genAnonyousMirror( cls)) anonymousMirror(monoType, ExtendsProductMirror , span)
322- else companionPath(mirroredType , span)
308+ if cls.useCompanionAsProductMirror then companionPath(mirroredType , span)
309+ else anonymousMirror(monoType, ExtendsProductMirror , span)
323310 withNoErrors(mirrorRef.cast(mirrorType))
324311 end makeProductMirror
325312
326- def getError (cls : Symbol ): String =
327- val reason = if ! cls.isGenericProduct then
328- i " because ${cls.whyNotGenericProduct}"
329- else if ! canAccessCtor(cls) then
330- i " because the constructor of $cls is innaccessible from the calling scope. "
331- else
332- " "
333- i " $cls is not a generic product $reason"
334- end getError
313+ /** widen TermRef to see if they are an alias to an enum singleton */
314+ def isEnumSingletonRef (tp : Type )(using Context ): Boolean = tp match
315+ case tp : TermRef =>
316+ val sym = tp.termSymbol
317+ sym.isEnumCase || (! tp.isOverloaded && isEnumSingletonRef(tp.underlying.widenExpr))
318+ case _ => false
335319
336320 mirroredType match
337321 case AndType (tp1, tp2) =>
338322 orElse(productMirror(tp1, formal, span), productMirror(tp2, formal, span))
339323 case _ =>
340- if mirroredType.termSymbol.is(CaseVal ) then
341- val module = mirroredType.termSymbol
342- val modulePath = pathFor(mirroredType).withSpan(span)
343- if module.info.classSymbol.is(Scala2x ) then
344- val mirrorType = mirrorCore(defn.Mirror_SingletonProxyClass , mirroredType, mirroredType, module.name, formal)
345- val mirrorRef = New (defn.Mirror_SingletonProxyClass .typeRef, modulePath :: Nil )
324+ val cls = mirroredType.classSymbol
325+ if isEnumSingletonRef(mirroredType) || cls.isAllOf(Case | Module ) then
326+ val (singleton, singletonRef) =
327+ if mirroredType.termSymbol.exists then (mirroredType.termSymbol, mirroredType)
328+ else (cls.sourceModule, cls.sourceModule.reachableTermRef)
329+ val singletonPath = pathFor(singletonRef).withSpan(span)
330+ if singleton.info.classSymbol.is(Scala2x ) then // could be Scala 3 alias of Scala 2 case object.
331+ val mirrorType = mirrorCore(defn.Mirror_SingletonProxyClass , mirroredType, mirroredType, singleton.name, formal)
332+ val mirrorRef = New (defn.Mirror_SingletonProxyClass .typeRef, singletonPath :: Nil )
346333 withNoErrors(mirrorRef.cast(mirrorType))
347334 else
348- val mirrorType = mirrorCore(defn.Mirror_SingletonClass , mirroredType, mirroredType, module .name, formal)
349- withNoErrors(modulePath .cast(mirrorType))
335+ val mirrorType = mirrorCore(defn.Mirror_SingletonClass , mirroredType, mirroredType, singleton .name, formal)
336+ withNoErrors(singletonPath .cast(mirrorType))
350337 else
351- val cls = mirroredType.classSymbol
352- if acceptable(mirroredType, cls)
353- && cls.isGenericProduct
354- && canAccessCtor(cls)
355- then
356- makeProductMirror(cls)
357- else
358- (EmptyTree , List (getError(cls)))
338+ val acceptableMsg = whyNotAcceptableType(mirroredType, cls)
339+ if acceptableMsg.isEmpty then
340+ if cls.isGenericProduct then makeProductMirror(cls)
341+ else withErrors(i " $cls is not a generic product because ${cls.whyNotGenericProduct}" )
342+ else withErrors(i " type $mirroredType is not a generic product because $acceptableMsg" )
359343 end productMirror
360344
361345 private def sumMirror (mirroredType : Type , formal : Type , span : Span )(using Context ): TreeWithErrors =
362346
363347 val cls = mirroredType.classSymbol
364- val useCompanion = cls.useCompanionAsSumMirror
365- val declScope = if useCompanion then cls.linkedClass else ctx.owner
366- val clsIsGenericSum = cls.isGenericSum(declScope)
367-
368- def acceptable (tp : Type ): Boolean = tp match
369- case tp : TermRef => false
370- case tp : HKTypeLambda if tp.resultType.isInstanceOf [HKTypeLambda ] => false
371- case tp : TypeProxy => acceptable(tp.underlying)
372- case OrType (tp1, tp2) => acceptable(tp1) && acceptable(tp2)
373- case _ => tp.classSymbol eq cls
374-
375- if acceptable(mirroredType) && clsIsGenericSum then
348+ val clsIsGenericSum = cls.isGenericSum
349+
350+ def whyNotAcceptableType (tp : Type ): String = tp match
351+ case tp : TermRef => i " its subpart $tp is a term reference "
352+ case tp : HKTypeLambda if tp.resultType.isInstanceOf [HKTypeLambda ] =>
353+ i " its subpart $tp is not a supported kind (either `*` or `* -> *`) "
354+ case tp : TypeProxy => whyNotAcceptableType(tp.underlying)
355+ case OrType (tp1, tp2) =>
356+ Seq (tp1, tp2).map(whyNotAcceptableType).find(_.nonEmpty).getOrElse(" " )
357+ case _ =>
358+ if tp.classSymbol eq cls then " "
359+ else i " a subpart reduces to the more precise ${tp.classSymbol}, expected $cls"
360+
361+
362+ val acceptableMsg = whyNotAcceptableType(mirroredType)
363+
364+ if acceptableMsg.isEmpty && clsIsGenericSum then
376365 val elemLabels = cls.children.map(c => ConstantType (Constant (c.name.toString)))
377366
378367 def solve (sym : Symbol ): Type = sym match
@@ -423,12 +412,14 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
423412 .refinedWith(tpnme.MirroredElemTypes , TypeAlias (elemsType))
424413 .refinedWith(tpnme.MirroredElemLabels , TypeAlias (TypeOps .nestedPairs(elemLabels)))
425414 val mirrorRef =
426- if useCompanion then companionPath(mirroredType, span)
415+ if cls.useCompanionAsSumMirror then companionPath(mirroredType, span)
427416 else anonymousMirror(monoType, ExtendsSumMirror , span)
428417 withNoErrors(mirrorRef.cast(mirrorType))
418+ else if acceptableMsg.nonEmpty then
419+ withErrors(i " type $mirroredType is not a generic sum because $acceptableMsg" )
429420 else if ! clsIsGenericSum then
430- ( EmptyTree , List ( i " $cls is not a generic sum because ${cls.whyNotGenericSum(declScope)} " ) )
431- else
421+ withErrors( i " $cls is not a generic sum because ${cls.whyNotGenericSum} " )
422+ else
432423 EmptyTreeNoError
433424 end sumMirror
434425
@@ -595,7 +586,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
595586 tp.baseType(cls)
596587 val base = baseWithRefinements(formal)
597588 val result =
598- if (base <:< formal.widenExpr)
589+ if (base <:< formal.widenExpr)
599590 // With the subtype test we enforce that the searched type `formal` is of the right form
600591 handler(base, span)
601592 else EmptyTreeNoError
@@ -609,19 +600,20 @@ end Synthesizer
609600
610601object Synthesizer :
611602
612- /** Tuple used to store the synthesis result with a list of errors. */
603+ /** Tuple used to store the synthesis result with a list of errors. */
613604 type TreeWithErrors = (Tree , List [String ])
614605 private def withNoErrors (tree : Tree ): TreeWithErrors = (tree, List .empty)
606+ private def withErrors (errors : String * ): TreeWithErrors = (EmptyTree , errors.toList)
615607
616608 private val EmptyTreeNoError : TreeWithErrors = withNoErrors(EmptyTree )
617609
618610 private def orElse (treeWithErrors1 : TreeWithErrors , treeWithErrors2 : => TreeWithErrors ): TreeWithErrors = treeWithErrors1 match
619- case (tree, errors) if tree eq genericEmptyTree =>
611+ case (tree, errors) if tree eq genericEmptyTree =>
620612 val (tree2, errors2) = treeWithErrors2
621613 (tree2, errors ::: errors2)
622614 case _ => treeWithErrors1
623615
624- private def clearErrorsIfNotEmpty (treeWithErrors : TreeWithErrors ) = treeWithErrors match
616+ private def clearErrorsIfNotEmpty (treeWithErrors : TreeWithErrors ) = treeWithErrors match
625617 case (tree, _) if tree eq genericEmptyTree => treeWithErrors
626618 case (tree, _) => withNoErrors(tree)
627619
0 commit comments