Skip to content

Commit faca7ec

Browse files
committed
SI-4729, overriding java varargs in scala.
[backport] This was a bad interaction between anonymous subclasses and bridge methods. new Foo { override def bar = 5 } Scala figures it can mark "bar" private since hey, what's the difference. The problem is that if it was overriding a java-defined varargs method in scala, the bridge method logic says "Oh, it's private? Then you don't need a varargs bridge." Hey scalac, you're the one that made me private! You made me like this! You! Conflicts: src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
1 parent 0990890 commit faca7ec

File tree

5 files changed

+69
-14
lines changed

5 files changed

+69
-14
lines changed

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

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -156,27 +156,22 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
156156

157157
// Override checking ------------------------------------------------------------
158158

159-
def isJavaVarargsAncestor(clazz: Symbol) = (
160-
clazz.isClass
161-
&& clazz.isJavaDefined
162-
&& (clazz.info.nonPrivateDecls exists isJavaVarArgsMethod)
163-
)
164-
165159
/** Add bridges for vararg methods that extend Java vararg methods
166160
*/
167161
def addVarargBridges(clazz: Symbol): List[Tree] = {
168162
// This is quite expensive, so attempt to skip it completely.
169163
// Insist there at least be a java-defined ancestor which
170164
// defines a varargs method. TODO: Find a cheaper way to exclude.
171-
if (clazz.thisType.baseClasses exists isJavaVarargsAncestor) {
165+
if (inheritsJavaVarArgsMethod(clazz)) {
172166
log("Found java varargs ancestor in " + clazz.fullLocationString + ".")
173167
val self = clazz.thisType
174168
val bridges = new ListBuffer[Tree]
175169

176170
def varargBridge(member: Symbol, bridgetpe: Type): Tree = {
177-
log("Generating varargs bridge for " + member.fullLocationString + " of type " + bridgetpe)
171+
log(s"Generating varargs bridge for ${member.fullLocationString} of type $bridgetpe")
178172

179-
val bridge = member.cloneSymbolImpl(clazz, member.flags | VBRIDGE) setPos clazz.pos
173+
val newFlags = (member.flags | VBRIDGE | ARTIFACT) & ~PRIVATE
174+
val bridge = member.cloneSymbolImpl(clazz, newFlags) setPos clazz.pos
180175
bridge.setInfo(bridgetpe.cloneInfo(bridge))
181176
clazz.info.decls enter bridge
182177

@@ -189,26 +184,35 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
189184
localTyper typed DefDef(bridge, body)
190185
}
191186

192-
// For all concrete non-private members that have a (Scala) repeated parameter:
193-
// compute the corresponding method type `jtpe` with a Java repeated parameter
187+
// For all concrete non-private members (but: see below) that have a (Scala) repeated
188+
// parameter: compute the corresponding method type `jtpe` with a Java repeated parameter
194189
// if a method with type `jtpe` exists and that method is not a varargs bridge
195190
// then create a varargs bridge of type `jtpe` that forwards to the
196191
// member method with the Scala vararg type.
197-
for (member <- clazz.info.nonPrivateMembers) {
192+
//
193+
// @PP: Can't call nonPrivateMembers because we will miss refinement members,
194+
// which have been marked private. See SI-4729.
195+
for (member <- nonTrivialMembers(clazz)) {
196+
log(s"Considering $member for java varargs bridge in $clazz")
198197
if (!member.isDeferred && member.isMethod && hasRepeatedParam(member.info)) {
199198
val inherited = clazz.info.nonPrivateMemberAdmitting(member.name, VBRIDGE)
199+
200200
// Delaying calling memberType as long as possible
201201
if (inherited ne NoSymbol) {
202-
val jtpe = toJavaRepeatedParam(self.memberType(member))
202+
val jtpe = toJavaRepeatedParam(self memberType member)
203203
// this is a bit tortuous: we look for non-private members or bridges
204204
// if we find a bridge everything is OK. If we find another member,
205205
// we need to create a bridge
206-
if (inherited filter (sym => (self.memberType(sym) matches jtpe) && !(sym hasFlag VBRIDGE)) exists)
206+
val inherited1 = inherited filter (sym => !(sym hasFlag VBRIDGE) && (self memberType sym matches jtpe))
207+
if (inherited1.exists)
207208
bridges += varargBridge(member, jtpe)
208209
}
209210
}
210211
}
211212

213+
if (bridges.size > 0)
214+
log(s"Adding ${bridges.size} bridges for methods extending java varargs.")
215+
212216
bridges.toList
213217
}
214218
else Nil

src/reflect/scala/reflect/internal/Types.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7114,6 +7114,14 @@ trait Types extends api.Types { self: SymbolTable =>
71147114
}
71157115
}
71167116

7117+
def isJavaVarargsAncestor(clazz: Symbol) = (
7118+
clazz.isClass
7119+
&& clazz.isJavaDefined
7120+
&& (clazz.info.nonPrivateDecls exists isJavaVarArgsMethod)
7121+
)
7122+
def inheritsJavaVarArgsMethod(clazz: Symbol) =
7123+
clazz.thisType.baseClasses exists isJavaVarargsAncestor
7124+
71177125
/** All types in list must be polytypes with type parameter lists of
71187126
* same length as tparams.
71197127
* Returns list of list of bounds infos, where corresponding type
@@ -7226,6 +7234,12 @@ trait Types extends api.Types { self: SymbolTable =>
72267234
else (ps :+ SerializableClass.tpe).toList
72277235
)
72287236

7237+
/** Members of the given class, other than those inherited
7238+
* from Any or AnyRef.
7239+
*/
7240+
def nonTrivialMembers(clazz: Symbol): Iterable[Symbol] =
7241+
clazz.info.members filterNot (sym => sym.owner == ObjectClass || sym.owner == AnyClass)
7242+
72297243
def objToAny(tp: Type): Type =
72307244
if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyClass.tpe
72317245
else tp

test/files/run/t4729.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
WrappedArray(1, 2)
2+
WrappedArray(1, 2)
3+
WrappedArray(1, 2)
4+
WrappedArray(1, 2)

test/files/run/t4729/J_1.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Java Interface:
2+
public interface J_1 {
3+
public void method(String... s);
4+
}

test/files/run/t4729/S_2.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Scala class:
2+
class ScalaVarArgs extends J_1 {
3+
// -- no problem on overriding it using ordinary class
4+
def method(s: String*) { println(s) }
5+
}
6+
7+
object Test {
8+
def main(args: Array[String]) {
9+
//[1] Ok - no problem using inferred type
10+
val varArgs = new J_1 {
11+
def method(s: String*) { println(s) }
12+
}
13+
varArgs.method("1", "2")
14+
15+
//[2] Ok -- no problem when explicit set its type after construction
16+
val b: J_1 = varArgs
17+
b.method("1", "2")
18+
19+
//[3] Ok -- no problem on calling its method
20+
(new ScalaVarArgs).method("1", "2")
21+
(new ScalaVarArgs: J_1).method("1", "2")
22+
23+
//[4] Not Ok -- error when assigning anonymous class to a explictly typed val
24+
// Compiler error: object creation impossible, since method method in trait VarArgs of type (s: <repeated...>[java.lang.String])Unit is not defined
25+
val tagged: J_1 = new J_1 {
26+
def method(s: String*) { println(s) }
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)