From 798af02749b890eb0cf2036e219a6275e911a5d6 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 13 Jul 2021 17:52:59 +0200 Subject: [PATCH 1/3] fix #9482: implement manifest algorithm --- .../dotty/tools/dotc/core/Definitions.scala | 8 ++ .../src/dotty/tools/dotc/core/StdNames.scala | 5 +- .../dotty/tools/dotc/typer/Synthesizer.scala | 111 +++++++++++++++++- tests/neg/manifest-summoning.scala | 27 +++++ tests/pos/i9482.scala | 11 ++ tests/pos/manifest-summoning.scala | 22 ++++ tests/run/i9482.scala | 83 +++++++++++++ tests/run/manifest-summoning.scala | 101 ++++++++++++++++ 8 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 tests/neg/manifest-summoning.scala create mode 100644 tests/pos/i9482.scala create mode 100644 tests/pos/manifest-summoning.scala create mode 100644 tests/run/i9482.scala create mode 100644 tests/run/manifest-summoning.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index a0db1a893062..7c1c2494d323 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -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 @@ -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. diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 5c718d4af0da..b6aea21bee8b 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -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" @@ -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" @@ -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" @@ -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" @@ -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" diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index e848a19e147e..546527ca1d26 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -375,6 +375,112 @@ 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) + * however,`toString` may be different. + * + * There are some differences to `ClassTag`, + * e.g. in Scala 2 `manifest[Int @unchecked]` will fail, but `classTag[Int @unchecked]` succeeds. + */ + 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, topLevel: Boolean): Tree = + factoryManifest(nme.wildcardType, tp, singletonManifest(nme.Nothing), synthesize(hi, Full, topLevel)) + + /** `Nil` if not full manifest */ + def synthArgManifests(tp: Manifestable): List[Tree] = tp match + case AppliedType(_, args) if kind == Full && tp.typeSymbol.isClass => + args.map(synthesize(_, Full, topLevel = false)) + case _ => + Nil + + /** This type contains all top-level types supported by Scala 2's algorithm */ + type Manifestable = + ThisType | TermRef | ConstantType | TypeRef | AppliedType | TypeBounds | RecType | RefinedType | AndType + + def canManifest(tp: Manifestable, topLevel: Boolean) = + val sym = tp.typeSymbol + !sym.isAbstractType + && hasStableErasure(tp) + && !(topLevel && defn.isBottomClassAfterErasure(sym)) + + /** 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, topLevel) + + case tp if canManifest(tp, topLevel) => + val sym = tp.typeSymbol + if sym.isPrimitiveValueClass || defn.SpecialManifestClasses.contains(sym) then + singletonManifest(sym.name.toTermName) + else + erasure(tp) match + case JavaArrayType(elemTp) => + synthArrayManifest(escapeJavaArray(elemTp), kind, topLevel) + + case etp => + 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): Tree = + manifestOfType(tp, kind, topLevel) match + case EmptyTree if kind == Opt => ref(defn.NoManifestModule) + case result => result + + formal.argInfos match + case arg :: Nil => + synthesize(fullyDefinedType(arg, "Manifest argument", span), kind, topLevel = true) + 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, @@ -382,7 +488,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): 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 diff --git a/tests/neg/manifest-summoning.scala b/tests/neg/manifest-summoning.scala new file mode 100644 index 000000000000..c08e1cefaa91 --- /dev/null +++ b/tests/neg/manifest-summoning.scala @@ -0,0 +1,27 @@ +val `Array[Nothing]` = manifest[Array[Nothing]] // error +val `Array[Null]` = manifest[Array[Null]] // error +val m_Nothing = manifest[Nothing] // error +val m_Null = manifest[Null] // error + +val `Array[? <: Nothing]` = manifest[Array[? <: Nothing]] // error +val `Array[? <: Null]` = manifest[Array[? <: Null]] // error + +val `Int @unchecked` = manifest[Int @unchecked] // error + +val `0 | 1` = manifest[0 | 1] // error + +class Box[T] { + val m = manifest[T] // error +} + +object Foo { + type F[T] <: T + manifest[Array[F[Int]]] // error +} + +object opaques { + opaque type OpaqueList[+A] = List[A] +} +import opaques.* + +val `OpaqueList[Int]` = manifest[OpaqueList[Int]] // error (opaque types are not supported) diff --git a/tests/pos/i9482.scala b/tests/pos/i9482.scala new file mode 100644 index 000000000000..6549539e49a7 --- /dev/null +++ b/tests/pos/i9482.scala @@ -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 +} diff --git a/tests/pos/manifest-summoning.scala b/tests/pos/manifest-summoning.scala new file mode 100644 index 000000000000..e9840d33b8c8 --- /dev/null +++ b/tests/pos/manifest-summoning.scala @@ -0,0 +1,22 @@ +object Foo { + + object opaques { + opaque type Inner = String + val i: Inner = "i" + } + + val singleton: opaques.Inner = opaques.i + + val om_Inner = optManifest[opaques.Inner] // NoManifest + val om_singleton = optManifest[singleton.type] // NoManifest + val ct_Inner = reflect.classTag[opaques.Inner] + val ct_singleton = reflect.classTag[singleton.type] +} + +val `List[Nothing]` = manifest[List[Nothing]] +val `List[Array[Nothing]]` = manifest[List[Array[Nothing]]] // ok when Nothing is not the argument of top-level array + +val `Array[Array[List[Int]]]` = manifest[Array[Array[List[Int]]]] + +trait Mixin[T <: Mixin[T]] { type Self = T } +class Baz extends Mixin[Baz] { val m = manifest[Self] } diff --git a/tests/run/i9482.scala b/tests/run/i9482.scala new file mode 100644 index 000000000000..425a9b333d29 --- /dev/null +++ b/tests/run/i9482.scala @@ -0,0 +1,83 @@ +import scala.reflect.{OptManifest, ClassTag} + +object Ref { + + object Sentinel + + def makeWithArr[A: OptManifest]: String = optManifest[A] match { + case m: ClassTag[_] => m.newArray(0).asInstanceOf[AnyRef] match { + // these can be reordered, so long as Unit comes before AnyRef + case _: Array[Boolean] => "bool" + case _: Array[Byte] => "byte" + case _: Array[Short] => "short" + case _: Array[Char] => "char" + case _: Array[Int] => "int" + case _: Array[Float] => "float" + case _: Array[Long] => "long" + case _: Array[Double] => "double" + case _: Array[Unit] => "unit" + case a: Array[AnyRef] => a.getClass.getComponentType.getName + } + case _ => "" + } + + def make[A: OptManifest]: String = optManifest[A] match { + case m: ClassTag[a] => m match { + case ClassTag.Boolean => "bool" + case ClassTag.Byte => "byte" + case ClassTag.Short => "short" + case ClassTag.Char => "char" + case ClassTag.Int => "int" + case ClassTag.Float => "float" + case ClassTag.Long => "long" + case ClassTag.Double => "double" + case ClassTag.Unit => "unit" + case ClassTag.Any => "any" + case ClassTag.AnyVal => "anyval" + case ClassTag.Object => "anyref" + case _ => m.runtimeClass.getName + } + case NoManifest => "" + } + +} + +import Ref.* + +def baz[A] = Ref.makeWithArr[A] +def qux[A] = Ref.make[A] + +@main def Test = { + + assert(Ref.makeWithArr[Boolean] == "bool") + assert(Ref.makeWithArr[Byte] == "byte") + assert(Ref.makeWithArr[Short] == "short") + assert(Ref.makeWithArr[Char] == "char") + assert(Ref.makeWithArr[Int] == "int") + assert(Ref.makeWithArr[Float] == "float") + assert(Ref.makeWithArr[Long] == "long") + assert(Ref.makeWithArr[Double] == "double") + assert(Ref.makeWithArr[Unit] == "unit") + assert(Ref.makeWithArr["abc"] == "java.lang.String") + assert(Ref.makeWithArr[Null] == "") + assert(Ref.makeWithArr[Nothing] == "") + assert(baz[Int] == "") + + assert(Ref.make[Boolean] == "bool") + assert(Ref.make[Byte] == "byte") + assert(Ref.make[Short] == "short") + assert(Ref.make[Char] == "char") + assert(Ref.make[Int] == "int") + assert(Ref.make[Float] == "float") + assert(Ref.make[Long] == "long") + assert(Ref.make[Double] == "double") + assert(Ref.make[Unit] == "unit") + assert(Ref.make[Any] == "any") + assert(Ref.make[AnyVal] == "anyval") + assert(Ref.make[AnyRef] == "anyref") + assert(Ref.make["abc"] == "java.lang.String") + assert(Ref.make[Null] == "") + assert(Ref.make[Nothing] == "") + assert(qux[Int] == "") + +} diff --git a/tests/run/manifest-summoning.scala b/tests/run/manifest-summoning.scala new file mode 100644 index 000000000000..dcee63801863 --- /dev/null +++ b/tests/run/manifest-summoning.scala @@ -0,0 +1,101 @@ +import scala.reflect.{classTag, ClassTag, NoManifest} + +@main def Test: Unit = + + /* ====== no manifest available ====== */ + + locally { + noManifest[Array[? <: Int]] // available as a manifest + noManifest[Array[? <: String]] // available as a manifest + noManifest[Array[Nothing]] + noManifest[Array[Null]] + noManifest[Nothing] + noManifest[Null] + } + + /* ====== ClassTag and OptManifest have the same runtime class and same equality ======= */ + + locally { + interopOpt[List[Int]] + interopOpt[List[? <: Int]] + } + + /* ====== Test some OptManifest have the same runtime class and are equal ======= */ + + locally { + sameClassEqualOpt[List[Int], List[? <: Int]] // not equal for full manifests + sameClassEqualOpt[List[Int], List[String]] // not equal for full manifests + } + + /* ============================================================================= */ + // The following tests rely on <:< being correct, i.e. `equals` on Manifest // + // uses `<:<` underneath. // + /* ============================================================================= */ + + /* ====== Test some Manifest have the same runtime class and are equal ======= */ + + locally { + trait A + trait B {def b: Int} + trait C {def c: Int} + trait D {def d: Int} + class fooAnnot extends scala.annotation.StaticAnnotation + + type SomeRefinedType = + ((B {def b: 0} & C) & ((A @fooAnnot) & D {def d: 2})) {def c: 1} + + sameClassEqualMan[Array[? <: String], Array[String]] + sameClassEqualMan[SomeRefinedType, A] + } + + + /* ====== Test some Manifest have the same runtime class but are not equal ======= */ + + locally { + sameClassNonEqualMan[List[Int], List[? <: Int]] + sameClassNonEqualMan[List[Int], List[String]] + } + + /* ====== Test that some Manifest have the same runtime class, are not equal, but are `<:<` ======= */ + + locally { + class A + class B extends A + + sameClassSub[List[Int], List[AnyVal]] + sameClassSub[List[Unit], List[AnyVal]] + sameClassSub[List[B], List[A]] + sameClassSub[Array[List[B]], Array[List[A]]] + } + +end Test + +def noManifest[A: OptManifest] = + assert(optManifest[A] eq NoManifest) + +def interopOpt[A: ClassTag: OptManifest] = + assert(classTag[A] == optManifest[A]) + optManifest[A] match + case optA: ClassTag[_] => + assert(classTag[A].runtimeClass == optA.runtimeClass) + +def sameClassEqualOpt[A: OptManifest, B: OptManifest] = + assert(optManifest[A] == optManifest[B]) + (optManifest[A], optManifest[B]) match + case (a: ClassTag[_], b: ClassTag[_]) => + assert(a.runtimeClass == b.runtimeClass) + +def sameClassMan[A: Manifest, B: Manifest] = + assert(manifest[A].runtimeClass == manifest[B].runtimeClass) + +def sameClassEqualMan[A: Manifest, B: Manifest] = + sameClassMan[A, B] + assert(manifest[A] == manifest[B]) + +def sameClassNonEqualMan[A: Manifest, B: Manifest] = + sameClassMan[A, B] + assert(manifest[A] != manifest[B]) + +def sameClassSub[A: Manifest, B: Manifest] = + sameClassNonEqualMan[A, B] + assert(manifest[A] <:< manifest[B]) From f1c1195f1494f4b7c7992a128426ebadaba3d0b7 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sat, 24 Jul 2021 03:23:13 +0200 Subject: [PATCH 2/3] deprecate synthesis of Manifests --- .../src/dotty/tools/dotc/typer/Synthesizer.scala | 10 +++++++++- .../deprecation/manifest-summoning.check | 16 ++++++++++++++++ .../deprecation/manifest-summoning.scala | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/neg-custom-args/deprecation/manifest-summoning.check create mode 100644 tests/neg-custom-args/deprecation/manifest-summoning.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 546527ca1d26..ced11f14cd41 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -472,7 +472,15 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): formal.argInfos match case arg :: Nil => - synthesize(fullyDefinedType(arg, "Manifest argument", span), kind, topLevel = true) + val manifest = synthesize(fullyDefinedType(arg, "Manifest argument", span), kind, topLevel = true) + if manifest != EmptyTree then + report.deprecationWarning( + i"""Compiler synthesis of Manifest and OptManifest is deprecated, + |and calls should be replaced by `reflect.classTag[$arg]`. + |Alternatively, the using new metaprogramming features of Scala 3 + |could help avoid the need for runtime type information, see + |https://docs.scala-lang.org/scala3/reference/metaprogramming.html""", ctx.source.atSpan(span)) + manifest case _ => EmptyTree diff --git a/tests/neg-custom-args/deprecation/manifest-summoning.check b/tests/neg-custom-args/deprecation/manifest-summoning.check new file mode 100644 index 000000000000..2d43cf532a47 --- /dev/null +++ b/tests/neg-custom-args/deprecation/manifest-summoning.check @@ -0,0 +1,16 @@ +-- Error: tests/neg-custom-args/deprecation/manifest-summoning.scala:1:34 ---------------------------------------------- +1 |val foo = manifest[List[? <: Int]] // error + | ^ + | Compiler synthesis of Manifest and OptManifest is deprecated, + | and calls should be replaced by `reflect.classTag[List[? <: Int]]`. + | Alternatively, the using new metaprogramming features of Scala 3 + | could help avoid the need for runtime type information, see + | https://docs.scala-lang.org/scala3/reference/metaprogramming.html +-- Error: tests/neg-custom-args/deprecation/manifest-summoning.scala:2:41 ---------------------------------------------- +2 |val bar = optManifest[Array[? <: String]] // error + | ^ + | Compiler synthesis of Manifest and OptManifest is deprecated, + | and calls should be replaced by `reflect.classTag[Array[? <: String]]`. + | Alternatively, the using new metaprogramming features of Scala 3 + | could help avoid the need for runtime type information, see + | https://docs.scala-lang.org/scala3/reference/metaprogramming.html diff --git a/tests/neg-custom-args/deprecation/manifest-summoning.scala b/tests/neg-custom-args/deprecation/manifest-summoning.scala new file mode 100644 index 000000000000..7e9d9ee2cc9d --- /dev/null +++ b/tests/neg-custom-args/deprecation/manifest-summoning.scala @@ -0,0 +1,2 @@ +val foo = manifest[List[? <: Int]] // error +val bar = optManifest[Array[? <: String]] // error From ba67efaa4e881dcae88e18ca21bd49590d1340bf Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sat, 24 Jul 2021 13:14:49 +0200 Subject: [PATCH 3/3] address review --- .../dotty/tools/dotc/typer/Synthesizer.scala | 9 ++++----- .../deprecation/manifest-summoning.check | 18 ++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index ced11f14cd41..983d3be2ff20 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -475,11 +475,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val manifest = synthesize(fullyDefinedType(arg, "Manifest argument", span), kind, topLevel = true) if manifest != EmptyTree then report.deprecationWarning( - i"""Compiler synthesis of Manifest and OptManifest is deprecated, - |and calls should be replaced by `reflect.classTag[$arg]`. - |Alternatively, the using new metaprogramming features of Scala 3 - |could help avoid the need for runtime type information, see - |https://docs.scala-lang.org/scala3/reference/metaprogramming.html""", ctx.source.atSpan(span)) + i"""Compiler synthesis of Manifest and OptManifest is deprecated, instead + |replace with the type `scala.reflect.ClassTag[$arg]`. + |Alternatively, consider using the new metaprogramming features of Scala 3, + |see https://docs.scala-lang.org/scala3/reference/metaprogramming.html""", ctx.source.atSpan(span)) manifest case _ => EmptyTree diff --git a/tests/neg-custom-args/deprecation/manifest-summoning.check b/tests/neg-custom-args/deprecation/manifest-summoning.check index 2d43cf532a47..aa1462f8baba 100644 --- a/tests/neg-custom-args/deprecation/manifest-summoning.check +++ b/tests/neg-custom-args/deprecation/manifest-summoning.check @@ -1,16 +1,14 @@ -- Error: tests/neg-custom-args/deprecation/manifest-summoning.scala:1:34 ---------------------------------------------- 1 |val foo = manifest[List[? <: Int]] // error | ^ - | Compiler synthesis of Manifest and OptManifest is deprecated, - | and calls should be replaced by `reflect.classTag[List[? <: Int]]`. - | Alternatively, the using new metaprogramming features of Scala 3 - | could help avoid the need for runtime type information, see - | https://docs.scala-lang.org/scala3/reference/metaprogramming.html + | Compiler synthesis of Manifest and OptManifest is deprecated, instead + | replace with the type `scala.reflect.ClassTag[List[? <: Int]]`. + | Alternatively, consider using the new metaprogramming features of Scala 3, + | see https://docs.scala-lang.org/scala3/reference/metaprogramming.html -- Error: tests/neg-custom-args/deprecation/manifest-summoning.scala:2:41 ---------------------------------------------- 2 |val bar = optManifest[Array[? <: String]] // error | ^ - | Compiler synthesis of Manifest and OptManifest is deprecated, - | and calls should be replaced by `reflect.classTag[Array[? <: String]]`. - | Alternatively, the using new metaprogramming features of Scala 3 - | could help avoid the need for runtime type information, see - | https://docs.scala-lang.org/scala3/reference/metaprogramming.html + | Compiler synthesis of Manifest and OptManifest is deprecated, instead + | replace with the type `scala.reflect.ClassTag[Array[? <: String]]`. + | Alternatively, consider using the new metaprogramming features of Scala 3, + | see https://docs.scala-lang.org/scala3/reference/metaprogramming.html