Skip to content

Commit a060fc1

Browse files
committed
incoherent cyclic references between synthesized members
must replace old trait accessor symbols by mixed in symbols in the infos of the mixed in symbols ``` trait T { val a: String ; val b: a.type } class C extends T { // a, b synthesized, but the a in b's type, a.type, refers to the original symbol, not the clone in C } ``` symbols occurring in types of synthesized members do not get rebound to other synthesized symbols package <empty>#4 { abstract <defaultparam/trait> trait N#7352 extends scala#22.AnyRef#2378 { <method> <deferred> <mutable> <accessor> <triedcooking> <sub_synth> def N$_setter_$self_$eq#15011(x$1#15012: <empty>#3.this.N#7352): scala#23.this.Unit#2340; <method> <deferred> <mutable> <accessor> <triedcooking> <sub_synth> def N$_setter_$n_$eq#15013(x$1#15014: N#7352.this.self#7442.type): scala#23.this.Unit#2340; <method> def /*N*/$init$scala#7441(): scala#23.this.Unit#2340 = { () }; <method> <deferred> <stable> <accessor> <triedcooking> <sub_synth> def self#7442: <empty>#3.this.N#7352; N#7352.this.N$_setter_$self_$eq#15011(scala#22.Predef#1729.$qmark$qmark$qmark#6917); <method> <deferred> <stable> <accessor> <sub_synth> def n#7443: N#7352.this.self#7442.type; N#7352.this.N$_setter_$n_$eq#15013(N#7352.this.self#7442) }; abstract class M#7353 extends scala#22.AnyRef#2378 { <method> <triedcooking> def <init>#13465(): <empty>#3.this.M#7353 = { M#7353.super.<init>scala#2719(); () }; <method> <deferred> <stable> <accessor> <triedcooking> def self#13466: <empty>#3.this.N#7352; <method> <deferred> <stable> <accessor> def n#13467: M#7353.this.self#13466.type }; class C#7354 extends M#7353 with <empty>#3.this.N#7352 { <method> <stable> <accessor> <triedcooking> def self#15016: <empty>#3.this.N#7352 = C#7354.this.self #15015; <triedcooking> private[this] val self #15015: <empty>#3.this.N#7352 = _; <method> <stable> <accessor> def n#15018: C#7354.this.self#7442.type = C#7354.this.n #15017; <triedcooking> private[this] val n #15017: C#7354.this.self#7442.type = _; <method> <mutable> <accessor> <triedcooking> def N$_setter_$self_$eq#15019(x$1#15021: <empty>#3.this.N#7352): scala#23.this.Unit#2340 = C#7354.this.self #15015 = x$1#15021; <method> <mutable> <accessor> <triedcooking> def N$_setter_$n_$eq#15022(x$1#15025: C#7354.this.self#7442.type): scala#23.this.Unit#2340 = C#7354.this.n #15017 = x$1#15025; <method> def <init>#14997(): <empty>#3.this.C#7354 = { C#7354.super.<init>#13465(); () } } } [running phase pickler on dependent_rebind.scala] [running phase refchecks on dependent_rebind.scala] test/files/trait-defaults/dependent_rebind.scala:16: error: overriding field n#15049 in trait N#7352 of type C#7354.this.self#15016.type; value n #15017 has incompatible type; found : => C#7354.this.self#7442.type (with underlying type => C#7354.this.self#7442.type) required: => N#7352.this.self#7442.type class C extends M with N ^
1 parent f2f9a0e commit a060fc1

File tree

2 files changed

+39
-11
lines changed

2 files changed

+39
-11
lines changed

src/compiler/scala/tools/nsc/transform/Fields.scala

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,19 +210,20 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
210210
// class OverridingVal extends OneVal[Int] { override val x: Int = ??? }
211211

212212

213-
// mixin field accessors
214-
val mixedInFieldAndAccessors = accessorsMaybeNeedingImpl flatMap { accessor =>
213+
// mixin field accessors --
214+
// invariant: (accessorsMaybeNeedingImpl, mixedInAccessorAndFields).zipped.forall(case (acc, clone :: _) => `clone` is clone of `acc` case _ => true)
215+
val mixedInAccessorAndFields = accessorsMaybeNeedingImpl map { accessor =>
215216
def cloneAccessor() = {
216217
val clonedAccessor = (accessor cloneSymbol clazz) setPos clazz.pos setFlag NEEDS_TREES resetFlag DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS | FINAL_TRAIT_ACCESSOR
217218
if (accessor hasFlag FINAL_TRAIT_ACCESSOR) {
218219
clonedAccessor setFlag FINAL | lateFINAL // lateFINAL thrown in for good measure, by analogy to makeNotPrivate
219220
}
220221
// if we don't cloneInfo, method argument symbols are shared between trait and subclasses --> lambalift proxy crash
221222
// TODO: use derive symbol variant?
222-
val clonedInfo = accessor.info.cloneInfo(clonedAccessor)
223-
val relativeInfo = clonedInfo.asSeenFrom(clazz.thisType, accessor.owner)
224-
// println(s"cloning accessor $accessor to $clazz / $clonedInfo -> $relativeInfo")
225-
clonedAccessor setInfo relativeInfo
223+
224+
// println(s"cloning accessor $accessor to $clazz / $clonedInfo -> $relativeInfo")
225+
clonedAccessor setInfo ((clazz.thisType memberType accessor) cloneInfo clonedAccessor) // accessor.info.cloneInfo(clonedAccessor).asSeenFrom(clazz.thisType, accessor.owner)
226+
226227
}
227228

228229
// when considering whether to mix in the trait setter, forget about conflicts -- they will be reported for the getter
@@ -247,6 +248,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
247248
else if (accessorConflictsExistingVal(accessor) || isOverriddenAccessor(accessor, clazz)) Nil
248249
else if (accessor.isGetter && fieldMemoizationIn(accessor, clazz).needsField) {
249250
// add field if needed
251+
250252
val field = clazz.newValue(accessor.localName, accessor.pos) setInfo fieldTypeForGetterIn(accessor, clazz.thisType)
251253

252254
val newFlags = (
@@ -264,19 +266,28 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
264266
} else List(cloneAccessor())
265267
}
266268

267-
// println(s"new decls for $clazz: $mixedInFieldAndAccessors")
269+
// println(s"new decls for $clazz: $mixedInAccessorAndFields")
268270

269271
// omit fields that are not memoized, retain all other members
270272
def omittableField(sym: Symbol) = sym.isValue && !sym.isMethod && fieldMemoizationIn(sym, clazz).effectOnly // TODO: not yet `needsField`, to produce same bytecode as M2
271273

272274
val newDecls =
273-
if (mixedInFieldAndAccessors.isEmpty) oldDecls.filterNot(omittableField)
275+
if (mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField)
274276
else { // must not alter `decls` directly
275277
val newDecls = newScope
276278
oldDecls foreach { d => if (!omittableField(d)) newDecls.enter(d) }
277-
mixedInFieldAndAccessors foreach newDecls.enter
279+
val enter = { mixedin: Symbol => newDecls enter mixedin }
280+
mixedInAccessorAndFields foreach { _ foreach enter }
281+
282+
// subst from accessors to corresponding clonedAccessors in types in newDecls
283+
val (origs, mixedins) = (accessorsMaybeNeedingImpl, mixedInAccessorAndFields).zipped.collect {
284+
case (traitAccessor, mixedin :: _) => (traitAccessor, mixedin)
285+
}.unzip
286+
newDecls foreach { sym => sym.substInfo(origs.toList, mixedins.toList) }
287+
278288
newDecls
279289
}
290+
280291
// println(s"new decls: $newDecls")
281292

282293
if (newDecls eq oldDecls) tp
@@ -294,13 +305,13 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
294305
def mkTypedUnit(pos: Position) = localTyper.typedPos(pos)(CODE.UNIT)
295306
def deriveUnitDef(stat: Tree) = deriveDefDef(stat)(_ => mkTypedUnit(stat.pos))
296307

297-
// synth trees for mixed in accessors/fields and trait setters
308+
// synth trees for accessors/fields and trait setters when they are mixed into a class
298309
def fieldsAndAccessors(templateSym: Symbol): List[ValOrDefDef] = {
299310
val clazz = templateSym.owner
300311
def fieldAccess(accessor: Symbol) = {
301312
val fieldName = accessor.localName
302313
val field = clazz.info.decl(fieldName)
303-
assert(field.exists, s"Field '$fieldName' not found in ${clazz.info.decls}")
314+
assert(field.exists, s"Field '$fieldName' not found in ${clazz.info.decls} of $clazz for $accessor")
304315
Select(This(clazz), field)
305316
}
306317

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// derived from test/files/pos/S5.scala
2+
3+
// compile with -uniqid to see a hint of the trouble
4+
trait N {
5+
// the symbol for self does not get rebound when synthesizing members in C
6+
val self: N = ???
7+
val n: self.type = self
8+
}
9+
10+
// uncomment extends clause for another bug
11+
abstract class M /*extends N*/ {
12+
val self: N
13+
val n: self.type
14+
}
15+
16+
class C extends M with N
17+

0 commit comments

Comments
 (0)