Skip to content

Commit dac1488

Browse files
committed
Fix for SI-6731, dropped trees in selectDynamic.
I rewrote mkInvoke entirely, and boosted the test coverage.
1 parent 4c3aaad commit dac1488

File tree

3 files changed

+221
-47
lines changed

3 files changed

+221
-47
lines changed

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3926,62 +3926,53 @@ trait Typers extends Modes with Adaptations with Tags {
39263926
*
39273927
*/
39283928
def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = {
3929-
debuglog(s"mkInvoke($cxTree, $tree, $qual, $name)")
3929+
log(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)")
3930+
val treeSelection = treeInfo.methPart(tree)
3931+
def isDesugaredApply = treeSelection match {
3932+
case Select(`qual`, nme.apply) => true
3933+
case _ => false
3934+
}
39303935
acceptsApplyDynamicWithType(qual, name) map { tp =>
3931-
// tp eq NoType => can call xxxDynamic, but not passing any type args (unless specified explicitly by the user)
3932-
// in scala-virtualized, when not NoType, tp is passed as type argument (for selection on a staged Struct)
3933-
3934-
// strip off type application -- we're not doing much with outer,
3935-
// so don't bother preserving cxTree's attributes etc
3936-
val cxTree1 = cxTree match {
3937-
case t: ValOrDefDef => t.rhs
3938-
case t => t
3939-
}
3940-
val cxTree2 = cxTree1 match {
3941-
case Typed(t, tpe) => t // ignore outer type annotation
3942-
case t => t
3943-
}
3944-
val (outer, explicitTargs) = cxTree2 match {
3945-
case TypeApply(fun, targs) => (fun, targs)
3946-
case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs)
3947-
case Select(TypeApply(fun, targs), nme) => (Select(fun, nme), targs)
3948-
case t => (t, Nil)
3949-
}
3950-
def hasNamedArg(as: List[Tree]) = as.collectFirst{case AssignOrNamedArg(lhs, rhs) =>}.nonEmpty
3951-
3952-
def desugaredApply = tree match {
3953-
case Select(`qual`, nme.apply) => true
3954-
case _ => false
3936+
// If tp == NoType, pass only explicit type arguments to applyXXX. Not used at all
3937+
// here - it is for scala-virtualized, where tp will be passed as an argument (for
3938+
// selection on a staged Struct)
3939+
def hasNamed(args: List[Tree]): Boolean = args exists (_.isInstanceOf[AssignOrNamedArg])
3940+
// not supported: foo.bar(a1,..., an: _*)
3941+
def hasStar(args: List[Tree]) = treeInfo.isWildcardStarArgList(args)
3942+
def applyOp(args: List[Tree]) = if (hasNamed(args)) nme.applyDynamicNamed else nme.applyDynamic
3943+
def matches(t: Tree) = isDesugaredApply || treeInfo.methPart(t) == treeSelection
3944+
3945+
/** Note that the trees which arrive here are potentially some distance from
3946+
* the trees of direct interest. `cxTree` is some enclosing expression which
3947+
* may apparently be arbitrarily larger than `tree`; and `tree` itself is
3948+
* too small, having at least in some cases lost its explicit type parameters.
3949+
* This logic is designed to use `tree` to pinpoint the immediately surrounding
3950+
* Apply/TypeApply/Select node, and only then creates the dynamic call.
3951+
* See SI-6731 among others.
3952+
*/
3953+
def findSelection(t: Tree): Option[(TermName, Tree)] = t match {
3954+
case Apply(fn, args) if hasStar(args) => DynamicVarArgUnsupported(tree, applyOp(args)) ; None
3955+
case Apply(fn, args) if matches(fn) => Some((applyOp(args), fn))
3956+
case Assign(lhs, _) if matches(lhs) => Some((nme.updateDynamic, lhs))
3957+
case _ if matches(t) => Some((nme.selectDynamic, t))
3958+
case _ => t.children flatMap findSelection headOption
39553959
}
3956-
// note: context.tree includes at most one Apply node
3957-
// thus, we can't use it to detect we're going to receive named args in expressions such as:
3958-
// qual.sel(a)(a2, arg2 = "a2")
3959-
val oper = outer match {
3960-
case Apply(q, as) if q == tree || desugaredApply =>
3961-
val oper =
3962-
if (hasNamedArg(as)) nme.applyDynamicNamed
3963-
else nme.applyDynamic
3964-
// not supported: foo.bar(a1,..., an: _*)
3965-
if (treeInfo.isWildcardStarArgList(as)) {
3966-
DynamicVarArgUnsupported(tree, oper)
3967-
return Some(setError(tree))
3968-
} else oper
3969-
case Assign(`tree`, _) => nme.updateDynamic
3970-
case _ => nme.selectDynamic
3960+
findSelection(cxTree) match {
3961+
case Some((opName, tapply)) =>
3962+
val targs = treeInfo.typeArguments(tapply)
3963+
val fun = gen.mkTypeApply(Select(qual, opName), targs)
3964+
atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil))
3965+
case _ =>
3966+
setError(tree)
39713967
}
3972-
3973-
val dynSel = Select(qual, oper)
3974-
val tappSel = if (explicitTargs.nonEmpty) TypeApply(dynSel, explicitTargs) else dynSel
3975-
3976-
atPos(qual.pos)(Apply(tappSel, List(Literal(Constant(name.decode)))))
39773968
}
39783969
}
39793970

39803971
def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = {
39813972
silent(typeTree) match {
39823973
case SilentResultValue(r) => r
39833974
case SilentTypeError(err) => DynamicRewriteError(tree, err)
3984-
}
3975+
}
39853976
}
39863977
}
39873978

@@ -5375,7 +5366,7 @@ trait Typers extends Modes with Adaptations with Tags {
53755366
case tt @ TypeTree() => tree setOriginal tt.original
53765367
case _ => tree
53775368
}
5378-
}
5369+
}
53795370
else
53805371
// we should get here only when something before failed
53815372
// and we try again (@see tryTypedApply). In that case we can assign

test/files/run/t6731.check

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Mono$.bar()
2+
Mono$.bar
3+
Mono$.bar()
4+
Mono$.bar
5+
Mono$.bar
6+
Mono$.baz
7+
Mono$.bar(bippy=1, boppy=2)
8+
Mono$.baz
9+
Poly.bar[Nothing]
10+
Poly.bar[Int]
11+
Poly.bar[Nothing]()
12+
Poly.bar[Int]()
13+
Poly.bar[Int](1, 2, 3)
14+
Poly.bar[Nothing]
15+
Poly.bar[Int]
16+
Poly.bar[Nothing]()
17+
Poly.bar[Int]()
18+
Poly.bar[Int](1, 2, 3)
19+
Updating.bar
20+
Updating.bar = b
21+
Nest1$Nest2$Nest3$.bippy(1, 2, 3)
22+
Nest1$Nest2$Nest3$.bippy
23+
Named.bippy(a=1, b=2)
24+
Named.boppy(c=3, d=4)
25+
Named.apply()
26+
Named.apply()
27+
Named.apply(e=5, f=6)
28+
Named2.bippy(1)(q0, c)
29+
Named2.bippy(1)(q0, c)
30+
Named2.bippy(1)(b, q0)
31+
Named2.bippy(1)(q0, c)
32+
Named2.bippy(1)(c, b)
33+
Named2.bippy(1)(b, c)
34+
Named2.bippy(1)(q0, c)
35+
Named2.bippy(2)(b, c)
36+
Named2.bippy(1)(q0, c)
37+
Named2.bippy(5)(b, c)
38+
Named2.dingus(100)(b, dong)
39+
Named2.bippy(1)(q0, q1)
40+
Named2.hello(100)(!!, !!)

test/files/run/t6731.scala

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import scala.language.dynamics
2+
import scala.reflect.{ ClassTag, classTag }
3+
4+
object Util {
5+
def show[T](x: T): T = { println(x) ; x }
6+
def mkArgs(xs: Any*) = xs map { case ((k, v)) => k + "=" + v ; case x => "" + x } mkString ("(", ", ", ")")
7+
}
8+
import Util._
9+
10+
abstract class MonoDynamic extends Dynamic {
11+
def selectDynamic(name: String): String = show(this + "." + name)
12+
def applyDynamic(name: String)(args: Any*): String = show(this + "." + name + mkArgs(args: _*))
13+
def applyDynamicNamed(name: String)(args: (String, Any)*): String = show(this + "." + name + mkArgs(args: _*))
14+
15+
override def toString = this.getClass.getName split '.' last
16+
}
17+
18+
object Mono extends MonoDynamic {
19+
def f(s: String): String = s
20+
21+
def f1 = this.bar()
22+
def f2 = this.bar
23+
def f3 = f(this.bar())
24+
def f4 = f(this.bar)
25+
def f5 = f(f(f(f(f(f(this.bar)))))) + f(f(f(f(f(f(this.baz))))))
26+
def f6 = f(f(f(f(f(f(this.bar(bippy = 1, boppy = 2))))))) + f(f(f(f(f(f(this.baz))))))
27+
}
28+
29+
object Poly extends Dynamic {
30+
def selectDynamic[T: ClassTag](name: String): String = show(s"$this.$name[${classTag[T]}]")
31+
def applyDynamic[T: ClassTag](name: String)(args: Any*): String = show(args.mkString(s"$this.$name[${classTag[T]}](", ", ", ")"))
32+
33+
def f(s: String): String = s
34+
35+
def f1 = this.bar
36+
def f2 = this.bar[Int]
37+
def f3 = this.bar()
38+
def f4 = this.bar[Int]()
39+
def f5 = this.bar[Int](1, 2, 3)
40+
41+
def f6 = f(f(this.bar))
42+
def f7 = f(f(this.bar[Int]))
43+
def f8 = f(f(this.bar()))
44+
def f9 = f(f(this.bar[Int]()))
45+
def f10 = f(f(this.bar[Int](1, 2, 3)))
46+
47+
override def toString = "Poly"
48+
}
49+
50+
object Updating extends Dynamic {
51+
def selectDynamic(name: String): String = show(s"$this.$name")
52+
def updateDynamic(name: String)(value: Any): String = show(s"$this.$name = $value")
53+
54+
def f1 = this.bar
55+
def f2 = this.bar = "b"
56+
57+
override def toString = "Updating"
58+
}
59+
60+
object Nest1 extends Dynamic {
61+
def applyDynamic(name: String)(args: Any*): Nest2.type = Nest2
62+
63+
object Nest2 extends Dynamic {
64+
def applyDynamicNamed(name: String)(args: (String, Any)*): Nest3.type = Nest3
65+
66+
object Nest3 extends MonoDynamic {
67+
68+
}
69+
}
70+
71+
def f1 = Nest1.bip().bop(foo = "bar").bippy(1, 2, 3)
72+
def f2 = Nest1.bip("abc").bop(foo = 5).bippy
73+
}
74+
75+
object Named extends Dynamic {
76+
def applyDynamic(name: String)(args: Any*): Named.type = {
77+
show(this + "." + name + mkArgs(args: _*))
78+
this
79+
}
80+
def applyDynamicNamed(name: String)(args: (String, Any)*): Named.type = {
81+
show(this + "." + name + mkArgs(args: _*))
82+
this
83+
}
84+
85+
def f1 = this.bippy(a = 1, b = 2).boppy(c = 3, d = 4)()()(e = 5, f = 6)
86+
override def toString = "Named"
87+
}
88+
89+
object Named2 extends Dynamic {
90+
def applyDynamic(name: String)(a: Any)(b: Any = "b", c: Any = "c"): Named2.type = {
91+
show(this + "." + name + mkArgs(a) + mkArgs(b, c))
92+
this
93+
}
94+
def applyDynamicNamed(name: String)(a: (String, Any))(b: (String, Any), c: (String, Any)): Named2.type = {
95+
show(this + "." + name + mkArgs(a) + mkArgs(b, c))
96+
this
97+
}
98+
99+
def f1 = this.bippy(1)(b = "q0")
100+
def f2 = this.bippy(1)("q0")
101+
def f3 = this.bippy(1)(c = "q0")
102+
def f4 = this.bippy(1)("q0")
103+
def f5 = this.bippy(1)(c = "b", b = "c")
104+
def f6 = this.bippy(1)("b", "c")
105+
def f7 = this.bippy(1)(b = "q0").bippy(2)()
106+
def f8 = this.bippy(1)("q0").bippy(5)(c = "c").dingus(100)(c = "dong")
107+
def f9 = this.bippy(1)(b = "q0", c = "q1").hello(100)("!!", "!!")
108+
109+
override def toString = "Named2"
110+
}
111+
112+
113+
object Test {
114+
def main(args: Array[String]): Unit = {
115+
{
116+
import Mono._
117+
f1 ; f2 ; f3 ; f4 ; f5
118+
f6
119+
}
120+
{
121+
import Poly._
122+
f1 ; f2 ; f3 ; f4 ; f5
123+
f6 ; f7 ; f8 ; f9 ; f10
124+
}
125+
{
126+
import Updating._
127+
f1 ; f2
128+
}
129+
{
130+
import Nest1._
131+
f1 ; f2
132+
}
133+
{
134+
import Named._
135+
f1
136+
}
137+
{
138+
import Named2._
139+
f1 ; f2 ; f3 ; f4 ; f5
140+
f6 ; f7 ; f8 ; f9
141+
}
142+
}
143+
}

0 commit comments

Comments
 (0)