-
Notifications
You must be signed in to change notification settings - Fork 1.1k
ClassTags: New phase which synthesises class tags. #563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9c55450
0064286
bf134bc
d1bb4d3
fe903c0
d894335
db11aaa
b6bb034
a1fda63
736682e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I usually use myClassTag/myTypeTag for private variables, includes variables serving as a cache. |
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you intend for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The phases themselves are only stored in context. https://github.com/lampepfl/dotty/blob/master/src/dotty/tools/dotc/core/Phases.scala They are reallocated per-run. Currently, this will not stop GC. If it would, we can cleanup in |
||
} | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this do the right thing for:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to get There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for Array example, It's indeed not handled correctly. It seems that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On Thu, May 14, 2015 at 9:07 AM, Dmitry Petrashko [email protected]
Martin Odersky There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed. Thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Scala, give a value class So I think using Scala erasure might be a little bit too simple. You could instead use the java erasure, which, after my fix for @smarter's recent bug report, does not unwrap value classes. Here's a few test cases: object Test {
type T = String
type U
def test = {
val a /* : Class[T] */ = classOf[T] // [Ljava/lang/String;
val b /* : ClassTag[T] */ = reflect.classTag[T] // ClassTag(classOf[java.lang.String])
// val c /* : */ = classOf[T with U] // error: class type required but Test.T with Test.U found
val d /* : ClassTag[T with U] */ = reflect.classTag[T with U] // ClassTag(classOf[java.lang.String])
val e /* : Class[Array[T with U]] */ = classOf[Array[T with U]] // [Ljava/lang/String;
val f /* : ClassTag[Array[T with U]] */ = reflect.classTag[Array[T with U]] // ClassTag(arrayClass(classOf[java.lang.String]))
val g /* : Class[Meter] */ = classOf[Meter] // [LMeter;
val h /* : ClassTag[Meter] */ = reflect.classTag[Meter] // ClassTag(classOf[Meter])
}
}
class Meter(val i: Int) extends AnyVal There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's the code that backs the |
||
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make classTag, typeTag lazy vals? Seems overkill to use side-effects here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They depend on context to be initialized. Which context would those lazy vals use?