| 00:00 | <shu> | (the magic from a spec perspective will be, like, look at the current parse node being evaluated, and then find the nearest enclosing block) |
| 00:00 | <rbuckton> | I don't necessarily think we need general purpose function coloring, though I do like the additional guardrail that provides. I'm primarily interested in just improving the DX of function f() { unsafe { and function f(x, y = do unsafe { x.y }) since those feel very awkward |
| 00:01 | <rbuckton> | (the magic from a spec perspective will be, like, look at the current parse node being evaluated, and then find the nearest enclosing block) |
| 00:01 | <Mathieu Hofman> | To inform this intrinsics call coloring question, I think the `Reflect.get` case is interesting. Would you expect `unsafe { Reflect.get(sharedStruct, "foo") }` to work? |
| 00:01 | <shu> | Wouldn't we just look at the current lexical environment as part of Call? |
| 00:02 | <shu> | must be nice to have an implementation that never optimizes away scopes! |
| 00:04 | <rbuckton> | To inform this intrinsics call coloring question, I think the `Reflect.get` case is interesting. Would you expect `unsafe { Reflect.get(sharedStruct, "foo") }` to work? unsafe (which is primarily for working directly with pointers) does not require unsafe to interact with pointers via reflection, but C#'s reflection is significantly different from JS's. |
| 00:05 | <rbuckton> | If we did require unsafe, then Reflect.get et al would also need an UnsafeCall |
| 00:05 | <Mathieu Hofman> | Good point. I would expect `Reflect.get` to throw if not in an unsafe context. |
| 00:06 | <rbuckton> | But there would still be no carryover of unsafe { proxyForS.x } through a get trap, and just marking every proxy trap unsafe is dangerous. |
| 00:07 | <Mathieu Hofman> | Which now means we need an unsafeCall trap for proxies if we expose this to user land. Ugh |
| 00:08 | <shu> | hey man i'm also happy being laissez-faire with data races |
| 00:08 | <rbuckton> | We could instead have new Proxy(s, { get(target, key, receiver, unsafe) { return Reflect.get(target, key, receiver, unsafe); } }) and traffic the caller's unsafe-ness around as a parameter. |
| 00:10 | <shu> | seems fine |
| 00:10 | <Mathieu Hofman> | Seems not, that would effectively allow creating unsafe context without syntax |
| 00:12 | <shu> | maybe unsafe will be some unforgeable capability token? |
| 00:12 | <shu> | i guess we can't prevent it from being exfiltrated |
| 00:12 | <rbuckton> | We either have all of this complexity, or we say:
|
| 00:13 | <rbuckton> | For the 4th bullet, that would mean new Proxy(s, { get() { } }).x would throw outside of an unsafe block without ever invoking the get trap |
| 00:13 | <shu> | i am definitely coming around to Atomics being internally unsafe, after what i said above |
| 00:13 | <shu> | in fact that's basically all Atomics do, access shared memory |
| 00:13 | <shu> | so they have to be internally unsafe in a no function coloring world |
| 00:14 | <rbuckton> | in fact that's basically all Atomics do, access shared memory |
| 00:14 | <shu> | yes, fair |
| 00:14 | <Mathieu Hofman> | Maybe for atomics, but I'm a lot less comfortable for reflect |
| 00:15 | <iain> | Atomics are grandfathered in, and it's not too bad to say "grep for 'atomics' and 'unsafe' to audit" |
| 00:15 | <rbuckton> | Otherwise what's the purpose of all of the happens-before and all of the other ordering relations in https://tc39.es/ecma262/#sec-relations-of-candidate-executions |
| 00:15 | <iain> | I agree that reflect is a harder case |
| 00:16 | <shu> | it could also be that Reflect methods are not internally unsafe, so they just straight up don't work in any context on shared structs |
| 00:16 | <shu> | i can live with that |
| 00:16 | <rbuckton> | Atomics are grandfathered in, and it's not too bad to say "grep for 'atomics' and 'unsafe' to audit" |
| 00:16 | <shu> | you then have to add Reflect.unsafeGet and Reflect.unsafeSet that are internally unsafe |
| 00:16 | <iain> | What I mean is that if you want to audit potential data races in your code, you have to look at your uses of Atomics, and we can't put that horse back in the barn |
| 00:17 | <shu> | no, Atomics can never exhibit data races |
| 00:17 | <shu> | only normal races |
| 00:17 | <iain> | Sorry, yeah, that's what I meant |
| 00:20 | <shu> | i'm off for the rest of the week. good progress and discussion everyone |
| 00:20 | <rbuckton> | I think "no function coloring" is a far simpler approach, overall. We shouldn't buy into that complexity unless it is absolutely necessary. |
| 00:21 | <rbuckton> | I think it has some interesting benefits, but I don't know that they outweigh the implementation complexity. |
| 00:21 | <iain> | I think it is worth preserving flexibility to add it later if it does not significantly conflict with other goals |
| 00:21 | <iain> | But I do not want function colouring now |
| 00:23 | <rbuckton> | In which case, I would still argue in favor of unsafe function f() {} as meaning something closer to C#'s interpretation than Rust's, in that unsafe in this case is only tagging the declaration as having an unsafe lexical scope, since unsafe tagging readily solves issues with lifting safe entrypoints to unsafe code out of an unsafe {} block. |
| 00:24 | <rbuckton> | We already have this problem with private state, I'd like us not to repeat that mistake. |
| 00:31 | <rbuckton> |
While it's one of the reasons I proposed |