Skip to content

Commit cd683e4

Browse files
committed
hijack reflect.ClassManifest definition
1 parent 7bf1f60 commit cd683e4

20 files changed

+151
-59
lines changed

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

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ package core
44

55
import scala.annotation.{threadUnsafe => tu}
66
import Types._, Contexts._, Symbols._, SymDenotations._, StdNames._, Names._, Phases._
7-
import Flags._, Scopes._, Decorators._, NameOps._, Periods._, NullOpsDecorator._
8-
import unpickleScala2.Scala2Unpickler.ensureConstructor
7+
import Flags._, Scopes._, Decorators._, NameOps._, Periods._, NullOpsDecorator._, Annotations.Annotation
8+
import unpickleScala2.Scala2Unpickler, Scala2Unpickler.ensureConstructor
99
import scala.collection.mutable
1010
import collection.mutable
1111
import Denotations.SingleDenotation
@@ -778,13 +778,36 @@ class Definitions {
778778
else
779779
NoSymbol
780780
}
781+
@tu lazy val ClassManifestAlias: Symbol = ReflectPackageClass.requiredType("ClassManifest")
781782
@tu lazy val ManifestClass: ClassSymbol = requiredClass("scala.reflect.Manifest")
782783
@tu lazy val ManifestFactoryModule: Symbol = requiredModule("scala.reflect.ManifestFactory")
783784
@tu lazy val ClassManifestFactoryModule: Symbol = requiredModule("scala.reflect.ClassManifestFactory")
784785
@tu lazy val OptManifestClass: ClassSymbol = requiredClass("scala.reflect.OptManifest")
785786
@tu lazy val NoManifestModule: Symbol = requiredModule("scala.reflect.NoManifest")
786787

787-
@tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass
788+
@tu lazy val ReflectPackageClass: Symbol = {
789+
790+
def adjustClassManifest(module: Symbol)(using Context): Unit =
791+
// `scala.reflect.ClassManifest` is a type alias to `scala.reflect.ClassTag`,
792+
// however we need to prevent it from being dealiased for the purpose of summoning of
793+
// a `ClassManifest`, which has a different result in Scala 2.
794+
// With this solution values of `ClassManifest` and `ClassTag` are still interchangeable.
795+
val classManifest = module.moduleClass.requiredType("ClassManifest")
796+
classManifest.infoOrCompleter match
797+
case TypeAlias(HKTypeLambda(params, ref)) =>
798+
val unchecked = Annotation(UncheckedAnnot) // could be any annotation really
799+
classManifest.info = HKTypeLambda.fromParams(params, TypeBounds(ref, AnnotatedType(ref, unchecked)))
800+
end adjustClassManifest
801+
802+
val module = requiredPackage("scala.reflect.package")
803+
module.infoOrCompleter match
804+
case completer: ModuleCompleter =>
805+
module.info = new ModuleCompleter(completer.moduleClass):
806+
override def complete(root: SymDenotation)(using Context): Unit =
807+
completer.complete(root)
808+
adjustClassManifest(module)
809+
module.moduleClass
810+
}
788811
@tu lazy val ClassTagClass: ClassSymbol = requiredClass("scala.reflect.ClassTag")
789812
@tu lazy val ClassTagModule: Symbol = ClassTagClass.companionModule
790813
@tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply)
@@ -1796,7 +1819,7 @@ class Definitions {
17961819
this.initCtx = ctx
17971820
if (!isInitialized) {
17981821
// force initialization of every symbol that is synthesized or hijacked by the compiler
1799-
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass
1822+
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass :+ ReflectPackageClass
18001823

18011824
isInitialized = true
18021825
}

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,11 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
731731
case tp: AndOrType =>
732732
// scalajs.js.|.UnionOps has a type parameter upper-bounded by `_ | _`
733733
tp.derivedAndOrType(mapArg(tp.tp1).bounds.hi, mapArg(tp.tp2).bounds.hi)
734+
case tp @ AnnotatedType(inner, annot) =>
735+
// added to support hijacking of `scala.reflect.ClassManifest`,
736+
// we set its info to `[T] >: ClassTag[T] <: ClassTag[T] @unchecked`
737+
val inner1 = elim(inner)
738+
tp.derivedAnnotatedType(inner1, annot)
734739
case _ =>
735740
tp
736741
}

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

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
2626

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

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

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

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

565560
val synthesizedManifest: SpecialHandler = manifestOfFactory(defn.ManifestClass)
566561
val synthesizedOptManifest: SpecialHandler = manifestOfFactory(defn.OptManifestClass)
567-
568-
val specialHandlers = List(
569-
defn.ClassTagClass -> synthesizedClassTag,
570-
defn.TypeTestClass -> synthesizedTypeTest,
571-
defn.CanEqualClass -> synthesizedCanEqual,
572-
defn.ValueOfClass -> synthesizedValueOf,
573-
defn.Mirror_ProductClass -> synthesizedProductMirror,
574-
defn.Mirror_SumClass -> synthesizedSumMirror,
575-
defn.MirrorClass -> synthesizedMirror,
576-
defn.ManifestClass -> synthesizedManifest,
577-
defn.OptManifestClass -> synthesizedOptManifest,
562+
val synthesizedClassManifest: SpecialHandler = manifestOfFactory(defn.ClassManifestAlias)
563+
564+
def genSynthesizedClassTag(formal: Type)(using Context): SpecialHandler =
565+
if formal.dealias.typeSymbol == defn.ClassManifestAlias then
566+
synthesizedClassManifest
567+
else
568+
synthesizedClassTag
569+
570+
val specialHandlers: SpecialHandlers = List(
571+
defn.ClassTagClass -> genSynthesizedClassTag,
572+
defn.TypeTestClass -> Function.const(synthesizedTypeTest),
573+
defn.CanEqualClass -> Function.const(synthesizedCanEqual),
574+
defn.ValueOfClass -> Function.const(synthesizedValueOf),
575+
defn.Mirror_ProductClass -> Function.const(synthesizedProductMirror),
576+
defn.Mirror_SumClass -> Function.const(synthesizedSumMirror),
577+
defn.MirrorClass -> Function.const(synthesizedMirror),
578+
defn.ManifestClass -> Function.const(synthesizedManifest),
579+
defn.OptManifestClass -> Function.const(synthesizedOptManifest),
578580
)
579581

580582
def tryAll(formal: Type, span: Span)(using Context): Tree =
@@ -587,10 +589,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
587589
tp.baseType(cls)
588590
val base = baseWithRefinements(formal)
589591
val result =
590-
if (base <:< formal.widenExpr)
591-
// With the subtype test we enforce that the searched type `formal` is of the right form
592-
handler(base, span)
593-
else EmptyTree
592+
if base <:< formal.widenExpr then
593+
handler(formal)(base, span)
594+
else
595+
EmptyTree
594596
result.orElse(recur(rest))
595597
case Nil =>
596598
EmptyTree

tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.check

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.scala

Lines changed: 0 additions & 11 deletions
This file was deleted.

tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.check

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.scala

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
3+
-- Error: tests/neg/interop_abstypetags_arenot_classmanifests/Test_3.scala:6:40 ----------------------------------------
4+
6 | println(implicitly[ClassManifest[T]]) // error
5+
| ^
6+
| No ClassManifest available for T.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.reflect.runtime.universe._
2+
import scala.reflect.ClassManifest
3+
4+
object Test extends App {
5+
def weakTypeTagIsnotClassManifest[T: WeakTypeTag] = {
6+
println(implicitly[ClassManifest[T]]) // error
7+
}
8+
9+
// weakTypeTagIsnotClassManifest[Int]
10+
// weakTypeTagIsnotClassManifest[String]
11+
// weakTypeTagIsnotClassManifest[Array[Int]]
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package scala.reflect.api
2+
3+
trait TypeTags { self: Universe =>
4+
trait WeakTypeTag[T]
5+
trait TypeTag[T] extends WeakTypeTag[T]
6+
}
7+
8+
abstract class Universe extends TypeTags
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package scala.reflect
2+
3+
package object runtime {
4+
5+
lazy val universe: api.Universe = new api.Universe {}
6+
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
3+
-- Error: tests/neg/interop_typetags_arenot_classmanifests/Test_3.scala:6:40 -------------------------------------------
4+
6 | println(implicitly[ClassManifest[T]]) // error
5+
| ^
6+
| No ClassManifest available for T.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.reflect.runtime.universe._
2+
import scala.reflect.ClassManifest
3+
4+
object Test extends App {
5+
def typeTagIsnotClassManifest[T: TypeTag] = {
6+
println(implicitly[ClassManifest[T]]) // error
7+
}
8+
9+
// typeTagIsnotClassManifest[Int]
10+
// typeTagIsnotClassManifest[String]
11+
// typeTagIsnotClassManifest[Array[Int]]
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package scala.reflect.api
2+
3+
trait TypeTags { self: Universe =>
4+
trait WeakTypeTag[T]
5+
trait TypeTag[T] extends WeakTypeTag[T]
6+
}
7+
8+
abstract class Universe extends TypeTags
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package scala.reflect
2+
3+
package object runtime {
4+
5+
lazy val universe: api.Universe = new api.Universe {}
6+
7+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
@deprecated("Suppress warnings", since="2.11")
3+
object Test extends App {
4+
import scala.reflect.{ClassManifest, ClassTag}
5+
def classTagIsClassManifest[T: ClassTag] = {
6+
println(implicitly[ClassManifest[T]])
7+
}
8+
9+
classTagIsClassManifest[Int]
10+
classTagIsClassManifest[String]
11+
classTagIsClassManifest[Array[Int]]
12+
}

tests/disabled/reflect/run/interop_manifests_are_classtags.scala renamed to tests/run/interop_manifests_are_classtags.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import scala.reflect.{ClassTag, classTag}
1+
import scala.reflect.{ClassTag, classTag, ClassManifest}
22

33
@deprecated("Suppress warnings", since="2.11")
44
object Test extends App {

tests/run/summon-classmanifest.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
scala.collection.immutable.List[scala.Option[Int]]
2+
Array[java.lang.String]

tests/run/summon-classmanifest.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.reflect.{ClassManifest, ClassTag}
2+
3+
type CM[T] = reflect.ClassManifest[T] @annotation.nowarn("msg=deprecated")
4+
type CManifest[T] = CM[T] // test dealiasing mixed with annotated types
5+
6+
@main def Test =
7+
// manifests are ClassTags
8+
val manifestListOptionInt: ClassTag[List[Option[Int]]] = summon[CManifest[List[Option[Int]]]]
9+
val manifestArrayString: ClassTag[Array[String]] = summon[CManifest[Array[String]]]
10+
11+
println(manifestListOptionInt) // should print arguments to List
12+
println(manifestArrayString)

0 commit comments

Comments
 (0)