ponylang / ponyc

Pony is an open-source, actor-model, capabilities-secure, high performance programming language
http://www.ponylang.io
BSD 2-Clause "Simplified" License
5.71k stars 415 forks source link

Cannot refer to literal under construction #2859

Open patternspandemic opened 6 years ago

patternspandemic commented 6 years ago

There is no equivelant way to refer to the literal under construction, like how this can be used in class/actor constructors.

pony version:

0.24.4 [release]
compiled with: llvm 3.9.1 -- cc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
Defaults: pic=false ssl=openssl_0.9.0

source (run in playground)


trait Initable
  be _init()

class StateHelper
  """ Just a class to construct and hold some state, and callback to its parent. """
  let s: String
  new create(s': String, o: Initable tag) =>
    s = s' // Construct state, do other stuff
    o._init() // init parent object afterwards

actor EquivelantOfLiteral is Initable
  """ An actor equivelant of the literal shown below. """
  let _env: Env
  let _state: StateHelper

  new create(e: Env) =>
    _env = e
    // No problem referring to itself in constructor
    _state = StateHelper("Pony", this)

  be _init() =>
    _env.out.print(_state.s)

actor Main
  new create(env: Env) =>

    // Problem: There is no way to refer to the literal at construction time,
    //    unlike with class/actor equivelants. In the lexical scope, `this`
    //    inside of the object literal refers to the Main actor.

    let o = object is Initable
      let _state: StateHelper iso =
        recover iso StateHelper("hello", this) end

      be _init() =>
        // Do stuff relying on _state
        env.out.print(_state.s)
    end

    EquivelantOfLiteral(env) // Outputs "Pony"
patternspandemic commented 6 years ago

A variation on this, admittedly no very useful, but nonetheless results in an assertion:

I thought I could perhaps reference the literal under construction within itself by capturing the variable o to which it's being assigned. Of course o had to be made tag so that it's sendable to the literal.

// ...
let o: Initable tag = object is Initable
  let _state: StateHelper iso =
    recover iso StateHelper("hello", o) end

  be _init() =>
    // Do stuff relying on _state
    env.out.print(_state.s)
end
// ...

results in :

src/libponyc/codegen/genreference.c:286: gen_localptr: Assertion `value != NULL` failed.

Backtrace:
This is an optimised version of ponyc: the backtrace may be imprecise or incorrect.
Use a debug version to get more meaningful information.
  ponyc(ponyint_assert_fail+0x90) [0x731ff0]
  ponyc(gen_localload+0xc9) [0x6f5379]
  ponyc(gen_expr+0x173) [0x71e8e3]
  ponyc(gen_seq+0x2b) [0x7300db]
  ponyc(gen_expr+0x203) [0x71e973]
  ponyc(gen_call+0x256) [0x71cbf6]
  ponyc(gen_expr+0x1eb) [0x71e95b]
  ponyc(gen_seq+0x2b) [0x7300db]
  ponyc(gen_expr+0x203) [0x71e973]
  ponyc(gen_recover+0x2f) [0x7314ef]
  ponyc(gen_expr+0x34b) [0x71eabb]
  ponyc(gen_seq+0x2b) [0x7300db]
  ponyc(gen_expr+0x203) [0x71e973]
  ponyc(gen_call+0x256) [0x71cbf6]
  ponyc(gen_expr+0x1eb) [0x71e95b]
  ponyc(gen_assign+0x55) [0x724d75]
  ponyc(gen_expr+0xcb) [0x71e83b]
  ponyc(gen_seq+0x2b) [0x7300db]
  ponyc(gen_expr+0x203) [0x71e973]
  ponyc(genfun_method_bodies+0xac1) [0x726ff1]
  ponyc(gentypes+0xd1b) [0x7198eb]
  ponyc(genexe+0x16b) [0x72025b]
  ponyc(codegen+0xcc) [0x6e7b4c]
  ponyc(main+0x1cd) [0x61604d]
  /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7fb8c5b9a830]
  ponyc(_start+0x29) [0x675cc9]
Aborted (core dumped)
jemc commented 3 years ago

We discussed on the sync call. The summary of my realization is that because of how lamba/object literal sugaring works (in the expr pass) by moving the content of the lambda/object literal to a new anonymous type, and running the refer or expr as "catch up" passes later, and because of how the refer pass works by only keeping track of the current/latest definition state of those variables, the result is that when you refer to a variable inside a lambda or object literal, you get the answer about its status as being whatever it was at the end of the function, instead of at the place in the function where it was actually supposed to have been captured.

Here's an example that demonstrates this:

actor Main
  new create(env: Env) =>
    let x: String = {(): String =>
      x + "foo"
    }()
    consume x
Error:
main.pony:4:7: can't use a consumed local or field in an expression
      x + "foo"
      ^

I'm not sure how we would address this facet of the issue, to be honest. It seems that either the refer pass or the way we do lambdas/object literals might need re-architecting.

ergl commented 3 years ago

Here's a minimal example:

interface Foo
  fun test(): Foo tag

actor Main
  new create(env: Env) =>
    let o: Foo tag =
      object is Foo
        fun test(): Foo tag =>
          o
      end

Surprisingly, if we remove the Foo tag type annotation, then the compiler will catch that o is not used:

Error:
/.../dev/ponyc/examples/crash/main.pony:42:11: can't find declaration of 'o'
          o
          ^
Backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00007fff726e42c2 libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff7279fbf1 libsystem_pthread.dylib`pthread_kill + 284
    frame #2: 0x00007fff7264e6a6 libsystem_c.dylib`abort + 127
    frame #3: 0x0000000100114e0d ponyc`ponyint_assert_fail(expr="value != NULL", file="/Users/ryan/dev/ponyc/src/libponyc/codegen/genreference.c", line=283, func="gen_localptr") at ponyassert.c:65:3
    frame #4: 0x0000000100064f3a ponyc`gen_localptr(c=0x00007ffeefbfefb8, ast=0x000000010b569240) at genreference.c:283:3
    frame #5: 0x0000000100064f6d ponyc`gen_localload(c=0x00007ffeefbfefb8, ast=0x000000010b569240) at genreference.c:290:28
    frame #6: 0x000000010004b7dd ponyc`gen_expr(c=0x00007ffeefbfefb8, ast=0x000000010b569240) at genexpr.c:56:13
    frame #7: 0x0000000100045864 ponyc`gen_seq(c=0x00007ffeefbfefb8, ast=0x000000010b5691c0) at gencontrol.c:22:13
    frame #8: 0x000000010004b759 ponyc`gen_expr(c=0x00007ffeefbfefb8, ast=0x000000010b5691c0) at genexpr.c:26:13
    frame #9: 0x0000000100041981 ponyc`gen_call(c=0x00007ffeefbfefb8, ast=0x000000010b56bbc0) at gencall.c:776:26
    frame #10: 0x000000010004b885 ponyc`gen_expr(c=0x00007ffeefbfefb8, ast=0x000000010b56bbc0) at genexpr.c:92:13
    frame #11: 0x00000001000594c5 ponyc`gen_assign(c=0x00007ffeefbfefb8, ast=0x000000010bee5640) at genoperator.c:919:26
    frame #12: 0x000000010004ba0c ponyc`gen_expr(c=0x00007ffeefbfefb8, ast=0x000000010bee5640) at genexpr.c:148:13
    frame #13: 0x0000000100045864 ponyc`gen_seq(c=0x00007ffeefbfefb8, ast=0x000000010bee44c0) at gencontrol.c:22:13
    frame #14: 0x000000010004b759 ponyc`gen_expr(c=0x00007ffeefbfefb8, ast=0x000000010bee44c0) at genexpr.c:26:13
    frame #15: 0x000000010004e577 ponyc`genfun_newbe(c=0x00007ffeefbfefb8, t=0x000000010c213c00, m=0x000000010c05e900) at genfun.c:589:24
    frame #16: 0x000000010004d70f ponyc`genfun_method(c=0x00007ffeefbfefb8, t=0x000000010c213c00, n=0x000000010c05e880, m=0x000000010c05e900) at genfun.c:784:15
    frame #17: 0x000000010004d542 ponyc`genfun_method_bodies(c=0x00007ffeefbfefb8, t=0x000000010c213c00) at genfun.c:946:11
    frame #18: 0x000000010006a096 ponyc`gentypes(c=0x00007ffeefbfefb8) at gentype.c:861:9
    frame #19: 0x000000010004b07c ponyc`genexe(c=0x00007ffeefbfefb8, program=0x000000010c7bfd00) at genexe.c:561:7
    frame #20: 0x000000010003ca7a ponyc`codegen(program=0x000000010c7bfd00, opt=0x00007ffeefbff488) at codegen.c:923:10
    frame #21: 0x00000001000b78b3 ponyc`generate_passes(program=0x000000010c7bfd00, options=0x00007ffeefbff488) at pass.c:360:10
    frame #22: 0x0000000100001fa1 ponyc`compile_package(path="examples/crash/", opt=0x00007ffeefbff488, print_program_ast=false, print_package_ast=false) at main.c:67:13
    frame #23: 0x0000000100001e3f ponyc`main(argc=2, argv=0x00007ffeefbff598) at main.c:112:15
    frame #24: 0x00007fff725a93d5 libdyld.dylib`start + 1
    frame #25: 0x00007fff725a93d5 libdyld.dylib`start + 1