Skip to content

Commit 5ab82c2

Browse files
committed
fix scala#9482: implement manifest algorithm
1 parent cf6fa97 commit 5ab82c2

File tree

3 files changed

+218
-2
lines changed

3 files changed

+218
-2
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,25 @@ class Definitions {
766766

767767
@tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass
768768
@tu lazy val ClassTagClass: ClassSymbol = requiredClass("scala.reflect.ClassTag")
769+
@tu lazy val TypeTagType: Type = {
770+
val optTypeTagsClass = getClassIfDefined("scala.reflect.api.TypeTags") // in scala-reflect module
771+
if optTypeTagsClass ne NoSymbol then
772+
TypeRef(optTypeTagsClass.typeRef, optTypeTagsClass.requiredClass("TypeTag"))
773+
else
774+
NoType
775+
}
776+
@tu lazy val ReflectRuntimePackageObject_universe: Symbol = {
777+
val runtimePkg = getModuleIfDefined("scala.reflect.runtime.package")
778+
if runtimePkg.exists then
779+
runtimePkg.requiredValue("universe")
780+
else
781+
NoSymbol
782+
}
783+
@tu lazy val ManifestClass: ClassSymbol = requiredClass("scala.reflect.Manifest")
784+
@tu lazy val ManifestFactoryModule: Symbol = requiredModule("scala.reflect.ManifestFactory")
785+
@tu lazy val ClassManifestFactoryModule: Symbol = requiredModule("scala.reflect.ClassManifestFactory")
786+
@tu lazy val OptManifestClass: ClassSymbol = requiredClass("scala.reflect.OptManifest")
787+
@tu lazy val NoManifestModule: Symbol = requiredModule("scala.reflect.NoManifest")
769788
@tu lazy val ClassTagModule: Symbol = ClassTagClass.companionModule
770789
@tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply)
771790

@@ -1200,6 +1219,8 @@ class Definitions {
12001219

12011220
@tu lazy val untestableClasses: Set[Symbol] = Set(NothingClass, NullClass, SingletonClass)
12021221

1222+
@tu lazy val isPhantomClass = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
1223+
12031224
@tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0)
12041225
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass))
12051226
def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,6 @@ object StdNames {
367367
val EnumValue: N = "EnumValue"
368368
val ExistentialTypeTree: N = "ExistentialTypeTree"
369369
val Flag : N = "Flag"
370-
val floatHash: N = "floatHash"
371370
val Ident: N = "Ident"
372371
val Import: N = "Import"
373372
val Literal: N = "Literal"
@@ -414,6 +413,7 @@ object StdNames {
414413
val argv : N = "argv"
415414
val arrayClass: N = "arrayClass"
416415
val arrayElementClass: N = "arrayElementClass"
416+
val arrayType: N = "arrayType"
417417
val arrayValue: N = "arrayValue"
418418
val array_apply : N = "array_apply"
419419
val array_clone : N = "array_clone"
@@ -440,6 +440,7 @@ object StdNames {
440440
val checkInitialized: N = "checkInitialized"
441441
val ClassManifestFactory: N = "ClassManifestFactory"
442442
val classOf: N = "classOf"
443+
val classType: N = "classType"
443444
val clone_ : N = "clone"
444445
val common: N = "common"
445446
val compiletime : N = "compiletime"
@@ -481,6 +482,7 @@ object StdNames {
481482
val find_ : N = "find"
482483
val flagsFromBits : N = "flagsFromBits"
483484
val flatMap: N = "flatMap"
485+
val floatHash: N = "floatHash"
484486
val foreach: N = "foreach"
485487
val format: N = "format"
486488
val fromDigits: N = "fromDigits"
@@ -489,6 +491,7 @@ object StdNames {
489491
val genericClass: N = "genericClass"
490492
val get: N = "get"
491493
val getClass_ : N = "getClass"
494+
val getClassLoader: N = "getClassLoader"
492495
val getOrElse: N = "getOrElse"
493496
val hasNext: N = "hasNext"
494497
val hashCode_ : N = "hashCode"
@@ -497,6 +500,7 @@ object StdNames {
497500
val head: N = "head"
498501
val higherKinds: N = "higherKinds"
499502
val identity: N = "identity"
503+
val intersectionType: N = "intersectionType"
500504
val implicitConversions: N = "implicitConversions"
501505
val implicitly: N = "implicitly"
502506
val in: N = "in"
@@ -588,6 +592,7 @@ object StdNames {
588592
val setSymbol: N = "setSymbol"
589593
val setType: N = "setType"
590594
val setTypeSignature: N = "setTypeSignature"
595+
val singleType: N = "singleType"
591596
val standardInterpolator: N = "standardInterpolator"
592597
val staticClass : N = "staticClass"
593598
val staticModule : N = "staticModule"
@@ -626,6 +631,7 @@ object StdNames {
626631
val values: N = "values"
627632
val view_ : N = "view"
628633
val wait_ : N = "wait"
634+
val wildcardType: N = "wildcardType"
629635
val withFilter: N = "withFilter"
630636
val withFilterIfRefutable: N = "withFilterIfRefutable$"
631637
val WorksheetWrapper: N = "WorksheetWrapper"

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

Lines changed: 190 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ package typer
44

55
import core._
66
import util.Spans.Span
7+
import unpickleScala2.Scala2Erasure
78
import Contexts._
89
import Types._, Flags._, Symbols._, Types._, Names._, StdNames._, Constants._
910
import TypeErasure.{erasure, hasStableErasure}
1011
import Decorators._
1112
import ProtoTypes._
1213
import Inferencing.{fullyDefinedType, isFullyDefined}
14+
import Implicits.SearchSuccess
1315
import ast.untpd
1416
import transform.SymUtils._
1517
import transform.TypeUtils._
1618
import transform.SyntheticMembers._
1719
import util.Property
1820
import annotation.{tailrec, constructorOnly}
21+
import collection.mutable
1922

2023
/** Synthesize terms for special classes */
2124
class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
@@ -375,14 +378,200 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
375378
synthesizedSumMirror(formal, span)
376379
case _ => EmptyTree
377380

381+
private type Scala2RefinedType = RecType | RefinedType | AndType
382+
383+
/** @see dotty.tools.dotc.core.unpickleScala2.Scala2Erasure.flattenedParents */
384+
private object Scala2RefinedType:
385+
386+
def unapply(tp: Scala2RefinedType)(using Context): Option[List[Type]] =
387+
388+
def checkSupported(tp: Type): Boolean = tp match
389+
case AnnotatedType(_, _) => false
390+
case tp @ TypeRef(prefix, _) => !(!tp.symbol.exists && prefix.dealias.isInstanceOf[Scala2RefinedType])
391+
case ground => true
392+
393+
@tailrec
394+
def inner(explore: List[Type], acc: mutable.ListBuffer[Type]): Option[List[Type]] = explore match
395+
case tp :: rest => tp match
396+
case tp: RecType => inner(tp.parent :: rest, acc)
397+
case RefinedType(parent, _, _) => inner(parent :: rest, acc)
398+
case AndType(l, r) => inner(l :: r :: rest, acc)
399+
case tp if checkSupported(tp) => inner(rest, acc += tp)
400+
case _ => None // we can not create a manifest for unsupported parents
401+
402+
case nil => Some(acc.toList)
403+
end inner
404+
405+
inner(tp :: Nil, new mutable.ListBuffer())
406+
407+
end unapply
408+
409+
end Scala2RefinedType
410+
411+
/** Creates a tree that calls the relevant factory method in object
412+
* scala.reflect.Manifest for type 'tp'. An EmptyTree is returned if
413+
* no manifest is found. todo: make this instantiate take type params as well?
414+
*/
415+
private def manifestOfFactory(flavor: Symbol): SpecialHandler = (formal, span) =>
416+
417+
def materializeImplicit(formal: Type, span: Span)(using Context): Tree =
418+
val arg = typer.inferImplicitArg(formal, span)
419+
if arg.tpe.isError then
420+
EmptyTree
421+
else
422+
arg
423+
424+
def inner(tp: Type, flavour: Symbol): Tree =
425+
426+
val full = flavor == defn.ManifestClass
427+
val opt = flavor == defn.OptManifestClass
428+
429+
/* Creates a tree that calls the factory method called constructor in object scala.reflect.Manifest */
430+
def manifestFactoryCall(constructor: TermName, tparg: Type, args: Tree*): Tree =
431+
if args contains EmptyTree then
432+
EmptyTree
433+
else
434+
val factory = if full then defn.ManifestFactoryModule else defn.ClassManifestFactoryModule
435+
applyOverloaded(ref(factory), constructor, args.toList, tparg :: Nil, Types.WildcardType)
436+
.withSpan(span)
437+
438+
/* Creates a tree representing one of the singleton manifests.*/
439+
def findSingletonManifest(name: TermName) =
440+
ref(defn.ManifestFactoryModule)
441+
.select(name)
442+
.ensureApplied
443+
.withSpan(span)
444+
445+
/** Re-wraps a type in a manifest before calling inferImplicit on the result
446+
*
447+
* TODO: in scala 2 if not full the default is `reflect.ClassManifest`,
448+
* not `reflect.ClassTag`, which is treated differently.
449+
*/
450+
def findManifest(tp: Type, manifestClass: Symbol = if full then defn.ManifestClass else NoSymbol) =
451+
if manifestClass.exists then
452+
materializeImplicit(manifestClass.typeRef.appliedTo(tp), span)
453+
else
454+
inner(tp, NoSymbol) // workaround so that a `ClassManifest` will be generated
455+
456+
def findSubManifest(tp: Type) =
457+
findManifest(tp, if (full) defn.ManifestClass else defn.OptManifestClass)
458+
459+
def inferTypeTag(tp: Type) =
460+
if defn.TypeTagType.exists then
461+
typer.inferImplicit(defn.TypeTagType.appliedTo(tp), EmptyTree, span) match
462+
case SearchSuccess(tagInScope, _, _, _) => Some(tagInScope)
463+
case _ => None
464+
else
465+
None
466+
467+
def interopTypeTag(tp: Type, tagInScope: Tree) =
468+
materializeImplicit(defn.ClassTagClass.typeRef.appliedTo(tp), span) match
469+
case EmptyTree =>
470+
report.error(i"""To create a Manifest here, it is necessary to interoperate with the
471+
|TypeTag `$tagInScope` in scope. However TypeTag to Manifest conversion requires a
472+
|ClassTag for the corresponding type to be present.
473+
|To proceed add a ClassTag for the type `$tp` (e.g. by introducing a context bound)
474+
|and recompile.""".stripMargin, ctx.source.atSpan(span))
475+
EmptyTree
476+
case clsTag =>
477+
// if TypeTag is available, assume scala.reflect.runtime.universe is also
478+
val ru = ref(defn.ReflectRuntimePackageObject_universe)
479+
val optEnclosing = ctx.owner.enclosingClass
480+
if optEnclosing.exists then
481+
val enclosing = ref(defn.Predef_classOf).appliedToType(optEnclosing.typeRef)
482+
val currentMirror = ru.select(nme.runtimeMirror).appliedTo(enclosing.select(nme.getClassLoader))
483+
ru.select(nme.internal).select(nme.typeTagToManifest)
484+
.appliedToType(tp)
485+
.appliedTo(currentMirror, tagInScope)
486+
.appliedTo(clsTag)
487+
.withSpan(span)
488+
else
489+
EmptyTree
490+
491+
def manifestOfType(tp0: Type): Tree =
492+
493+
val tp1 = tp0.dealiasKeepAnnots
494+
495+
extension [T](xs: List[T]) def isSingleton = xs.lengthCompare(1) == 0
496+
497+
def classManifest(clsRef: Type, args: List[Type]): Tree =
498+
val classarg = ref(defn.Predef_classOf).appliedToType(tp1)
499+
val suffix0 = classarg :: (args map findSubManifest)
500+
val pre = clsRef.normalizedPrefix
501+
val ignorePrefix = (pre eq NoPrefix) || pre.typeSymbol.isStaticOwner
502+
val suffix = if ignorePrefix then suffix0 else findSubManifest(pre) :: suffix0
503+
manifestFactoryCall(nme.classType, tp, suffix*)
504+
505+
tp1 match
506+
case ThisType(_) | TermRef(_,_) => manifestFactoryCall(nme.singleType, tp, singleton(tp1))
507+
case ConstantType(c) => inner(c.tpe, defn.ManifestClass)
508+
509+
case tp1 @ TypeRef(pre, desig) =>
510+
val tpSym = tp1.typeSymbol
511+
if tpSym.isPrimitiveValueClass || defn.isPhantomClass(tpSym) then
512+
findSingletonManifest(tpSym.name.toTermName)
513+
else if tpSym == defn.ObjectClass || tpSym == defn.AnyRefAlias then
514+
findSingletonManifest(nme.Object)
515+
else if tpSym.isClass then
516+
classManifest(tp1, Nil)
517+
else
518+
EmptyTree
519+
520+
case tp1 @ AppliedType(tycon, args) =>
521+
val tpSym = tycon.typeSymbol
522+
if tpSym == defn.RepeatedParamClass then
523+
EmptyTree
524+
else if tpSym == defn.ArrayClass && args.isSingleton then
525+
manifestFactoryCall(nme.arrayType, args.head, findManifest(args.head))
526+
else if tpSym.isClass then
527+
classManifest(tycon, args)
528+
else
529+
EmptyTree
530+
531+
case TypeBounds(lo, hi) if full =>
532+
manifestFactoryCall(nme.wildcardType, tp, findManifest(lo), findManifest(hi))
533+
534+
case Scala2RefinedType(parents) =>
535+
if parents.isSingleton then findManifest(parents.head)
536+
else if full then manifestFactoryCall(nme.intersectionType, tp, (parents map findSubManifest)*)
537+
else manifestOfType(Scala2Erasure.intersectionDominator(parents))
538+
539+
case _ =>
540+
EmptyTree
541+
end manifestOfType
542+
543+
if full then
544+
inferTypeTag(tp) match
545+
case Some(tagInScope) => interopTypeTag(tp, tagInScope)
546+
case _ => manifestOfType(tp)
547+
else
548+
manifestOfType(tp) match
549+
case EmptyTree if opt => ref(defn.NoManifestModule)
550+
case result => result
551+
552+
end inner
553+
554+
formal.argInfos match
555+
case arg :: Nil =>
556+
inner(fullyDefinedType(arg, "Manifest argument", span), flavor)
557+
case _ =>
558+
EmptyTree
559+
end manifestOfFactory
560+
561+
val synthesizedManifest: SpecialHandler = manifestOfFactory(defn.ManifestClass)
562+
val synthesizedOptManifest: SpecialHandler = manifestOfFactory(defn.OptManifestClass)
563+
378564
val specialHandlers = List(
379565
defn.ClassTagClass -> synthesizedClassTag,
380566
defn.TypeTestClass -> synthesizedTypeTest,
381567
defn.CanEqualClass -> synthesizedCanEqual,
382568
defn.ValueOfClass -> synthesizedValueOf,
383569
defn.Mirror_ProductClass -> synthesizedProductMirror,
384570
defn.Mirror_SumClass -> synthesizedSumMirror,
385-
defn.MirrorClass -> synthesizedMirror)
571+
defn.MirrorClass -> synthesizedMirror,
572+
defn.ManifestClass -> synthesizedManifest,
573+
defn.OptManifestClass -> synthesizedOptManifest,
574+
)
386575

387576
def tryAll(formal: Type, span: Span)(using Context): Tree =
388577
def recur(handlers: SpecialHandlers): Tree = handlers match

0 commit comments

Comments
 (0)