-
Notifications
You must be signed in to change notification settings - Fork 455
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deterministic runtime representation for extensible types and exceptions #6570
Deterministic runtime representation for extensible types and exceptions #6570
Conversation
This is awesome! We're taking it a bit easy right now after shipping v11, but hoping we can get around to reviewing this soon. |
@diogomqbm I haven't touched this part of the compiler yet, so bear with me for some stupid questions:
|
@zth No worries, and thank you for taking a look 🙏
Yes. We discovered this bug while working on the Walnut codebase. And yes, we fixed it by having a transformer build pass that removed the calls to
I'm not sure I completely understand this question. What do you mean by "only stuff happening"? |
From what I understand of exceptions and extensible variants, this looks fine to me. But I'd like to see what @cristianoc thinks too. For posterity: As I tested this, I noticed a few things not working that might be cool to address in the future to enhance extensible variants more. Although I'm not 100% sure all of them makes sense, I'll list them anyway:
|
I wonder what the original intentions were. Looks like the number was assigned at runtime intentionally. If so, it would be great to understand the intentions to be more confident in the change. |
Yeah, same. I think that for exceptions it does make sense to have the current implementation. But when thinking about extensible types, I don't think it does. I agree that it would be ideal to know why the original implementation was like this and what can we do to maintain consistency between two different contexts. Being dependent on the runtime is not good enough because then you depend on how or which order that the modules are loaded. |
Looking at the details of issue #6569 again, I'm not sure I understand the problem experienced. var A = require("./A.js");
var B = require("./B.js");
function handleRequest(req) {
if (req.RE_EXN_ID === A.FromModuleA) {
return "received from A";
}
if (req.RE_EXN_ID === B.FromModuleB) {
return "received from B";
}
... So the generated code does not seem depend on the actual value used in module A or B. Even if they were to be loaded in different order, the runtime representation would be different, but the code behaves in the same way as it uses whatever value is in that module (e.g. Perhaps the issue reports a simplified example? I can't see a problem in the example shown there. |
@cristianoc I'm sorry if I was not clear enough in the issue description. The issue happened for us with message passing between two different runtimes/contexts where the message was an extensible type. So, while they both refer to the same variable, this variable is created in different contexts with different values. E.g. while we're pattern matching on Is it clear? I'm happy to discuss this in a more synchronous forum if needed. I'm available on the discord development server as well. |
Thanks for providing the context. In fact, I'm not sure I can even produce an example where the number is required in order to disambiguate values. |
Looks like functors are what require the usage of numbers to disambiguate:
Technically, I think a counter per-instance is what's required as a minimum for this to be correct in all cases. |
As far as I know, there's no representation difference between the extensible variant type and exceptions in OCaml. So, it looks like we'd need to make changes in lambda if we were to create this distinction between them. Or we agree to generate them both at compile time with a random aspect to them. Maybe instead of adding the counter, we could have a random number attached to the string.
Yes, I also tried to find an example where the number would be needed but failed to do so. However, I thought of keeping it as a safety measure since I didn't know all the details of the first implementation. |
Technically, not sure it can be done at compile time (with random or other methods), given that a functor is a function that can be invoked several times. |
Would a counter per-string take care of the loading order issue? |
Indeed, when thinking about functors it only works if they're on the same file. Consider the following example: module M = (A: {}) => {
exception E
}
module M1 = M({})
module M2 = M({}) Generated: // Generated by ReScript, PLEASE EDIT WITH CARE
function M(A) {
var E = "Functor.M(A).E/1";
return {
E: E
};
}
var E = "Functor.M(A).E/2";
var M1 = {
E: E
};
var E$1 = "Functor.M(A).E/3";
var M2 = {
E: E$1
};
export {
M ,
M1 ,
M2 ,
}
/* No side effect */ As you can see it works for the same file, but now if we have another file calling // Generated by ReScript, PLEASE EDIT WITH CARE
import * as Functor from "./Functor.res.mjs";
var M3 = Functor.M({});
console.log({
RE_EXN_ID: M3.E
});
export {
M3 ,
}
/* M3 Not a pure module */ which means that
@cristianoc Could you elaborate more on this? |
The idea would be to modify slightly the runtime so instead of keeping just an integer, the counter would be a dict, to store a different counter for each string. |
@cristianoc interesting, I think that would work! I'm going to submit an implementation later and also write a test for it so we make sure that a) it's not dependent on the order we load them and b) that we can cover functors by generating different values. Thank you! |
1ad0ace
to
02f5770
Compare
@cristianoc @zth just pushed a new implementation following the idea mentioned here. So, basically we have an The only edge case I can see now is if you're using functors with exceptions you might stumble into this problem. But at least it's more narrow now. WDYT? |
Great work @diogomqbm ! I'll let @cristianoc comment on the implementation. With a little luck this fix can go in v11.1. |
Looks great. |
For posterity, the corner case left would involve eg creating an exception E in the body of a functor. |
@diogomqbm a changelog and then we're good to go. Great work, well done! 🎉 |
Ping @diogomqbm (soon time for another RC, would be good to get this in) |
02f5770
to
ebe2dd7
Compare
@zth sorry for the delay. I got COVID last week and things have been bumpy since then 😅 Anyway, just pushed the changelog. One thing that came to mind is that maybe we don't want to expose |
I hope you feel better soon! Covid is still pretty nasty. Good catch! One final thing that I forgot about - could you change the base branch and rebase to 11.0_release? |
627da1e
to
d0dc3a7
Compare
done ✅ |
Attempts to solve #6569