Skip to content

Commit

Permalink
incoherent cyclic references between synthesized members
Browse files Browse the repository at this point in the history
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
      ^
  • Loading branch information
adriaanm committed Oct 11, 2015
1 parent ec2283e commit 728e71e
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 11 deletions.
33 changes: 22 additions & 11 deletions src/compiler/scala/tools/nsc/transform/Fields.scala
Expand Up @@ -210,19 +210,20 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// class OverridingVal extends OneVal[Int] { override val x: Int = ??? }


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

// println(s"cloning accessor $accessor to $clazz / $clonedInfo -> $relativeInfo")
clonedAccessor setInfo ((clazz.thisType memberType accessor) cloneInfo clonedAccessor) // accessor.info.cloneInfo(clonedAccessor).asSeenFrom(clazz.thisType, accessor.owner)

}

// when considering whether to mix in the trait setter, forget about conflicts -- they will be reported for the getter
Expand All @@ -247,6 +248,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
else if (accessorConflictsExistingVal(accessor) || isOverriddenAccessor(accessor, clazz)) Nil
else if (accessor.isGetter && fieldMemoizationIn(accessor, clazz).needsField) {
// add field if needed

val field = clazz.newValue(accessor.localName, accessor.pos) setInfo fieldTypeForGetterIn(accessor, clazz.thisType)

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

// println(s"new decls for $clazz: $mixedInFieldAndAccessors")
// println(s"new decls for $clazz: $mixedInAccessorAndFields")

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

val newDecls =
if (mixedInFieldAndAccessors.isEmpty) oldDecls.filterNot(omittableField)
if (mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField)
else { // must not alter `decls` directly
val newDecls = newScope
oldDecls foreach { d => if (!omittableField(d)) newDecls.enter(d) }
mixedInFieldAndAccessors foreach newDecls.enter
val enter = { mixedin: Symbol => newDecls enter mixedin }
mixedInAccessorAndFields foreach { _ foreach enter }

// subst from accessors to corresponding clonedAccessors in types in newDecls
val (origs, mixedins) = (accessorsMaybeNeedingImpl, mixedInAccessorAndFields).zipped.collect {
case (traitAccessor, mixedin :: _) => (traitAccessor, mixedin)
}.unzip
newDecls foreach { sym => sym.substInfo(origs.toList, mixedins.toList) }

newDecls
}

// println(s"new decls: $newDecls")

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

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

Expand Down
16 changes: 16 additions & 0 deletions test/files/trait-defaults/dependent_rebind.scala
@@ -0,0 +1,16 @@
// derived from test/files/pos/S5.scala

// compile with -uniqid to see a hint of the trouble
trait N {
// the symbol for self does not get rebound when synthesizing members in C
val self: N = ???
val n: self.type = self
}

// uncomment extends clause for another bug
abstract class M /*extends N*/ {
val self: N
val n: self.type
}

class C extends M with N

0 comments on commit 728e71e

Please sign in to comment.