Skip to content

Commit 2124b9d

Browse files
author
James Iry
committed
SI-6536 Generates super accessors X.super[Y].blah when Y is a class
The main change here is to add another case for generating super accessors - the case in X.super[Y].blah when X isn't the current class and Y is a class. The change is deliberately kept as minimal as possible to reduce the chance of breaking something in the 2.9.x line. Additionally GenICode now detects the case when we're trying to emit byte code that would be nonsense and warns about it. That can safely be made an assert for 2.11. Finally a related assert in RefChecks is beefed up to output a bit more useful information.
1 parent 3d2bcf2 commit 2124b9d

File tree

5 files changed

+347
-5
lines changed

5 files changed

+347
-5
lines changed

src/compiler/scala/tools/nsc/backend/icode/GenICode.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -767,11 +767,15 @@ abstract class GenICode extends SubComponent {
767767
// to call super constructors explicitly and/or use their 'returned' value.
768768
// therefore, we can ignore this fact, and generate code that leaves nothing
769769
// on the stack (contrary to what the type in the AST says).
770-
case Apply(fun @ Select(Super(_, mix), _), args) =>
770+
case Apply(fun @ Select(Super(qual, mix), _), args) =>
771+
772+
if (!qual.isInstanceOf[This]) {
773+
log("WARNING: super call where selector isn't 'this'. May generate invalid bytecode. Previous phases should have tansformed away this form of super.")
774+
}
771775
if (settings.debug.value)
772-
log("Call to super: " + tree);
776+
log("Call to super: " + tree)
777+
773778
val invokeStyle = SuperCall(mix)
774-
// if (fun.symbol.isConstructor) Static(true) else SuperCall(mix);
775779

776780
ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos)
777781
val ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1375,7 +1375,7 @@ abstract class RefChecks extends InfoTransform {
13751375

13761376
def checkSuper(mix: Name) =
13771377
// term should have been eliminated by super accessors
1378-
assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY))
1378+
assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY), "The following selector should have been transformed by SuperAccessors:" + tree)
13791379

13801380
transformCaseApply(tree,
13811381
qual match {

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
110110
val Select(sup @ Super(_, mix), name) = sel
111111
val sym = sel.symbol
112112
val clazz = sup.symbol
113+
val SuperType(_, mixtpe) = sup.tpe
113114

114115
if (sym.isDeferred) {
115116
val member = sym.overridingSymbol(clazz);
@@ -118,7 +119,20 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
118119
unit.error(sel.pos, ""+sym.fullLocationString+" is accessed from super. It may not be abstract "+
119120
"unless it is overridden by a member declared `abstract' and `override'");
120121
}
121-
if (name.isTermName && mix == tpnme.EMPTY && (clazz.isTrait || clazz != currentClass || !validCurrentOwner))
122+
123+
// we need an accessor to get to a super on an outer thing, but only if we can't call name more directly on
124+
// a trait implementation class. So this complicated condition is leaving alone cases where we don't need to do
125+
// anything special (i.e. we're getting a direct super class) or where a later transform will inject a call to
126+
// a trait implementation method directly.
127+
// So, we're looking for items of the form clazz.super[mix].name (or clazz.super.name wich is seen as clazz.super[EMPTY].name.
128+
// with some limitations. First, name has to be a term rather than a type.
129+
// Then there are a couple of cases. If mix is empty then we only need an accessor if clazz is a trait, it's not this current class,
130+
// or the validCurentOwner setting is false...which...ugh, is a mess.
131+
// If the mix is set then if it refers to a class and the clazz part isn't the current class
132+
// it's not just super[mix].name then we need to generate an accessor.
133+
// SI-6536 has more discussion about how this works.
134+
if (name.isTermName && (mix == tpnme.EMPTY && (clazz.isTrait || clazz != currentClass || !validCurrentOwner ))
135+
|| (mix != tpnme.EMPTY && !mixtpe.typeSymbol.isTrait && clazz != currentClass))
122136
ensureAccessor(sel)
123137
else sel
124138
}

test/files/run/t6536.check

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
a1 a2 a3_t o1/o1 o2 outertrait.Outer/o1 List(a1, o1)
2+
a1 a2 a3_c o1/o1 o2 outertrait.Outer/o1 List(a1, o1)
3+
a1 a2 a3_o o1/o1 o2 outertrait.Outer/o1 List(a1, o1)
4+
a1 a2 a3_tc o1/o1 o2 outertrait.Outer/o1 List(a1, o1)
5+
a1 a2 a3_to o1/o1 o2 outertrait.Outer/o1 List(a1, o1)
6+
a1 a2 a3_tco o1/o1 o2 outertrait.Outer/o1 List(a1, o1)
7+
8+
a1 a2 a3_t o1/o1 o2 outerclass.Outer/o1 List(a1, o1)
9+
a1 a2 a3_c o1/o1 o2 outerclass.Outer/o1 List(a1, o1)
10+
a1 a2 a3_o o1/o1 o2 outerclass.Outer/o1 List(a1, o1)
11+
a1 a2 a3_tc o1/o1 o2 outerclass.Outer/o1 List(a1, o1)
12+
a1 a2 a3_to o1/o1 o2 outerclass.Outer/o1 List(a1, o1)
13+
a1 a2 a3_tco o1/o1 o2 outerclass.Outer/o1 List(a1, o1)
14+
15+
a1 a2 a3_t o1/o1 o2 withinmethod.Outer/o1 List(a1, o1)
16+
a1 a2 a3_c o1/o1 o2 withinmethod.Outer/o1 List(a1, o1)
17+
a1 a2 a3_o o1/o1 o2 withinmethod.Outer/o1 List(a1, o1)
18+
a1 a2 a3_tc o1/o1 o2 withinmethod.Outer/o1 List(a1, o1)
19+
a1 a2 a3_to o1/o1 o2 withinmethod.Outer/o1 List(a1, o1)
20+
a1 a2 a3_tco o1/o1 o2 withinmethod.Outer/o1 List(a1, o1)
21+
child super class
22+
child super trait
23+
child super class 2
24+
child super trait 2
25+
child super class 3
26+
child super trait 3a
27+
child super trait 3a

test/files/run/t6536.scala

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
2+
// this was what was broken
3+
class AClass {
4+
def a = "super class"
5+
}
6+
class BClass extends AClass {
7+
class M {
8+
def z = "child " + BClass.super[AClass].a
9+
}
10+
override def a = ""
11+
}
12+
13+
// this variant would crash the compiler
14+
class O3 { def f = "" } // O3 must be a class
15+
class O4 extends O3 {
16+
class T1 { def g = O4.super[O3].f } // ok
17+
trait T2 { def g = O4.super[O3].f } // crash
18+
}
19+
20+
// make sure the fix didn't break this case, which wasn't broken
21+
trait ATrait {
22+
def a = "super trait"
23+
}
24+
class BTrait extends ATrait {
25+
class M {
26+
def z = "child " + BTrait.super[ATrait].a
27+
}
28+
override def a = ""
29+
}
30+
31+
// make sure the fix didn't break the simplest case
32+
class AClass2 {
33+
def a = "super class 2"
34+
}
35+
class BClass2 extends AClass2 {
36+
override def a = ""
37+
def z = "child " + super.a
38+
}
39+
40+
// make sure the fix didn't break this simplest case
41+
class ATrait2 {
42+
def a = "super trait 2"
43+
}
44+
class BTrait2 extends ATrait2 {
45+
override def a = ""
46+
def z = "child " + super.a
47+
}
48+
49+
// a more interesting example of the all that
50+
// this was what was broken
51+
class AClass3 {
52+
def a = "super class 3"
53+
}
54+
trait ATrait3a {
55+
def a = "super trait 3a"
56+
}
57+
trait ATrait3b {
58+
def a = "super trait 3b"
59+
}
60+
class BClass3 extends AClass3 with ATrait3a with ATrait3b {
61+
class M {
62+
def zclass = "child " + BClass3.super[AClass3].a
63+
def ztraita = "child " + BClass3.super[ATrait3a].a
64+
def ztraitb = "child " + BClass3.super[ATrait3a].a
65+
}
66+
override def a = ""
67+
}
68+
69+
// here's a case where we call super from a trait
70+
trait Root {
71+
def a = "root"
72+
}
73+
74+
trait Mid extends Root {
75+
override def a = "mid"
76+
def b = super.a
77+
}
78+
79+
class Bottom extends Mid
80+
81+
// and this is a bunch of other stuff we want to make sure doesn't explode
82+
trait A1 { def m1 = "a1" }
83+
trait A2 { def m1 = "a2" }
84+
85+
trait O1 { def m2 = "o1" ; def o1 = "o1" }
86+
trait O2 { def m2 = "o2" }
87+
88+
class C1 { def m3 = "c1" }
89+
trait C2 { def m3 = "c2" }
90+
91+
package outertrait {
92+
trait Outer extends O1 with O2 {
93+
override def m2 = "outertrait.Outer"
94+
95+
trait A3_T extends A1 with A2 {
96+
override def m1 = "a3_t"
97+
98+
def f1 = super[A1].m1
99+
def f2 = super[A2].m1
100+
def f3 = m1
101+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
102+
def f5 = Outer.super[O2].m2
103+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
104+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
105+
}
106+
107+
class A3_C extends A1 with A2 {
108+
override def m1 = "a3_c"
109+
110+
def f1 = super[A1].m1
111+
def f2 = super[A2].m1
112+
def f3 = m1
113+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
114+
def f5 = Outer.super[O2].m2
115+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
116+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
117+
}
118+
119+
object A3_O extends A1 with A2 {
120+
override def m1 = "a3_o"
121+
122+
def f1 = super[A1].m1
123+
def f2 = super[A2].m1
124+
def f3 = m1
125+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
126+
def f5 = Outer.super[O2].m2
127+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
128+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
129+
}
130+
131+
class A3_TC extends A3_T { override def m1 = "a3_tc" }
132+
object A3_TO extends A3_T { override def m1 = "a3_to" }
133+
object A3_TCO extends A3_TC { override def m1 = "a3_tco" }
134+
}
135+
}
136+
137+
package outerclass {
138+
class Outer extends O1 with O2 {
139+
override def m2 = "outerclass.Outer"
140+
141+
trait A3_T extends A1 with A2 {
142+
override def m1 = "a3_t"
143+
144+
def f1 = super[A1].m1
145+
def f2 = super[A2].m1
146+
def f3 = m1
147+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
148+
def f5 = Outer.super[O2].m2
149+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
150+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
151+
}
152+
153+
class A3_C extends A1 with A2 {
154+
override def m1 = "a3_c"
155+
156+
def f1 = super[A1].m1
157+
def f2 = super[A2].m1
158+
def f3 = m1
159+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
160+
def f5 = Outer.super[O2].m2
161+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
162+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
163+
}
164+
165+
object A3_O extends A1 with A2 {
166+
override def m1 = "a3_o"
167+
168+
def f1 = super[A1].m1
169+
def f2 = super[A2].m1
170+
def f3 = m1
171+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
172+
def f5 = Outer.super[O2].m2
173+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
174+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
175+
}
176+
177+
class A3_TC extends A3_T { override def m1 = "a3_tc" }
178+
object A3_TO extends A3_T { override def m1 = "a3_to" }
179+
object A3_TCO extends A3_TC { override def m1 = "a3_tco" }
180+
}
181+
}
182+
183+
package withinmethod {
184+
trait Outer extends O1 with O2 {
185+
override def m2 = "withinmethod.Outer"
186+
187+
def method1 = {
188+
trait A3_T extends A1 with A2 {
189+
override def m1 = "a3_t"
190+
191+
def f1 = super[A1].m1
192+
def f2 = super[A2].m1
193+
def f3 = m1
194+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
195+
def f5 = Outer.super[O2].m2
196+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
197+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
198+
}
199+
class A3_C extends A1 with A2 {
200+
override def m1 = "a3_c"
201+
202+
def f1 = super[A1].m1
203+
def f2 = super[A2].m1
204+
def f3 = m1
205+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
206+
def f5 = Outer.super[O2].m2
207+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
208+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
209+
}
210+
object A3_O extends A1 with A2 {
211+
override def m1 = "a3_o"
212+
213+
def f1 = super[A1].m1
214+
def f2 = super[A2].m1
215+
def f3 = m1
216+
def f4 = Outer.super[O1].m2 + "/" + Outer.super[O1].o1
217+
def f5 = Outer.super[O2].m2
218+
def f6 = Outer.this.m2 + "/" + Outer.this.o1
219+
def f7 = () => List(super[A1].m1, Outer.super[O1].m2)
220+
}
221+
class A3_TC extends A3_T { override def m1 = "a3_tc" }
222+
object A3_TO extends A3_T { override def m1 = "a3_to" }
223+
object A3_TCO extends A3_TC { override def m1 = "a3_tco" }
224+
225+
List[Test.Anything](new A3_T { }, new A3_C, A3_O, new A3_TC, A3_TO, A3_TCO)
226+
}
227+
}
228+
}
229+
230+
object Test {
231+
type Anything = {
232+
def f1: Any
233+
def f2: Any
234+
def f3: Any
235+
def f4: Any
236+
def f5: Any
237+
def f6: Any
238+
def f7: () => Any
239+
}
240+
241+
def show(x: Anything) {
242+
import x._
243+
println(List(f1, f2, f3, f4, f5, f6, f7()) mkString " ")
244+
}
245+
def main(args: Array[String]): Unit = {
246+
{
247+
val o1 = new outertrait.Outer { }
248+
show(new o1.A3_T { })
249+
show(new o1.A3_C)
250+
show(o1.A3_O)
251+
show(new o1.A3_TC)
252+
show(o1.A3_TO)
253+
show(o1.A3_TCO)
254+
println("")
255+
}
256+
257+
{
258+
val o1 = new outerclass.Outer { }
259+
show(new o1.A3_T { })
260+
show(new o1.A3_C)
261+
show(o1.A3_O)
262+
show(new o1.A3_TC)
263+
show(o1.A3_TO)
264+
show(o1.A3_TCO)
265+
println("")
266+
}
267+
268+
{
269+
val o1 = new withinmethod.Outer { }
270+
o1.method1 foreach show
271+
}
272+
273+
val bclass = new BClass
274+
val bclassm = new bclass.M
275+
println(bclassm.z)
276+
277+
val btrait = new BTrait
278+
val btraitm = new btrait.M
279+
println(btraitm.z)
280+
281+
val bclass2 = new BClass2
282+
println(bclass2.z)
283+
284+
val btrait2 = new BTrait2
285+
println(btrait2.z)
286+
287+
val bclass3 = new BClass3
288+
val bclass3m = new bclass3.M
289+
println(bclass3m.zclass)
290+
println(bclass3m.ztraita)
291+
println(bclass3m.ztraitb)
292+
293+
val bottom = new Bottom
294+
bottom.a
295+
bottom.b
296+
}
297+
}

0 commit comments

Comments
 (0)