From 9c55450be3514fd3c4741dd3a618ad24606792e5 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 13 May 2015 15:09:34 +0200 Subject: [PATCH 01/10] Rename variables in ExtensionMethods ref => moduleClass origClass => value class --- .../dotc/transform/ExtensionMethods.scala | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 009257dc06ee..0aaebbc2dec3 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -46,24 +46,24 @@ 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 moduleClass: ClassDenotation if moduleClass is ModuleClass => + moduleClass.linkedClass match { + case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => + val cinfo = moduleClass.classInfo val decls1 = cinfo.decls.cloneScope 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, moduleClass.symbol)) } } - val sym = ref.symbol - val underlying = erasure(underlyingOfValueClass(origClass)) - val evt = ErasedValueType(origClass, underlying) + val sym = moduleClass.symbol + val underlying = erasure(underlyingOfValueClass(valueClass)) + val evt = ErasedValueType(valueClass, underlying) val u2evtSym = ctx.newSymbol(sym, nme.U2EVT, Synthetic | Method, MethodType(List(nme.x_0), List(underlying), evt)) val evt2uSym = ctx.newSymbol(sym, nme.EVT2U, Synthetic | Method, @@ -71,10 +71,10 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful decls1.enter(u2evtSym) decls1.enter(evt2uSym) } - if (decls1.isEmpty) ref - else ref.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1)) + if (decls1.isEmpty) moduleClass + else moduleClass.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1)) case _ => - ref + moduleClass } case ref: SymDenotation if isMethodWithExtension(ref) && ref.hasAnnotation(defn.TailrecAnnotationClass) => From 0064286422fedefab88247f21ec48f68e95e001a Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 13 May 2015 15:49:51 +0200 Subject: [PATCH 02/10] Relax restriction on entering symbols into mutable scopes. The restriction here is used to make sure that the latest scope is being updated. Previous was to harsh and allowed only typer to call normalizeToClassRefs --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 1ad718c292c2..5d0b6470ea2c 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 } From bf134bcce241f42f2e5a7ad58f1125ad0b29d753 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 13 May 2015 17:17:02 +0200 Subject: [PATCH 03/10] Relax requirement in enterNoReplace Allows entering new symbols in future scope of a denotation. Only past scope is already frozen, and we should be free to modify future one. --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 5d0b6470ea2c..0de515026403 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -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) From d1bb4d333de08502e19db620adc1f92aaa9fca94 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 13 May 2015 17:17:37 +0200 Subject: [PATCH 04/10] ExtensionMethods: companions of value classes become class tags. --- .../dotc/transform/ExtensionMethods.scala | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 0aaebbc2dec3..c93e87cf4b2a 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -51,6 +51,10 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => val cinfo = moduleClass.classInfo val decls1 = cinfo.decls.cloneScope + val moduleSym = moduleClass.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. @@ -61,18 +65,41 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful } } - val sym = moduleClass.symbol val underlying = erasure(underlyingOfValueClass(valueClass)) val evt = ErasedValueType(valueClass, underlying) - val u2evtSym = ctx.newSymbol(sym, nme.U2EVT, Synthetic | Method, + 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 underlyingName = underlying.classSymbol match { + case defn.IntClass => nme.Int + case defn.ByteClass => nme.Byte + case defn.ShortClass => nme.Short + case defn.DoubleClass => nme.Double + case defn.FloatClass => nme.Float + case defn.LongClass => nme.Long + case defn.BooleanClass => nme.Boolean + case defn.CharClass => nme.Char + case _ => nme.Object + } + + val syp = ctx.requiredClass(s"dotty.runtime.vc.VC${underlyingName}Companion").asClass + + newSuperClass = tpd.ref(syp).select(nme.CONSTRUCTOR).appliedToType(valueClass.typeRef).tpe.resultType + decls1.enter(u2evtSym) decls1.enter(evt2uSym) } - if (decls1.isEmpty) moduleClass - else moduleClass.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1)) + + // add a VCXXXCompanion superclass + + moduleClass.copySymDenotation(info = + cinfo.derivedClassInfo( + classParents = ctx.normalizeToClassRefs(List(newSuperClass), moduleSym, decls1), + decls = decls1)) case _ => moduleClass } From fe903c00027f0440207dc70c8b1d50ef3d0b3701 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 13 May 2015 17:18:09 +0200 Subject: [PATCH 05/10] ClassTags: New phase which synthesises class tags. --- src/dotty/tools/dotc/Compiler.scala | 3 +- .../tools/dotc/transform/ClassTags.scala | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/dotty/tools/dotc/transform/ClassTags.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 4899454a2f8b..10f77353f524 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -55,7 +55,8 @@ class Compiler { new Literalize, new Getters, new ElimByName, - new ResolveSuper), + new ResolveSuper, + new ClassTags), List(new Erasure), List(new ElimErasedValueType, new VCInline, diff --git a/src/dotty/tools/dotc/transform/ClassTags.scala b/src/dotty/tools/dotc/transform/ClassTags.scala new file mode 100644 index 000000000000..0d33cc6eca3c --- /dev/null +++ b/src/dotty/tools/dotc/transform/ClassTags.scala @@ -0,0 +1,57 @@ +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._ + + object names { + val classTag = "classTag".toTermName + val typeTag = "typeTag".toTermName + } + + 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, names.classTag) + typeTagCache = ctx.requiredMethod(predefClass, names.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 claz = tp.classSymbol + assert(!claz.isPrimitiveValueClass) // should be inserted by typer + if (ValueClasses.isDerivedValueClass(claz)) ref(claz.companionModule) + else if (claz eq defn.AnyClass) ref(scala2ClassTagModule).select(nme.Any).ensureConforms(tree.tpe) + else ref(scala2ClassTagModule).select(nme.apply).appliedToType(tp).appliedTo(Literal(Constant(claz.typeRef))) + } else tree +} From d894335f03b667ce3d3f6c4a29bfe6fcdf6be7a6 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 15 May 2015 11:09:26 +0200 Subject: [PATCH 06/10] Move classTags before ElimByName. To have less interactions with erasure. --- src/dotty/tools/dotc/Compiler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 10f77353f524..1569d360d1ae 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -54,9 +54,9 @@ class Compiler { new InterceptedMethods, new Literalize, new Getters, + new ClassTags, new ElimByName, - new ResolveSuper, - new ClassTags), + new ResolveSuper), List(new Erasure), List(new ElimErasedValueType, new VCInline, From db11aaacfe01daff72732ba4588cb006512abcd2 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 15 May 2015 11:09:53 +0200 Subject: [PATCH 07/10] Move names from ClassTags to StdNames. --- src/dotty/tools/dotc/core/StdNames.scala | 2 ++ src/dotty/tools/dotc/transform/ClassTags.scala | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) 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/transform/ClassTags.scala b/src/dotty/tools/dotc/transform/ClassTags.scala index 0d33cc6eca3c..53adab2c361f 100644 --- a/src/dotty/tools/dotc/transform/ClassTags.scala +++ b/src/dotty/tools/dotc/transform/ClassTags.scala @@ -25,11 +25,6 @@ import collection.mutable class ClassTags extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => import ast.tpd._ - object names { - val classTag = "classTag".toTermName - val typeTag = "typeTag".toTermName - } - private var classTagCache: Symbol = null private var typeTagCache: Symbol = null private var scala2ClassTagModule: Symbol = null From b6bb0340fdc6b7b523b011940dc99cc899663368 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 15 May 2015 11:10:15 +0200 Subject: [PATCH 08/10] Remove duplicate spaces in ClassTags --- src/dotty/tools/dotc/transform/ClassTags.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ClassTags.scala b/src/dotty/tools/dotc/transform/ClassTags.scala index 53adab2c361f..a048cdfc98c8 100644 --- a/src/dotty/tools/dotc/transform/ClassTags.scala +++ b/src/dotty/tools/dotc/transform/ClassTags.scala @@ -32,8 +32,8 @@ class ClassTags extends MiniPhaseTransform with IdentityDenotTransformer { thisT override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { val predefClass = defn.DottyPredefModule.moduleClass.asClass - classTagCache = ctx.requiredMethod(predefClass, names.classTag) - typeTagCache = ctx.requiredMethod(predefClass, names.typeTag) + classTagCache = ctx.requiredMethod(predefClass, nme.classTag) + typeTagCache = ctx.requiredMethod(predefClass, nme.typeTag) scala2ClassTagModule = ctx.requiredModule("scala.reflect.ClassTag") this } From a1fda63908b24c0b5287c2a3425891d0add25026 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 15 May 2015 11:10:56 +0200 Subject: [PATCH 09/10] ClassTags: handle Or and AndTypes, and Arrays. --- .../tools/dotc/transform/ClassTags.scala | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ClassTags.scala b/src/dotty/tools/dotc/transform/ClassTags.scala index a048cdfc98c8..9d8abae93035 100644 --- a/src/dotty/tools/dotc/transform/ClassTags.scala +++ b/src/dotty/tools/dotc/transform/ClassTags.scala @@ -43,10 +43,23 @@ class ClassTags extends MiniPhaseTransform with IdentityDenotTransformer { thisT 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 - if (ValueClasses.isDerivedValueClass(claz)) ref(claz.companionModule) - else if (claz eq defn.AnyClass) ref(scala2ClassTagModule).select(nme.Any).ensureConforms(tree.tpe) - else ref(scala2ClassTagModule).select(nme.apply).appliedToType(tp).appliedTo(Literal(Constant(claz.typeRef))) + 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 } From 736682e306ae3c9cb48d83b2750e9d36dee4e9b5 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Fri, 15 May 2015 11:11:32 +0200 Subject: [PATCH 10/10] Extension methods: address reviewers comments #563 --- .../dotc/transform/ExtensionMethods.scala | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index c93e87cf4b2a..9c7b7ebda5cc 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -46,12 +46,12 @@ 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 moduleClass: ClassDenotation if moduleClass is ModuleClass => - moduleClass.linkedClass match { + case moduleClassSym: ClassDenotation if moduleClassSym is ModuleClass => + moduleClassSym.linkedClass match { case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => - val cinfo = moduleClass.classInfo + val cinfo = moduleClassSym.classInfo val decls1 = cinfo.decls.cloneScope - val moduleSym = moduleClass.symbol.asClass + val moduleSym = moduleClassSym.symbol.asClass var newSuperClass: Type = null @@ -61,7 +61,7 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful if (!(valueClass is Scala2x)) ctx.atPhase(thisTransformer) { implicit ctx => for (decl <- valueClass.classInfo.decls) { if (isMethodWithExtension(decl)) - decls1.enter(createExtensionMethod(decl, moduleClass.symbol)) + decls1.enter(createExtensionMethod(decl, moduleClassSym.symbol)) } } @@ -74,19 +74,13 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful val defn = ctx.definitions - val underlyingName = underlying.classSymbol match { - case defn.IntClass => nme.Int - case defn.ByteClass => nme.Byte - case defn.ShortClass => nme.Short - case defn.DoubleClass => nme.Double - case defn.FloatClass => nme.Float - case defn.LongClass => nme.Long - case defn.BooleanClass => nme.Boolean - case defn.CharClass => nme.Char - case _ => nme.Object - } + 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${underlyingName}Companion").asClass + val syp = ctx.requiredClass(s"dotty.runtime.vc.VC${underlyingClsName}Companion").asClass newSuperClass = tpd.ref(syp).select(nme.CONSTRUCTOR).appliedToType(valueClass.typeRef).tpe.resultType @@ -96,12 +90,12 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful // add a VCXXXCompanion superclass - moduleClass.copySymDenotation(info = + moduleClassSym.copySymDenotation(info = cinfo.derivedClassInfo( classParents = ctx.normalizeToClassRefs(List(newSuperClass), moduleSym, decls1), decls = decls1)) case _ => - moduleClass + moduleClassSym } case ref: SymDenotation if isMethodWithExtension(ref) && ref.hasAnnotation(defn.TailrecAnnotationClass) =>