Skip to content

Commit

Permalink
hijack reflect.ClassManifest definition
Browse files Browse the repository at this point in the history
  • Loading branch information
bishabosha committed Jul 22, 2021
1 parent 7bf1f60 commit cd683e4
Show file tree
Hide file tree
Showing 20 changed files with 151 additions and 59 deletions.
31 changes: 27 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ package core

import scala.annotation.{threadUnsafe => tu}
import Types._, Contexts._, Symbols._, SymDenotations._, StdNames._, Names._, Phases._
import Flags._, Scopes._, Decorators._, NameOps._, Periods._, NullOpsDecorator._
import unpickleScala2.Scala2Unpickler.ensureConstructor
import Flags._, Scopes._, Decorators._, NameOps._, Periods._, NullOpsDecorator._, Annotations.Annotation
import unpickleScala2.Scala2Unpickler, Scala2Unpickler.ensureConstructor
import scala.collection.mutable
import collection.mutable
import Denotations.SingleDenotation
Expand Down Expand Up @@ -778,13 +778,36 @@ class Definitions {
else
NoSymbol
}
@tu lazy val ClassManifestAlias: Symbol = ReflectPackageClass.requiredType("ClassManifest")
@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 ReflectPackageClass: Symbol = {

def adjustClassManifest(module: Symbol)(using Context): Unit =
// `scala.reflect.ClassManifest` is a type alias to `scala.reflect.ClassTag`,
// however we need to prevent it from being dealiased for the purpose of summoning of
// a `ClassManifest`, which has a different result in Scala 2.
// With this solution values of `ClassManifest` and `ClassTag` are still interchangeable.
val classManifest = module.moduleClass.requiredType("ClassManifest")
classManifest.infoOrCompleter match
case TypeAlias(HKTypeLambda(params, ref)) =>
val unchecked = Annotation(UncheckedAnnot) // could be any annotation really
classManifest.info = HKTypeLambda.fromParams(params, TypeBounds(ref, AnnotatedType(ref, unchecked)))
end adjustClassManifest

val module = requiredPackage("scala.reflect.package")
module.infoOrCompleter match
case completer: ModuleCompleter =>
module.info = new ModuleCompleter(completer.moduleClass):
override def complete(root: SymDenotation)(using Context): Unit =
completer.complete(root)
adjustClassManifest(module)
module.moduleClass
}
@tu lazy val ClassTagClass: ClassSymbol = requiredClass("scala.reflect.ClassTag")
@tu lazy val ClassTagModule: Symbol = ClassTagClass.companionModule
@tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply)
Expand Down Expand Up @@ -1796,7 +1819,7 @@ class Definitions {
this.initCtx = ctx
if (!isInitialized) {
// force initialization of every symbol that is synthesized or hijacked by the compiler
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass :+ ReflectPackageClass

isInitialized = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,11 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
case tp: AndOrType =>
// scalajs.js.|.UnionOps has a type parameter upper-bounded by `_ | _`
tp.derivedAndOrType(mapArg(tp.tp1).bounds.hi, mapArg(tp.tp2).bounds.hi)
case tp @ AnnotatedType(inner, annot) =>
// added to support hijacking of `scala.reflect.ClassManifest`,
// we set its info to `[T] >: ClassTag[T] <: ClassTag[T] @unchecked`
val inner1 = elim(inner)
tp.derivedAnnotatedType(inner1, annot)
case _ =>
tp
}
Expand Down
50 changes: 26 additions & 24 deletions compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):

/** Handlers to synthesize implicits for special types */
type SpecialHandler = (Type, Span) => Context ?=> Tree
private type SpecialHandlers = List[(ClassSymbol, SpecialHandler)]
type SpecialHandlerGen = Type => Context ?=> SpecialHandler
private type SpecialHandlers = List[(ClassSymbol, SpecialHandlerGen)]

val synthesizedClassTag: SpecialHandler = (formal, span) =>
formal.argInfos match
Expand Down Expand Up @@ -442,15 +443,9 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
.withSpan(span)

/** Re-wraps a type in a manifest before calling `materializeImplicit` on the result
*
* TODO: in scala 2 if not full the default is `reflect.ClassManifest`,
* not `reflect.ClassTag`, which is treated differently.
*/
def findManifest(tp: Type, manifestClass: Symbol = if full then defn.ManifestClass else NoSymbol) =
if manifestClass.exists then
materializeImplicit(manifestClass.typeRef.appliedTo(tp), span)
else
inner(tp, NoSymbol) // workaround so that a `ClassManifest` will be generated
def findManifest(tp: Type, manifestClass: Symbol = if full then defn.ManifestClass else defn.ClassManifestAlias) =
materializeImplicit(manifestClass.typeRef.appliedTo(tp), span)

def findSubManifest(tp: Type) =
findManifest(tp, if (full) defn.ManifestClass else defn.OptManifestClass)
Expand Down Expand Up @@ -564,17 +559,24 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):

val synthesizedManifest: SpecialHandler = manifestOfFactory(defn.ManifestClass)
val synthesizedOptManifest: SpecialHandler = manifestOfFactory(defn.OptManifestClass)

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.ManifestClass -> synthesizedManifest,
defn.OptManifestClass -> synthesizedOptManifest,
val synthesizedClassManifest: SpecialHandler = manifestOfFactory(defn.ClassManifestAlias)

def genSynthesizedClassTag(formal: Type)(using Context): SpecialHandler =
if formal.dealias.typeSymbol == defn.ClassManifestAlias then
synthesizedClassManifest
else
synthesizedClassTag

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

def tryAll(formal: Type, span: Span)(using Context): Tree =
Expand All @@ -587,10 +589,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
tp.baseType(cls)
val base = baseWithRefinements(formal)
val result =
if (base <:< formal.widenExpr)
// With the subtype test we enforce that the searched type `formal` is of the right form
handler(base, span)
else EmptyTree
if base <:< formal.widenExpr then
handler(formal)(base, span)
else
EmptyTree
result.orElse(recur(rest))
case Nil =>
EmptyTree
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

6 changes: 6 additions & 0 deletions tests/neg/interop_abstypetags_arenot_classmanifests.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@


-- Error: tests/neg/interop_abstypetags_arenot_classmanifests/Test_3.scala:6:40 ----------------------------------------
6 | println(implicitly[ClassManifest[T]]) // error
| ^
| No ClassManifest available for T.
12 changes: 12 additions & 0 deletions tests/neg/interop_abstypetags_arenot_classmanifests/Test_3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.reflect.runtime.universe._
import scala.reflect.ClassManifest

object Test extends App {
def weakTypeTagIsnotClassManifest[T: WeakTypeTag] = {
println(implicitly[ClassManifest[T]]) // error
}

// weakTypeTagIsnotClassManifest[Int]
// weakTypeTagIsnotClassManifest[String]
// weakTypeTagIsnotClassManifest[Array[Int]]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package scala.reflect.api

trait TypeTags { self: Universe =>
trait WeakTypeTag[T]
trait TypeTag[T] extends WeakTypeTag[T]
}

abstract class Universe extends TypeTags
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package scala.reflect

package object runtime {

lazy val universe: api.Universe = new api.Universe {}

}
6 changes: 6 additions & 0 deletions tests/neg/interop_typetags_arenot_classmanifests.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@


-- Error: tests/neg/interop_typetags_arenot_classmanifests/Test_3.scala:6:40 -------------------------------------------
6 | println(implicitly[ClassManifest[T]]) // error
| ^
| No ClassManifest available for T.
12 changes: 12 additions & 0 deletions tests/neg/interop_typetags_arenot_classmanifests/Test_3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.reflect.runtime.universe._
import scala.reflect.ClassManifest

object Test extends App {
def typeTagIsnotClassManifest[T: TypeTag] = {
println(implicitly[ClassManifest[T]]) // error
}

// typeTagIsnotClassManifest[Int]
// typeTagIsnotClassManifest[String]
// typeTagIsnotClassManifest[Array[Int]]
}
8 changes: 8 additions & 0 deletions tests/neg/interop_typetags_arenot_classmanifests/api_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package scala.reflect.api

trait TypeTags { self: Universe =>
trait WeakTypeTag[T]
trait TypeTag[T] extends WeakTypeTag[T]
}

abstract class Universe extends TypeTags
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package scala.reflect

package object runtime {

lazy val universe: api.Universe = new api.Universe {}

}
12 changes: 12 additions & 0 deletions tests/run/interop_classtags_are_classmanifests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

@deprecated("Suppress warnings", since="2.11")
object Test extends App {
import scala.reflect.{ClassManifest, ClassTag}
def classTagIsClassManifest[T: ClassTag] = {
println(implicitly[ClassManifest[T]])
}

classTagIsClassManifest[Int]
classTagIsClassManifest[String]
classTagIsClassManifest[Array[Int]]
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import scala.reflect.{ClassTag, classTag}
import scala.reflect.{ClassTag, classTag, ClassManifest}

@deprecated("Suppress warnings", since="2.11")
object Test extends App {
Expand Down
2 changes: 2 additions & 0 deletions tests/run/summon-classmanifest.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scala.collection.immutable.List[scala.Option[Int]]
Array[java.lang.String]
12 changes: 12 additions & 0 deletions tests/run/summon-classmanifest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.reflect.{ClassManifest, ClassTag}

type CM[T] = reflect.ClassManifest[T] @annotation.nowarn("msg=deprecated")
type CManifest[T] = CM[T] // test dealiasing mixed with annotated types

@main def Test =
// manifests are ClassTags
val manifestListOptionInt: ClassTag[List[Option[Int]]] = summon[CManifest[List[Option[Int]]]]
val manifestArrayString: ClassTag[Array[String]] = summon[CManifest[Array[String]]]

println(manifestListOptionInt) // should print arguments to List
println(manifestArrayString)

0 comments on commit cd683e4

Please sign in to comment.