summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2012-12-05 06:52:40 -0800
committerAdriaan Moors <adriaan.moors@typesafe.com>2012-12-05 06:52:40 -0800
commitedbcc64483ac0009eeeb58fe31e57a411da9e4c6 (patch)
tree2e436216e6838dc5efe33c73c0669fb534235e3c
parentfd57069a3a49de1757a518b573a0cd8cb98bbbd5 (diff)
parenta6941944bf80f660722e9151801776715c3e4ab5 (diff)
downloadscala-edbcc64483ac0009eeeb58fe31e57a411da9e4c6.tar.gz
scala-edbcc64483ac0009eeeb58fe31e57a411da9e4c6.tar.bz2
scala-edbcc64483ac0009eeeb58fe31e57a411da9e4c6.zip
Merge pull request #1681 from paulp/issue/6731
Fix for SI-6731, dropped trees in selectDynamic.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala85
-rw-r--r--test/files/pos/t5726.scala17
-rw-r--r--test/files/pos/t6551.scala2
-rw-r--r--test/files/pos/t6722.scala11
-rw-r--r--test/files/run/t5733.check2
-rw-r--r--test/files/run/t5733.scala53
-rw-r--r--test/files/run/t6320.check17
-rw-r--r--test/files/run/t6320.scala9
-rw-r--r--test/files/run/t6731.check40
-rw-r--r--test/files/run/t6731.scala143
10 files changed, 331 insertions, 48 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 5714c2c109..ae506cd6ab 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3926,54 +3926,45 @@ trait Typers extends Modes with Adaptations with Tags {
*
*/
def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = {
- debuglog(s"mkInvoke($cxTree, $tree, $qual, $name)")
+ log(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)")
+ val treeSelection = treeInfo.methPart(tree)
+ def isDesugaredApply = treeSelection match {
+ case Select(`qual`, nme.apply) => true
+ case _ => false
+ }
acceptsApplyDynamicWithType(qual, name) map { tp =>
- // tp eq NoType => can call xxxDynamic, but not passing any type args (unless specified explicitly by the user)
- // in scala-virtualized, when not NoType, tp is passed as type argument (for selection on a staged Struct)
-
- // strip off type application -- we're not doing much with outer,
- // so don't bother preserving cxTree's attributes etc
- val cxTree1 = cxTree match {
- case t: ValOrDefDef => t.rhs
- case t => t
- }
- val cxTree2 = cxTree1 match {
- case Typed(t, tpe) => t // ignore outer type annotation
- case t => t
- }
- val (outer, explicitTargs) = cxTree2 match {
- case TypeApply(fun, targs) => (fun, targs)
- case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs)
- case Select(TypeApply(fun, targs), nme) => (Select(fun, nme), targs)
- case t => (t, Nil)
- }
- def hasNamedArg(as: List[Tree]) = as.collectFirst{case AssignOrNamedArg(lhs, rhs) =>}.nonEmpty
-
- def desugaredApply = tree match {
- case Select(`qual`, nme.apply) => true
- case _ => false
+ // If tp == NoType, pass only explicit type arguments to applyXXX. Not used at all
+ // here - it is for scala-virtualized, where tp will be passed as an argument (for
+ // selection on a staged Struct)
+ def hasNamed(args: List[Tree]): Boolean = args exists (_.isInstanceOf[AssignOrNamedArg])
+ // not supported: foo.bar(a1,..., an: _*)
+ def hasStar(args: List[Tree]) = treeInfo.isWildcardStarArgList(args)
+ def applyOp(args: List[Tree]) = if (hasNamed(args)) nme.applyDynamicNamed else nme.applyDynamic
+ def matches(t: Tree) = isDesugaredApply || treeInfo.methPart(t) == treeSelection
+
+ /** Note that the trees which arrive here are potentially some distance from
+ * the trees of direct interest. `cxTree` is some enclosing expression which
+ * may apparently be arbitrarily larger than `tree`; and `tree` itself is
+ * too small, having at least in some cases lost its explicit type parameters.
+ * This logic is designed to use `tree` to pinpoint the immediately surrounding
+ * Apply/TypeApply/Select node, and only then creates the dynamic call.
+ * See SI-6731 among others.
+ */
+ def findSelection(t: Tree): Option[(TermName, Tree)] = t match {
+ case Apply(fn, args) if hasStar(args) => DynamicVarArgUnsupported(tree, applyOp(args)) ; None
+ case Apply(fn, args) if matches(fn) => Some((applyOp(args), fn))
+ case Assign(lhs, _) if matches(lhs) => Some((nme.updateDynamic, lhs))
+ case _ if matches(t) => Some((nme.selectDynamic, t))
+ case _ => t.children flatMap findSelection headOption
}
- // note: context.tree includes at most one Apply node
- // thus, we can't use it to detect we're going to receive named args in expressions such as:
- // qual.sel(a)(a2, arg2 = "a2")
- val oper = outer match {
- case Apply(q, as) if q == tree || desugaredApply =>
- val oper =
- if (hasNamedArg(as)) nme.applyDynamicNamed
- else nme.applyDynamic
- // not supported: foo.bar(a1,..., an: _*)
- if (treeInfo.isWildcardStarArgList(as)) {
- DynamicVarArgUnsupported(tree, oper)
- return Some(setError(tree))
- } else oper
- case Assign(`tree`, _) => nme.updateDynamic
- case _ => nme.selectDynamic
+ findSelection(cxTree) match {
+ case Some((opName, tapply)) =>
+ val targs = treeInfo.typeArguments(tapply)
+ val fun = gen.mkTypeApply(Select(qual, opName), targs)
+ atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil))
+ case _ =>
+ setError(tree)
}
-
- val dynSel = Select(qual, oper)
- val tappSel = if (explicitTargs.nonEmpty) TypeApply(dynSel, explicitTargs) else dynSel
-
- atPos(qual.pos)(Apply(tappSel, List(Literal(Constant(name.decode)))))
}
}
@@ -3981,7 +3972,7 @@ trait Typers extends Modes with Adaptations with Tags {
silent(typeTree) match {
case SilentResultValue(r) => r
case SilentTypeError(err) => DynamicRewriteError(tree, err)
- }
+ }
}
}
@@ -5375,7 +5366,7 @@ trait Typers extends Modes with Adaptations with Tags {
case tt @ TypeTree() => tree setOriginal tt.original
case _ => tree
}
- }
+ }
else
// we should get here only when something before failed
// and we try again (@see tryTypedApply). In that case we can assign
diff --git a/test/files/pos/t5726.scala b/test/files/pos/t5726.scala
new file mode 100644
index 0000000000..b28ebd8674
--- /dev/null
+++ b/test/files/pos/t5726.scala
@@ -0,0 +1,17 @@
+import scala.language.dynamics
+
+class DynamicTest extends Dynamic {
+ def selectDynamic(name: String) = s"value of $name"
+ def updateDynamic(name: String)(value: Any) {
+ println(s"You have just updated property '$name' with value: $value")
+ }
+}
+
+object MyApp extends App {
+ def testing() {
+ val test = new DynamicTest
+ test.firstName = "John"
+ }
+
+ testing()
+}
diff --git a/test/files/pos/t6551.scala b/test/files/pos/t6551.scala
index fb68663809..8bb396a19f 100644
--- a/test/files/pos/t6551.scala
+++ b/test/files/pos/t6551.scala
@@ -1,4 +1,4 @@
-import language.dynamics
+import scala.language.dynamics
object Test {
def main(args: Array[String]) {
diff --git a/test/files/pos/t6722.scala b/test/files/pos/t6722.scala
new file mode 100644
index 0000000000..576746c915
--- /dev/null
+++ b/test/files/pos/t6722.scala
@@ -0,0 +1,11 @@
+import scala.language.dynamics
+
+class Dyn extends Dynamic {
+ def selectDynamic(s: String): Dyn = new Dyn
+ def get[T]: T = null.asInstanceOf[T]
+}
+
+object Foo {
+ val dyn = new Dyn
+ dyn.foo.bar.baz.get[String]
+}
diff --git a/test/files/run/t5733.check b/test/files/run/t5733.check
new file mode 100644
index 0000000000..e697046a94
--- /dev/null
+++ b/test/files/run/t5733.check
@@ -0,0 +1,2 @@
+Running ABTest asserts
+Done
diff --git a/test/files/run/t5733.scala b/test/files/run/t5733.scala
new file mode 100644
index 0000000000..360264e4ed
--- /dev/null
+++ b/test/files/run/t5733.scala
@@ -0,0 +1,53 @@
+import scala.language.dynamics
+
+object A extends Dynamic {
+ var a = "a"
+
+ def selectDynamic(method:String): String = a
+
+ def updateDynamic(method:String)(v:String) { a = v }
+}
+
+class B extends Dynamic {
+ var b = "b"
+
+ def selectDynamic(method:String): String = b
+
+ def updateDynamic(method:String)(v:String) { b = v }
+}
+
+object Test extends App {
+ assert( A.foo == "a" )
+ assert( A.bar == "a" )
+ A.aaa = "aaa"
+ assert( A.bar == "aaa" )
+
+ val b = new B
+ assert( b.foo == "b" )
+ assert( b.bar == "b" )
+ b.bbb = "bbb"
+ assert( b.bar == "bbb" )
+
+ {
+ println("Running ABTest asserts")
+ A.a = "a"
+ (new ABTest).test()
+ }
+
+ println("Done")
+}
+
+class ABTest {
+ def test() {
+ assert( A.foo == "a" )
+ assert( A.bar == "a" )
+ A.aaa = "aaa"
+ assert( A.bar == "aaa" )
+
+ val b = new B
+ assert( b.foo == "b" )
+ assert( b.bar == "b" )
+ b.bbb = "bbb"
+ assert( b.bar == "bbb" )
+ }
+}
diff --git a/test/files/run/t6320.check b/test/files/run/t6320.check
new file mode 100644
index 0000000000..e56bacd223
--- /dev/null
+++ b/test/files/run/t6320.check
@@ -0,0 +1,17 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> import scala.language.dynamics
+import scala.language.dynamics
+
+scala> class Dyn(m: Map[String, Any]) extends Dynamic { def selectDynamic[T](s: String): T = m(s).asInstanceOf[T] }
+defined class Dyn
+
+scala> new Dyn(Map("foo" -> 10)).foo[Int]
+res0: Int = 10
+
+scala>
+
+scala>
diff --git a/test/files/run/t6320.scala b/test/files/run/t6320.scala
new file mode 100644
index 0000000000..26085a3d7d
--- /dev/null
+++ b/test/files/run/t6320.scala
@@ -0,0 +1,9 @@
+import scala.tools.partest.ReplTest
+
+object Test extends ReplTest {
+ def code = """
+import scala.language.dynamics
+class Dyn(m: Map[String, Any]) extends Dynamic { def selectDynamic[T](s: String): T = m(s).asInstanceOf[T] }
+new Dyn(Map("foo" -> 10)).foo[Int]
+ """
+}
diff --git a/test/files/run/t6731.check b/test/files/run/t6731.check
new file mode 100644
index 0000000000..a5d59bd378
--- /dev/null
+++ b/test/files/run/t6731.check
@@ -0,0 +1,40 @@
+Mono$.bar()
+Mono$.bar
+Mono$.bar()
+Mono$.bar
+Mono$.bar
+Mono$.baz
+Mono$.bar(bippy=1, boppy=2)
+Mono$.baz
+Poly.bar[Nothing]
+Poly.bar[Int]
+Poly.bar[Nothing]()
+Poly.bar[Int]()
+Poly.bar[Int](1, 2, 3)
+Poly.bar[Nothing]
+Poly.bar[Int]
+Poly.bar[Nothing]()
+Poly.bar[Int]()
+Poly.bar[Int](1, 2, 3)
+Updating.bar
+Updating.bar = b
+Nest1$Nest2$Nest3$.bippy(1, 2, 3)
+Nest1$Nest2$Nest3$.bippy
+Named.bippy(a=1, b=2)
+Named.boppy(c=3, d=4)
+Named.apply()
+Named.apply()
+Named.apply(e=5, f=6)
+Named2.bippy(1)(q0, c)
+Named2.bippy(1)(q0, c)
+Named2.bippy(1)(b, q0)
+Named2.bippy(1)(q0, c)
+Named2.bippy(1)(c, b)
+Named2.bippy(1)(b, c)
+Named2.bippy(1)(q0, c)
+Named2.bippy(2)(b, c)
+Named2.bippy(1)(q0, c)
+Named2.bippy(5)(b, c)
+Named2.dingus(100)(b, dong)
+Named2.bippy(1)(q0, q1)
+Named2.hello(100)(!!, !!)
diff --git a/test/files/run/t6731.scala b/test/files/run/t6731.scala
new file mode 100644
index 0000000000..89d212e91e
--- /dev/null
+++ b/test/files/run/t6731.scala
@@ -0,0 +1,143 @@
+import scala.language.dynamics
+import scala.reflect.{ ClassTag, classTag }
+
+object Util {
+ def show[T](x: T): T = { println(x) ; x }
+ def mkArgs(xs: Any*) = xs map { case ((k, v)) => k + "=" + v ; case x => "" + x } mkString ("(", ", ", ")")
+}
+import Util._
+
+abstract class MonoDynamic extends Dynamic {
+ def selectDynamic(name: String): String = show(this + "." + name)
+ def applyDynamic(name: String)(args: Any*): String = show(this + "." + name + mkArgs(args: _*))
+ def applyDynamicNamed(name: String)(args: (String, Any)*): String = show(this + "." + name + mkArgs(args: _*))
+
+ override def toString = this.getClass.getName split '.' last
+}
+
+object Mono extends MonoDynamic {
+ def f(s: String): String = s
+
+ def f1 = this.bar()
+ def f2 = this.bar
+ def f3 = f(this.bar())
+ def f4 = f(this.bar)
+ def f5 = f(f(f(f(f(f(this.bar)))))) + f(f(f(f(f(f(this.baz))))))
+ def f6 = f(f(f(f(f(f(this.bar(bippy = 1, boppy = 2))))))) + f(f(f(f(f(f(this.baz))))))
+}
+
+object Poly extends Dynamic {
+ def selectDynamic[T: ClassTag](name: String): String = show(s"$this.$name[${classTag[T]}]")
+ def applyDynamic[T: ClassTag](name: String)(args: Any*): String = show(args.mkString(s"$this.$name[${classTag[T]}](", ", ", ")"))
+
+ def f(s: String): String = s
+
+ def f1 = this.bar
+ def f2 = this.bar[Int]
+ def f3 = this.bar()
+ def f4 = this.bar[Int]()
+ def f5 = this.bar[Int](1, 2, 3)
+
+ def f6 = f(f(this.bar))
+ def f7 = f(f(this.bar[Int]))
+ def f8 = f(f(this.bar()))
+ def f9 = f(f(this.bar[Int]()))
+ def f10 = f(f(this.bar[Int](1, 2, 3)))
+
+ override def toString = "Poly"
+}
+
+object Updating extends Dynamic {
+ def selectDynamic(name: String): String = show(s"$this.$name")
+ def updateDynamic(name: String)(value: Any): String = show(s"$this.$name = $value")
+
+ def f1 = this.bar
+ def f2 = this.bar = "b"
+
+ override def toString = "Updating"
+}
+
+object Nest1 extends Dynamic {
+ def applyDynamic(name: String)(args: Any*): Nest2.type = Nest2
+
+ object Nest2 extends Dynamic {
+ def applyDynamicNamed(name: String)(args: (String, Any)*): Nest3.type = Nest3
+
+ object Nest3 extends MonoDynamic {
+
+ }
+ }
+
+ def f1 = Nest1.bip().bop(foo = "bar").bippy(1, 2, 3)
+ def f2 = Nest1.bip("abc").bop(foo = 5).bippy
+}
+
+object Named extends Dynamic {
+ def applyDynamic(name: String)(args: Any*): Named.type = {
+ show(this + "." + name + mkArgs(args: _*))
+ this
+ }
+ def applyDynamicNamed(name: String)(args: (String, Any)*): Named.type = {
+ show(this + "." + name + mkArgs(args: _*))
+ this
+ }
+
+ def f1 = this.bippy(a = 1, b = 2).boppy(c = 3, d = 4)()()(e = 5, f = 6)
+ override def toString = "Named"
+}
+
+object Named2 extends Dynamic {
+ def applyDynamic(name: String)(a: Any)(b: Any = "b", c: Any = "c"): Named2.type = {
+ show(this + "." + name + mkArgs(a) + mkArgs(b, c))
+ this
+ }
+ def applyDynamicNamed(name: String)(a: (String, Any))(b: (String, Any), c: (String, Any)): Named2.type = {
+ show(this + "." + name + mkArgs(a) + mkArgs(b, c))
+ this
+ }
+
+ def f1 = this.bippy(1)(b = "q0")
+ def f2 = this.bippy(1)("q0")
+ def f3 = this.bippy(1)(c = "q0")
+ def f4 = this.bippy(1)("q0")
+ def f5 = this.bippy(1)(c = "b", b = "c")
+ def f6 = this.bippy(1)("b", "c")
+ def f7 = this.bippy(1)(b = "q0").bippy(2)()
+ def f8 = this.bippy(1)("q0").bippy(5)(c = "c").dingus(100)(c = "dong")
+ def f9 = this.bippy(1)(b = "q0", c = "q1").hello(100)("!!", "!!")
+
+ override def toString = "Named2"
+}
+
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ {
+ import Mono._
+ f1 ; f2 ; f3 ; f4 ; f5
+ f6
+ }
+ {
+ import Poly._
+ f1 ; f2 ; f3 ; f4 ; f5
+ f6 ; f7 ; f8 ; f9 ; f10
+ }
+ {
+ import Updating._
+ f1 ; f2
+ }
+ {
+ import Nest1._
+ f1 ; f2
+ }
+ {
+ import Named._
+ f1
+ }
+ {
+ import Named2._
+ f1 ; f2 ; f3 ; f4 ; f5
+ f6 ; f7 ; f8 ; f9
+ }
+ }
+}