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..4b47c7c1f989 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -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): @@ -375,6 +376,113 @@ 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, @@ -382,7 +490,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/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 +}