-
Notifications
You must be signed in to change notification settings - Fork 1.1k
TypeTestCasts fixes and InterceptedMethods transformer #103
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
Merged
DarkDimius
merged 2 commits into
scala:master
from
DarkDimius:transform/erasure-transforms
Apr 2, 2014
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
src/dotty/tools/dotc/transform/InterceptedMethods.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package dotty.tools.dotc | ||
package transform | ||
|
||
import TreeTransforms._ | ||
import core.DenotTransformers._ | ||
import core.Denotations._ | ||
import core.SymDenotations._ | ||
import core.Contexts._ | ||
import core.Types._ | ||
import ast.Trees._ | ||
import ast.tpd.{Apply, Tree, cpy} | ||
import dotty.tools.dotc.ast.tpd | ||
import scala.collection.mutable | ||
import dotty.tools.dotc._ | ||
import core._ | ||
import Contexts._ | ||
import Symbols._ | ||
import Decorators._ | ||
import NameOps._ | ||
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, TreeTransform} | ||
import dotty.tools.dotc.ast.Trees._ | ||
import dotty.tools.dotc.ast.{untpd, tpd} | ||
import dotty.tools.dotc.core.Constants.Constant | ||
import dotty.tools.dotc.core.Types.MethodType | ||
import dotty.tools.dotc.core.Names.Name | ||
import dotty.runtime.LazyVals | ||
import scala.collection.mutable.ListBuffer | ||
import dotty.tools.dotc.core.Denotations.SingleDenotation | ||
import dotty.tools.dotc.core.SymDenotations.SymDenotation | ||
import dotty.tools.dotc.core.DenotTransformers.DenotTransformer | ||
import StdNames._ | ||
|
||
/** Replace member references as follows: | ||
* | ||
* - `x == y` for == in class Any becomes `x equals y` with equals in class Object. | ||
* - `x != y` for != in class Any becomes `!(x equals y)` with equals in class Object. | ||
* - `x.##` for ## in other classes becomes calls to ScalaRunTime.hash, | ||
* using the most precise overload available | ||
* - `x.getClass` for getClass in primitives becomes `x.getClass` with getClass in class Object. | ||
*/ | ||
class InterceptedMethods extends TreeTransform { | ||
|
||
import tpd._ | ||
|
||
override def name: String = "intercepted" | ||
|
||
private var getClassMethods: Set[Symbol] = _ | ||
private var poundPoundMethods: Set[Symbol] = _ | ||
private var Any_comparisons: Set[Symbol] = _ | ||
private var interceptedMethods: Set[Symbol] = _ | ||
private var primitiveGetClassMethods: Set[Symbol] = _ | ||
|
||
/** perform context-dependant initialization */ | ||
override def init(implicit ctx: Context, info: TransformerInfo): Unit = { | ||
getClassMethods = Set(defn.Any_getClass, defn.AnyVal_getClass) | ||
poundPoundMethods = Set(defn.Any_##, defn.Object_##) | ||
Any_comparisons = Set(defn.Any_==, defn.Any_!=) | ||
interceptedMethods = getClassMethods ++ poundPoundMethods ++ Any_comparisons | ||
primitiveGetClassMethods = Set[Symbol](defn.Any_getClass, defn.AnyVal_getClass) ++ | ||
defn.ScalaValueClasses.map(x => x.requiredMethod(nme.getClass_)) | ||
} | ||
|
||
// this should be removed if we have guarantee that ## will get Apply node | ||
override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): Tree = { | ||
if (tree.symbol.isTerm && poundPoundMethods.contains(tree.symbol.asTerm)) { | ||
val rewrite = PoundPoundValue(tree.qualifier) | ||
ctx.log(s"$name rewrote $tree to $rewrite") | ||
rewrite | ||
} | ||
else tree | ||
} | ||
|
||
private def PoundPoundValue(tree: Tree)(implicit ctx: Context) = { | ||
val s = tree.tpe.widen.typeSymbol | ||
if (s == defn.NullClass) Literal(Constant(0)) | ||
else { | ||
// Since we are past typer, we need to avoid creating trees carrying | ||
// overloaded types. This logic is custom (and technically incomplete, | ||
// although serviceable) for def hash. What is really needed is for | ||
// the overloading logic presently hidden away in a few different | ||
// places to be properly exposed so we can just call "resolveOverload" | ||
// after typer. Until then: | ||
|
||
def alts = defn.ScalaRuntimeModule.info.member(nme.hash_) | ||
|
||
// if tpe is a primitive value type, alt1 will match on the exact value, | ||
// taking in account that null.asInstanceOf[Int] == 0 | ||
def alt1 = alts.suchThat(_.info.firstParamTypes.head =:= tree.tpe.widen) | ||
|
||
// otherwise alt2 will match. alt2 also knows how to handle 'null' runtime value | ||
def alt2 = defn.ScalaRuntimeModule.info.member(nme.hash_) | ||
.suchThat(_.info.firstParamTypes.head.typeSymbol == defn.AnyClass) | ||
|
||
if (defn.ScalaNumericValueClasses contains s) { | ||
tpd.Apply(Ident(alt1.termRef), List(tree)) | ||
} else tpd.Apply(Ident(alt2.termRef), List(tree)) | ||
} | ||
} | ||
|
||
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { | ||
def unknown = { | ||
assert(false, s"The symbol '${tree.fun.symbol}' was interecepted but didn't match any cases, " + | ||
s"that means the intercepted methods set doesn't match the code") | ||
tree | ||
} | ||
if (tree.fun.symbol.isTerm && tree.args.isEmpty && | ||
(interceptedMethods contains tree.fun.symbol.asTerm)) { | ||
val rewrite: Tree = tree.fun match { | ||
case Select(qual, name) => | ||
if (poundPoundMethods contains tree.fun.symbol.asTerm) { | ||
PoundPoundValue(qual) | ||
} else if (Any_comparisons contains tree.fun.symbol.asTerm) { | ||
if (tree.fun.symbol eq defn.Any_==) { | ||
Apply(Select(qual, defn.Object_equals.termRef), tree.args) | ||
} else if (tree.fun.symbol eq defn.Any_!=) { | ||
Select(Apply(Select(qual, defn.Object_equals.termRef), tree.args), defn.Boolean_!.termRef) | ||
} else unknown | ||
} /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { | ||
// todo: this is needed to support value classes | ||
// Rewrite 5.getClass to ScalaRunTime.anyValClass(5) | ||
global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, | ||
List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen)))) | ||
}*/ | ||
else if (primitiveGetClassMethods.contains(tree.fun.symbol)) { | ||
// if we got here then we're trying to send a primitive getClass method to either | ||
// a) an Any, in which cage Object_getClass works because Any erases to object. Or | ||
// | ||
// b) a non-primitive, e.g. because the qualifier's type is a refinement type where one parent | ||
// of the refinement is a primitive and another is AnyRef. In that case | ||
// we get a primitive form of _getClass trying to target a boxed value | ||
// so we need replace that method name with Object_getClass to get correct behavior. | ||
// See SI-5568. | ||
Apply(Select(qual, defn.Object_getClass.termRef), Nil) | ||
} else { | ||
unknown | ||
} | ||
case _ => | ||
unknown | ||
} | ||
ctx.log(s"$name rewrote $tree to $rewrite") | ||
rewrite | ||
} | ||
else tree | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
You might want to drop a short line, why this is the proper resolution. E.g.:
tpe
is a primitive value type,alt1
will match on the exact value.tpe
is aNumber
,alt1
will matchalt2
will matchIt's currently unclear to me if this does the correct thing when
tpe
isjava.lang.Integer
. (It should call the number overload, but will choose theAny
overload).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.
If it's an
java.lang.Integer
Any overload still needs to be called, asNumber
overload doesn't support null values.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.
Oh, I see. Could you put this into a comment, please?
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.
Done.