Skip to content

Commit

Permalink
fix scala#9482: implement manifest algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
bishabosha committed Jul 23, 2021
1 parent cf6fa97 commit 7498987
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 2 deletions.
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,12 @@ class Definitions {
@tu lazy val SelectableClass: ClassSymbol = requiredClass("scala.Selectable")
@tu lazy val WithoutPreciseParameterTypesClass: Symbol = requiredClass("scala.Selectable.WithoutPreciseParameterTypes")

@tu lazy val ManifestClass: ClassSymbol = requiredClass("scala.reflect.Manifest")
@tu lazy val ManifestFactoryModule: Symbol = requiredModule("scala.reflect.ManifestFactory")
@tu lazy val ClassManifestFactoryModule: Symbol = requiredModule("scala.reflect.ClassManifestFactory")
@tu lazy val OptManifestClass: ClassSymbol = requiredClass("scala.reflect.OptManifest")
@tu lazy val NoManifestModule: Symbol = requiredModule("scala.reflect.NoManifest")

@tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass
@tu lazy val ClassTagClass: ClassSymbol = requiredClass("scala.reflect.ClassTag")
@tu lazy val ClassTagModule: Symbol = ClassTagClass.companionModule
Expand Down Expand Up @@ -1433,6 +1439,8 @@ class Definitions {

@tu lazy val SpecialClassTagClasses: Set[Symbol] = Set(UnitClass, AnyClass, AnyValClass)

@tu lazy val SpecialManifestClasses: Set[Symbol] = Set(AnyClass, AnyValClass, ObjectClass, NullClass, NothingClass)

/** Classes that are known not to have an initializer irrespective of
* whether NoInits is set. Note: FunctionXXLClass is in this set
* because if it is compiled by Scala2, it does not get a NoInit flag.
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,6 @@ object StdNames {
val EnumValue: N = "EnumValue"
val ExistentialTypeTree: N = "ExistentialTypeTree"
val Flag : N = "Flag"
val floatHash: N = "floatHash"
val Ident: N = "Ident"
val Import: N = "Import"
val Literal: N = "Literal"
Expand Down Expand Up @@ -414,6 +413,7 @@ object StdNames {
val argv : N = "argv"
val arrayClass: N = "arrayClass"
val arrayElementClass: N = "arrayElementClass"
val arrayType: N = "arrayType"
val arrayValue: N = "arrayValue"
val array_apply : N = "array_apply"
val array_clone : N = "array_clone"
Expand All @@ -440,6 +440,7 @@ object StdNames {
val checkInitialized: N = "checkInitialized"
val ClassManifestFactory: N = "ClassManifestFactory"
val classOf: N = "classOf"
val classType: N = "classType"
val clone_ : N = "clone"
val common: N = "common"
val compiletime : N = "compiletime"
Expand Down Expand Up @@ -481,6 +482,7 @@ object StdNames {
val find_ : N = "find"
val flagsFromBits : N = "flagsFromBits"
val flatMap: N = "flatMap"
val floatHash: N = "floatHash"
val foreach: N = "foreach"
val format: N = "format"
val fromDigits: N = "fromDigits"
Expand Down Expand Up @@ -626,6 +628,7 @@ object StdNames {
val values: N = "values"
val view_ : N = "view"
val wait_ : N = "wait"
val wildcardType: N = "wildcardType"
val withFilter: N = "withFilter"
val withFilterIfRefutable: N = "withFilterIfRefutable$"
val WorksheetWrapper: N = "WorksheetWrapper"
Expand Down
113 changes: 112 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import transform.TypeUtils._
import transform.SyntheticMembers._
import util.Property
import annotation.{tailrec, constructorOnly}
import collection.mutable

/** Synthesize terms for special classes */
class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
Expand Down Expand Up @@ -375,14 +376,124 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
synthesizedSumMirror(formal, span)
case _ => EmptyTree

private def escapeJavaArray(elemTp: Type)(using Context): Type = elemTp match
case JavaArrayType(elemTp1) => defn.ArrayOf(escapeJavaArray(elemTp1))
case _ => elemTp

private enum ManifestKind:
case Full, Opt, Clss

/** The kind that should be used for an array element, if we are `OptManifest` then this
* prevents wildcards arguments of Arrays being converted to `NoManifest`
*/
def arrayElem = if this == Full then this else Clss

end ManifestKind

/** Manifest factory that does enough to satisfy the equality semantics for
* - `scala.reflect.OptManifest` (only runtime class is recorded)
* - `scala.reflect.Manifest` (runtime class of arguments are recorded, with wildcard upper bounds wrapped)
*
* `toString` may be different.
*/
private def manifestFactoryOf(kind: ManifestKind): SpecialHandler = (formal, span) =>
import ManifestKind.*

/* Creates a tree that calls the factory method called constructor in object scala.reflect.Manifest */
def factoryManifest(constructor: TermName, tparg: Type, args: Tree*): Tree =
if args.contains(EmptyTree) then
EmptyTree
else
val factory = if kind == Full then defn.ManifestFactoryModule else defn.ClassManifestFactoryModule
applyOverloaded(ref(factory), constructor, args.toList, tparg :: Nil, Types.WildcardType).withSpan(span)

/* Creates a tree representing one of the singleton manifests.*/
def singletonManifest(name: TermName) =
ref(defn.ManifestFactoryModule).select(name).ensureApplied.withSpan(span)

def synthArrayManifest(elemTp: Type, kind: ManifestKind, topLevel: Boolean): Tree =
factoryManifest(nme.arrayType, elemTp, synthesize(elemTp, kind.arrayElem, topLevel))

/** manifests generated from wildcards can not equal Int,Long,Any,AnyRef,AnyVal etc,
* so we wrap their upper bound.
*/
def synthWildcardManifest(tp: Manifestable, hi: Type): Tree =
factoryManifest(nme.wildcardType, tp, singletonManifest(nme.Nothing), synthesizeNested(hi))


/** `Nil` if not full manifest */
def synthArgManifests(tp: Manifestable): List[Tree] = tp match
case AppliedType(_, args) if kind == Full && tp.typeSymbol.isClass =>
args.map(synthesizeNested)
case _ =>
Nil

/** This type contains all top-level types supported by Scala 2's algorithm.
* This is strictly less than what is supported by `ClassTag`,
* e.g. in Scala 2 `manifest[Int @unchecked]` will fail,
* but `classTag[Int @unchecked]` succeeds.
*/
type Manifestable =
ThisType | TermRef | ConstantType | TypeRef | AppliedType | TypeBounds | RecType | RefinedType | AndType

/** adapted from `syntheticClassTag` */
def synthManifest(tp: Manifestable, kind: ManifestKind, topLevel: Boolean) = tp match
case defn.ArrayOf(elemTp) => synthArrayManifest(elemTp, kind, topLevel)
case TypeBounds(_, hi) if kind == Full => synthWildcardManifest(tp, hi)

case tp if hasStableErasure(tp) && !(topLevel && defn.isBottomClassAfterErasure(tp.typeSymbol)) =>
// should this be Scala 2 erasure? (e.g. intersection types behave differently)
erasure(tp) match
// erase first so that union types collapse to one of the singleton Manifests
case JavaArrayType(elemTp) =>
synthArrayManifest(escapeJavaArray(elemTp), kind, topLevel)

case etp =>
val sym = etp.typeSymbol
if sym.isPrimitiveValueClass || defn.SpecialManifestClasses.contains(sym) then
singletonManifest(sym.name.toTermName)
else
val clsArg = clsOf(etp).asInstance(defn.ClassType(tp)) // cast needed to resolve overloading
factoryManifest(nme.classType, tp, (clsArg :: synthArgManifests(tp))*)

case _ =>
EmptyTree

end synthManifest

def manifestOfType(tp0: Type, kind: ManifestKind, topLevel: Boolean): Tree = tp0.dealiasKeepAnnots match
case tp1: Manifestable => synthManifest(tp1, kind, topLevel)
case tp1 => EmptyTree

def synthesize(tp: Type, kind: ManifestKind, topLevel: Boolean = true): Tree =
manifestOfType(tp, kind, topLevel) match
case EmptyTree if kind == Opt => ref(defn.NoManifestModule)
case result => result

def synthesizeNested(tp: Type): Tree = synthesize(tp, Full, topLevel = false)

formal.argInfos match
case arg :: Nil =>
synthesize(fullyDefinedType(arg, "Manifest argument", span), kind)
case _ =>
EmptyTree

end manifestFactoryOf

val synthesizedManifest: SpecialHandler = manifestFactoryOf(ManifestKind.Full)
val synthesizedOptManifest: SpecialHandler = manifestFactoryOf(ManifestKind.Opt)

val specialHandlers = List(
defn.ClassTagClass -> synthesizedClassTag,
defn.TypeTestClass -> synthesizedTypeTest,
defn.CanEqualClass -> synthesizedCanEqual,
defn.ValueOfClass -> synthesizedValueOf,
defn.Mirror_ProductClass -> synthesizedProductMirror,
defn.Mirror_SumClass -> synthesizedSumMirror,
defn.MirrorClass -> synthesizedMirror)
defn.MirrorClass -> synthesizedMirror,
defn.ManifestClass -> synthesizedManifest,
defn.OptManifestClass -> synthesizedOptManifest,
)

def tryAll(formal: Type, span: Span)(using Context): Tree =
def recur(handlers: SpecialHandlers): Tree = handlers match
Expand Down
11 changes: 11 additions & 0 deletions tests/pos/i9482.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import scala.reflect.OptManifest

object Ref {
def make[A: OptManifest]: Ref[A] = ???
}
trait Ref[A]

trait Foo[A] {
val bar = Ref.make[Int]
val baz: Ref[A] = Ref.make
}

0 comments on commit 7498987

Please sign in to comment.