diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 4899454a2f8b..1569d360d1ae 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -54,6 +54,7 @@ class Compiler { new InterceptedMethods, new Literalize, new Getters, + new ClassTags, new ElimByName, new ResolveSuper), List(new Erasure), diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 826cbe2c696b..14d6bedc75b4 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -149,8 +149,10 @@ object StdNames { final val Seq: N = "Seq" final val Symbol: N = "Symbol" final val ClassTag: N = "ClassTag" + final val classTag: N = "classTag" final val WeakTypeTag: N = "WeakTypeTag" final val TypeTag : N = "TypeTag" + final val typeTag: N = "typeTag" final val Expr: N = "Expr" final val String: N = "String" final val Annotation: N = "Annotation" diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 1ad718c292c2..0de515026403 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1346,7 +1346,7 @@ object SymDenotations { // than this is a scope that will eventually become decls of this symbol. // And this should only happen if this is first time the scope of symbol // is computed, ie symbol yet has no future. - assert(this.nextInRun == this) + assert(this.nextInRun.validFor.code <= this.validFor.code) scope case _ => unforcedDecls.openForMutations } @@ -1367,7 +1367,7 @@ object SymDenotations { /** Enter a symbol in given `scope` without potentially replacing the old copy. */ def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = { - require((sym.denot.flagsUNSAFE is Private) || !(this is Frozen)) + require((sym.denot.flagsUNSAFE is Private) || !(this is Frozen) || (scope ne this.unforcedDecls)) scope.enter(sym) if (myMemberFingerPrint != FingerPrint.unknown) diff --git a/src/dotty/tools/dotc/transform/ClassTags.scala b/src/dotty/tools/dotc/transform/ClassTags.scala new file mode 100644 index 000000000000..9d8abae93035 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ClassTags.scala @@ -0,0 +1,65 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import StdNames._ +import NameOps._ +import ast.Trees._ +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Constants.Constant +import util.Positions._ +import Names._ +import collection.mutable + +/** This phase replaces calls to DottyPredef.classTag by code that synthesizes appropriate ClassTag + */ +class ClassTags extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + private var classTagCache: Symbol = null + private var typeTagCache: Symbol = null + private var scala2ClassTagModule: Symbol = null + + + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { + val predefClass = defn.DottyPredefModule.moduleClass.asClass + classTagCache = ctx.requiredMethod(predefClass, nme.classTag) + typeTagCache = ctx.requiredMethod(predefClass, nme.typeTag) + scala2ClassTagModule = ctx.requiredModule("scala.reflect.ClassTag") + this + } + + override def phaseName: String = "classTags" + + override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = + if (tree.fun.symbol eq classTagCache) { + val tp = tree.args.head.tpe + val defn = ctx.definitions + val (elemType, ndims) = tp match { + case defn.MultiArrayType(elem, ndims) => (elem, ndims) + case _ => (tp, 0) + } + + val claz = tp.classSymbol + val elemClaz = elemType.classSymbol + assert(!claz.isPrimitiveValueClass) // should be inserted by typer + val elemTag = if (defn.ScalaValueClasses.contains(elemClaz) || elemClaz == defn.NothingClass || elemClaz == defn.NullClass) + ref(defn.DottyPredefModule).select(s"${elemClaz.name}ClassTag".toTermName) + else if (ValueClasses.isDerivedValueClass(elemClaz)) ref(claz.companionModule) + else if (elemClaz eq defn.AnyClass) ref(scala2ClassTagModule).select(nme.Any) + else { + val erazedTp = TypeErasure.erasure(elemType).classSymbol.typeRef + ref(scala2ClassTagModule).select(nme.apply).appliedToType(erazedTp).appliedTo(Literal(Constant(erazedTp))) + } + (1 to ndims).foldLeft(elemTag)((arr, level) => Select(arr, nme.wrap).ensureApplied).ensureConforms(tree.tpe) + } else tree +} diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 009257dc06ee..9c7b7ebda5cc 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -46,35 +46,56 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful override def runsAfterGroupsOf = Set(classOf[FirstTransform]) // need companion objects to exist override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { - case ref: ClassDenotation if ref is ModuleClass => - ref.linkedClass match { - case origClass: ClassSymbol if isDerivedValueClass(origClass) => - val cinfo = ref.classInfo + case moduleClassSym: ClassDenotation if moduleClassSym is ModuleClass => + moduleClassSym.linkedClass match { + case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => + val cinfo = moduleClassSym.classInfo val decls1 = cinfo.decls.cloneScope + val moduleSym = moduleClassSym.symbol.asClass + + var newSuperClass: Type = null + ctx.atPhase(thisTransformer.next) { implicit ctx => // In Scala 2, extension methods are added before pickling so we should // not generate them again. - if (!(origClass is Scala2x)) ctx.atPhase(thisTransformer) { implicit ctx => - for (decl <- origClass.classInfo.decls) { + if (!(valueClass is Scala2x)) ctx.atPhase(thisTransformer) { implicit ctx => + for (decl <- valueClass.classInfo.decls) { if (isMethodWithExtension(decl)) - decls1.enter(createExtensionMethod(decl, ref.symbol)) + decls1.enter(createExtensionMethod(decl, moduleClassSym.symbol)) } } - val sym = ref.symbol - val underlying = erasure(underlyingOfValueClass(origClass)) - val evt = ErasedValueType(origClass, underlying) - val u2evtSym = ctx.newSymbol(sym, nme.U2EVT, Synthetic | Method, + val underlying = erasure(underlyingOfValueClass(valueClass)) + val evt = ErasedValueType(valueClass, underlying) + val u2evtSym = ctx.newSymbol(moduleSym, nme.U2EVT, Synthetic | Method, MethodType(List(nme.x_0), List(underlying), evt)) - val evt2uSym = ctx.newSymbol(sym, nme.EVT2U, Synthetic | Method, + val evt2uSym = ctx.newSymbol(moduleSym, nme.EVT2U, Synthetic | Method, MethodType(List(nme.x_0), List(evt), underlying)) + + val defn = ctx.definitions + + val underlyingCls = underlying.classSymbol + val underlyingClsName = + if (defn.ScalaNumericValueClasses.contains(underlyingCls) || + underlyingCls == defn.BooleanClass) underlyingCls.name else nme.Object + + + val syp = ctx.requiredClass(s"dotty.runtime.vc.VC${underlyingClsName}Companion").asClass + + newSuperClass = tpd.ref(syp).select(nme.CONSTRUCTOR).appliedToType(valueClass.typeRef).tpe.resultType + decls1.enter(u2evtSym) decls1.enter(evt2uSym) } - if (decls1.isEmpty) ref - else ref.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1)) + + // add a VCXXXCompanion superclass + + moduleClassSym.copySymDenotation(info = + cinfo.derivedClassInfo( + classParents = ctx.normalizeToClassRefs(List(newSuperClass), moduleSym, decls1), + decls = decls1)) case _ => - ref + moduleClassSym } case ref: SymDenotation if isMethodWithExtension(ref) && ref.hasAnnotation(defn.TailrecAnnotationClass) =>