Skip to content

Conversation

@adriaanm
Copy link
Contributor

traits now do the lambada without fields

retronym and others added 11 commits March 9, 2016 07:36
This is a second attempt at making this script operate when
making a binary incompatible change to the compiler, such as
the current effort to change the trait encoding.

I have just disabled the docs when STARR_REF is provided,
rather than disabling them in the first pass as I tried in
c4fc2fd. scala-dev/scala#89 showed that we can't defer docs to
the second pass, as the second pass isn't always run.
  - Leave the members in the trait instead (these will be emitted as
    default methods in the interface)
  - Add the trait mixin constructor to the interface, rather than
    the impl class
  - Change the invocation of the mixin constructor to use an
    invokespecial. This is encoded with the AST:
    `Apply(Select(Super(_, _), mixinConstructor)))`

I've tried to remove all traces of the interface / implclass
distinction.

To allow us to call arbitrary super methods with invokespecial,
the backend will add a transitively inherited interface as a direct
when needed to circumvent the JVM restriction that invokespecial
can only use a direct parent as the receiver.
These manual mixins were forwarding to the impl classes have
just been removed. We can now rely on default methods instead.

Update Tests:

 - Fix test/files/pos/t1237.scala, we can't have an outer field
   in an interface, always use the outer method.
 - Don't crash on meaningless trait early init fields
   test/files/neg/t2796.scala
 - Remove impl class relate parts of inner class test
 - Remove impl class relate parts of elidable test
 - Remove impl class related reflection test.
 - Remove test solely about trait impl classes renaming
 - Update check file with additional stub symbol error
 - Disable unstable parts of serialization test.
    - TODO explain, and reset the expectation
As built in https://scala-ci.typesafe.com/view/scala-2.12.x/job/scala-2.12.x-integrate-bootstrap/326/console

To pick this up in PR validation, I've configured SBT to use the extra
resolver. Once we gain some more confidence about the new STARR candidate,
we'll tag it and have the bootstrap script publish it to Sonatype, at
which time we can drop this change.
based on scala/scala3#1133

lambdalift updates info of trait members, so must preserve this when mixing in
class MethodNesting {
  def method2method(a: Int) = {
    def intermediate = {
      def localmethod = a + 1
      localmethod
    }
    intermediate
  }
}

trait TraitNesting { def foo(x: Int) = { trait U { def bar = x }; (new U{}).bar } }

class ClassUseOuter {
  def local(x: Int) = {
    class StoreCapture {
      class AccessViaOuter {
        def y = x
      }
    }
    val s = new StoreCapture
    val via = (new s.AccessViaOuter)
    // TODO: use reflection to ensure via's class has only an $outer field
    via.y
  }
}

class ConstructorCall(val x: Any)
object ConstructorCall {
  def m(UGH: Any): ConstructorCall = {
    class X extends ConstructorCall(Option(null).getOrElse(UGH))
    new X
  }
}

object Test extends App {
//  def main(args: Array[String]): Unit = {
    assert((new MethodNesting).method2method(1) == 2)
    assert((new TraitNesting {}).foo(2) == 2)
    assert((new ClassUseOuter).local(2) == 2)
    assert(ConstructorCall.m(2).x == 2)
//  }
}
trait EarlyInitImplRestriction {
  trait Tree {
    def orElse(alt: => Tree): Tree = ???
  }
  trait MacroContext {
    val bla: Tree
    def meh: Tree
  }
  def macroContext(local: Tree): MacroContext = {
    new {
      // the anon class in bla captures local
      // note that bla's owner is the anon subclass of MacroContext
      // we'll need to mark `local` free in things nested in the ctor (premature self weirdness)
      val bla = local orElse local
    } with MacroContext {
      def meh = local
    }
  }
}
@scala-jenkins scala-jenkins added this to the 2.12.0-M4 milestone Mar 12, 2016
@adriaanm
Copy link
Contributor Author

class MethodNesting {
  def method2method(a: Int) = {
    def intermediate = {
      def localmethod = a + 1
      localmethod
    }
    intermediate
  }
}

trait TraitNesting { def foo(x: Int) = { trait Nested { def bar = x }; (new Nested{}).bar } }

class ClassUseOuter {
  def local(x: Int) = {
    class StoreCapture {
      class AccessViaOuter {
        def y = x
      }
    }
    val s = new StoreCapture
    val via = (new s.AccessViaOuter)
    // TODO: use reflection to ensure via's class has only an $outer field
    via.y
  }
}

class ConstructorCall(val x: Any)
object ConstructorCall {
  def m(UGH: Any): ConstructorCall = {
    class X extends ConstructorCall(Option(null).getOrElse(UGH))
    new X
  }
}

object Test extends App {
//  def main(args: Array[String]): Unit = {
    assert((new MethodNesting).method2method(1) == 2)
    assert((new TraitNesting {}).foo(2) == 2)
    assert((new ClassUseOuter).local(2) == 2)
    assert(ConstructorCall.m(2).x == 2)
//  }
}

trait EarlyInitImplRestriction {
  trait Tree {
    def orElse(alt: => Tree): Tree = ???
  }
  trait MacroContext {
    val bla: Tree
    def meh: Tree
  }
  def macroContext(local: Tree): MacroContext = {
    new {
      // the anon class in bla captures local
      // note that bla's owner is the anon subclass of MacroContext
      // we'll need to mark `local` free in things nested in the ctor (premature self weirdness)
      val bla = local orElse local
    } with MacroContext {
      def meh = local
    }
  }
}

@adriaanm adriaanm changed the title New traits lambda [ci: last-only] WIP New traits lambda [ci: last-only] Mar 12, 2016
@adriaanm
Copy link
Contributor Author

The test failure shows we of course can't escape traits that capture locals that easily. The capture could happen in the initializer or, equivalently, the RHS of a val.

@adriaanm
Copy link
Contributor Author

adriaanm commented Mar 12, 2016

More specifically, dotty's approach of pushing down captures in traits to its methods (to avoid adding fields for them) doesn't work for us because our trait initialization works differently. (Dotty's lifts RHS of fields into methods, which can then get the captured locals via arguments.)

For us, to compile something like this

object TraitValCapturing {
  def bar(ev: Any) = {
    trait Y { val ev2 = ev }

    (new Y{}).ev2
  }
}

we'll need to have a getter for ev in Y and implement it in each subclass of Y (we see all those because Y is local).

@adriaanm adriaanm modified the milestones: 2.12.0-M5, 2.12.0-M4 Mar 14, 2016
@adriaanm adriaanm mentioned this pull request Apr 1, 2016
2 tasks
@adriaanm adriaanm closed this Apr 27, 2016
@adriaanm adriaanm deleted the new-traits-lambda branch April 28, 2016 00:49
@adriaanm adriaanm added 2.12 and removed 2.12 labels Oct 29, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants