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.73k stars 415 forks source link

Misleading compiler message: ("... tag is not a subcap of val") #2319

Open slfritchie opened 7 years ago

slfritchie commented 7 years ago

Hi, everyone. @jtfmumm recommended that I try opening a bug for what appears to me to be q misleading compiler message. I spent a couple of newbie hours swearing at the compiler trying to figure out what was going on. Later, John agreed that the compiler in this case definitely wasn't being helpful.

Source & compilation info: today's "master" branch, though I also see the same error when using the '0.20.0' tag. My (very!) flawed example code can be found at https://gist.github.com/slfritchie/6e80abae3baf486df745d90d5e3778ec.

% ponyc --version
0.20.0-f477519ae [release]
compiled with: llvm 3.9.1 -- Apple LLVM version 9.0.0 (clang-900.0.38)

The puzzling output from the compiler is:

/Users/scott/src/study/message-passing-benchmark/tmp/foo.pony:17:21: argument not a subtype of parameter
      p.set_workers(ws')
                    ^
    Info:
    /Users/scott/src/study/message-passing-benchmark/tmp/foo.pony:9:13: Array[Worker tag] tag is not a subtype of Array[Worker tag] val: tag is not a subcap of val
        let ws: Array[Worker] = ws.create()  // Error msg: Array[Worker tag] tag
                ^

Line 9 doesn't explicitly say anything about the entire array having capability tag. I'm modifying that array at line 13, so the array can't be tag. Has the compiler mixed up reporting the type of ws with the type of ws' here?

plietar commented 7 years ago

Hey, So the problem here comes from the recover:

let ws' = recover ws end

Here ws comes from outside the recover block and has a non-sendable type (ref), so it shouldn't be allowed at all. But for some reason the compiler allows it and assigns a tag capability to the value, hence the error message.

Recovering to tag when the recover is invalid is probably sound (making tag references is pretty low risk), but definitely useless and misleading.

Minimal reproduction: https://is.gd/W7QAJm

class A
actor Main
  new create(env: Env) =>
    let a: A ref = A
    let a': A val = recover a end
Error:
main.pony:6:19: right side must be a subtype of left side
    let a': A val = recover a end
                  ^
    Info:
    main.pony:5:12: A tag is not a subtype of A val: tag is not a subcap of val
        let a: A ref = A

The error should be something like "a is used in recover block but has type A ref which is non-sendable".


In order to make your code build you should move everything inside the recover block, so you don't have anything non-sendable coming from outside it anymore:

let ws = recover val
  let ws: Array[Worker] = ws.create()

  for i in Range[I32](0, num_workers) do
    let p = Worker(env, i)
      ws.push(p)
    end
    ws
  end
  for p in ws'.values() do
    p.set_workers(ws')
  end
end

for p in ws.values() do
  p.set_workers(ws)
end

Playground: https://is.gd/PbyT5o

jemc commented 7 years ago

We discussed this a bit on the sync call.

Due to the way the compiler works, it would require a significant amount of effort to do either:

  1. tailor the compiler message for this situation.
  2. prevent this situation by not implicitly treating non-sendables from outside the recover as tag.

Since we're expecting to change the way the compiler analyzes recover blocks based on some work that @plietar has been cooking up with @sylvanc and Sophia, we decided that it would be best to put this issue on ice until we have that plan in hand and know how it will affect this case, rather than making a major change here just before making another major one.

SeanTAllen commented 4 years ago

This is currently blocked.