From cd683e41716b76b03079cc296199cd6e47a3c3e4 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 22 Jul 2021 12:28:58 +0200 Subject: [PATCH] hijack reflect.ClassManifest definition --- .../dotty/tools/dotc/core/Definitions.scala | 31 ++++++++++-- .../core/unpickleScala2/Scala2Unpickler.scala | 5 ++ .../dotty/tools/dotc/typer/Synthesizer.scala | 50 ++++++++++--------- ...op_abstypetags_arenot_classmanifests.check | 4 -- ...op_abstypetags_arenot_classmanifests.scala | 11 ---- ...terop_typetags_arenot_classmanifests.check | 4 -- ...terop_typetags_arenot_classmanifests.scala | 11 ---- ...op_abstypetags_arenot_classmanifests.check | 6 +++ .../Test_3.scala | 12 +++++ .../api_1.scala | 8 +++ .../universe_2.scala | 7 +++ ...terop_typetags_arenot_classmanifests.check | 6 +++ .../Test_3.scala | 12 +++++ .../api_1.scala | 8 +++ .../universe_2.scala | 7 +++ ...interop_classtags_are_classmanifests.scala | 12 +++++ .../run/interop_manifests_are_classtags.check | 0 .../run/interop_manifests_are_classtags.scala | 2 +- tests/run/summon-classmanifest.check | 2 + tests/run/summon-classmanifest.scala | 12 +++++ 20 files changed, 151 insertions(+), 59 deletions(-) delete mode 100644 tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.check delete mode 100644 tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.scala delete mode 100644 tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.check delete mode 100644 tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.scala create mode 100644 tests/neg/interop_abstypetags_arenot_classmanifests.check create mode 100644 tests/neg/interop_abstypetags_arenot_classmanifests/Test_3.scala create mode 100644 tests/neg/interop_abstypetags_arenot_classmanifests/api_1.scala create mode 100644 tests/neg/interop_abstypetags_arenot_classmanifests/universe_2.scala create mode 100644 tests/neg/interop_typetags_arenot_classmanifests.check create mode 100644 tests/neg/interop_typetags_arenot_classmanifests/Test_3.scala create mode 100644 tests/neg/interop_typetags_arenot_classmanifests/api_1.scala create mode 100644 tests/neg/interop_typetags_arenot_classmanifests/universe_2.scala create mode 100644 tests/run/interop_classtags_are_classmanifests.scala rename tests/{disabled/reflect => }/run/interop_manifests_are_classtags.check (100%) rename tests/{disabled/reflect => }/run/interop_manifests_are_classtags.scala (90%) create mode 100644 tests/run/summon-classmanifest.check create mode 100644 tests/run/summon-classmanifest.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 456499654ca9..0e611d6f6ed1 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -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 @@ -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) @@ -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 } diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 1a70fb1e9d2d..b7a201e626ea 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -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 } diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 6adb1c9d83d4..495a73d29dc1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -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 @@ -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) @@ -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 = @@ -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 diff --git a/tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.check b/tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.check deleted file mode 100644 index d15e33346cbc..000000000000 --- a/tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.check +++ /dev/null @@ -1,4 +0,0 @@ -interop_abstypetags_arenot_classmanifests.scala:5: error: No ClassManifest available for T. - println(classManifest[T]) - ^ -one error found diff --git a/tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.scala b/tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.scala deleted file mode 100644 index 6b05eddf7653..000000000000 --- a/tests/disabled/reflect/neg/interop_abstypetags_arenot_classmanifests.scala +++ /dev/null @@ -1,11 +0,0 @@ -import scala.reflect.runtime.universe._ - -object Test extends App { - def weakTypeTagIsnotClassManifest[T: WeakTypeTag] = { - println(classManifest[T]) - } - - weakTypeTagIsnotClassManifest[Int] - weakTypeTagIsnotClassManifest[String] - weakTypeTagIsnotClassManifest[Array[Int]] -} diff --git a/tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.check b/tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.check deleted file mode 100644 index 88fb1647e57b..000000000000 --- a/tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.check +++ /dev/null @@ -1,4 +0,0 @@ -interop_typetags_arenot_classmanifests.scala:5: error: No ClassManifest available for T. - println(classManifest[T]) - ^ -one error found diff --git a/tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.scala b/tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.scala deleted file mode 100644 index 04857aaa6d98..000000000000 --- a/tests/disabled/reflect/neg/interop_typetags_arenot_classmanifests.scala +++ /dev/null @@ -1,11 +0,0 @@ -import scala.reflect.runtime.universe._ - -object Test extends App { - def typeTagIsnotClassManifest[T: TypeTag] = { - println(classManifest[T]) - } - - typeTagIsnotClassManifest[Int] - typeTagIsnotClassManifest[String] - typeTagIsnotClassManifest[Array[Int]] -} diff --git a/tests/neg/interop_abstypetags_arenot_classmanifests.check b/tests/neg/interop_abstypetags_arenot_classmanifests.check new file mode 100644 index 000000000000..f13e0526322a --- /dev/null +++ b/tests/neg/interop_abstypetags_arenot_classmanifests.check @@ -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. diff --git a/tests/neg/interop_abstypetags_arenot_classmanifests/Test_3.scala b/tests/neg/interop_abstypetags_arenot_classmanifests/Test_3.scala new file mode 100644 index 000000000000..fc42cb42ad78 --- /dev/null +++ b/tests/neg/interop_abstypetags_arenot_classmanifests/Test_3.scala @@ -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]] +} diff --git a/tests/neg/interop_abstypetags_arenot_classmanifests/api_1.scala b/tests/neg/interop_abstypetags_arenot_classmanifests/api_1.scala new file mode 100644 index 000000000000..6590e7eac4f1 --- /dev/null +++ b/tests/neg/interop_abstypetags_arenot_classmanifests/api_1.scala @@ -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 diff --git a/tests/neg/interop_abstypetags_arenot_classmanifests/universe_2.scala b/tests/neg/interop_abstypetags_arenot_classmanifests/universe_2.scala new file mode 100644 index 000000000000..a40d7208651d --- /dev/null +++ b/tests/neg/interop_abstypetags_arenot_classmanifests/universe_2.scala @@ -0,0 +1,7 @@ +package scala.reflect + +package object runtime { + + lazy val universe: api.Universe = new api.Universe {} + +} diff --git a/tests/neg/interop_typetags_arenot_classmanifests.check b/tests/neg/interop_typetags_arenot_classmanifests.check new file mode 100644 index 000000000000..92e78a2e6971 --- /dev/null +++ b/tests/neg/interop_typetags_arenot_classmanifests.check @@ -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. diff --git a/tests/neg/interop_typetags_arenot_classmanifests/Test_3.scala b/tests/neg/interop_typetags_arenot_classmanifests/Test_3.scala new file mode 100644 index 000000000000..c01b76f24bb4 --- /dev/null +++ b/tests/neg/interop_typetags_arenot_classmanifests/Test_3.scala @@ -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]] +} diff --git a/tests/neg/interop_typetags_arenot_classmanifests/api_1.scala b/tests/neg/interop_typetags_arenot_classmanifests/api_1.scala new file mode 100644 index 000000000000..6590e7eac4f1 --- /dev/null +++ b/tests/neg/interop_typetags_arenot_classmanifests/api_1.scala @@ -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 diff --git a/tests/neg/interop_typetags_arenot_classmanifests/universe_2.scala b/tests/neg/interop_typetags_arenot_classmanifests/universe_2.scala new file mode 100644 index 000000000000..a40d7208651d --- /dev/null +++ b/tests/neg/interop_typetags_arenot_classmanifests/universe_2.scala @@ -0,0 +1,7 @@ +package scala.reflect + +package object runtime { + + lazy val universe: api.Universe = new api.Universe {} + +} diff --git a/tests/run/interop_classtags_are_classmanifests.scala b/tests/run/interop_classtags_are_classmanifests.scala new file mode 100644 index 000000000000..ef59354cf98d --- /dev/null +++ b/tests/run/interop_classtags_are_classmanifests.scala @@ -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]] +} diff --git a/tests/disabled/reflect/run/interop_manifests_are_classtags.check b/tests/run/interop_manifests_are_classtags.check similarity index 100% rename from tests/disabled/reflect/run/interop_manifests_are_classtags.check rename to tests/run/interop_manifests_are_classtags.check diff --git a/tests/disabled/reflect/run/interop_manifests_are_classtags.scala b/tests/run/interop_manifests_are_classtags.scala similarity index 90% rename from tests/disabled/reflect/run/interop_manifests_are_classtags.scala rename to tests/run/interop_manifests_are_classtags.scala index 705038ece7cb..a2666a566944 100644 --- a/tests/disabled/reflect/run/interop_manifests_are_classtags.scala +++ b/tests/run/interop_manifests_are_classtags.scala @@ -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 { diff --git a/tests/run/summon-classmanifest.check b/tests/run/summon-classmanifest.check new file mode 100644 index 000000000000..d75686b9641e --- /dev/null +++ b/tests/run/summon-classmanifest.check @@ -0,0 +1,2 @@ +scala.collection.immutable.List[scala.Option[Int]] +Array[java.lang.String] diff --git a/tests/run/summon-classmanifest.scala b/tests/run/summon-classmanifest.scala new file mode 100644 index 000000000000..7d69df4f4cac --- /dev/null +++ b/tests/run/summon-classmanifest.scala @@ -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)