Skip to content
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

Make 'return' (and later 'next'/'last'/'redo') bind to the mainline, not to the quasi/macro #483

Open
masak opened this issue Mar 21, 2019 · 6 comments

Comments

@masak
Copy link
Owner

masak commented Mar 21, 2019

Suggestion via conversation with @vendethiel++, who finally convinced me this is a good idea.

Here's some code to discuss around:

macro r42() {
    quasi {
        return 42;
    };
}

func foo() {
    r42();
    return -1;
}

According to my previously held view, the return 42 would get injected by the mainline, but then fail at runtime because at that point the surrounding routine that bound that return (the macro r42) has already exited.

This issue suggests changing the semantics so that return 42 actually returns from foo. This is done in practice by return binding later, at macro expansion time. No prob, since a quasi does not contain 007 code.

By the way, once we have #325, this should apply to those control-flow keywords as well.

(@vendethiel describes the above behavior as return being dynamic, but I don't think that's quite it. It's still a lexical binding, it's just that it's done a bit later, at macro expansion time. This is in analogy with #216 and macro calls, by the way. The whole thing can be justified, or at least hand-waved, because return is a statement form. In other words, this is a counterargument against making return a listop, as #300 suggests. (Because then return would just be a normal identifier, and there'd be no good reason for it to bind so late.))

@vendethiel
Copy link
Collaborator

@vendethiel describes the above behavior as return being dynamic, but I don't think that's quite it. It's still a lexical binding, it's just that it's done a bit later, at macro expansion time.

I did mean "dynamic at compile-time" (please don't make this dynamic at runtime!), I think Let Over Lambda's term of "sublexical" is applicable here.

@masak
Copy link
Owner Author

masak commented Mar 21, 2019

Same as in #159 (comment) ?

@vendethiel
Copy link
Collaborator

Yes -- you have better memory than I do.

@masak
Copy link
Owner Author

masak commented May 16, 2019

Probably not a big deal (since various dataflow steps will have to happen after macro expansion anyway), but implementing this issue means that you can't know things statically about unexpanded source with macros in it, since any given macro might contain control-flow primitives.

@vendethiel
Copy link
Collaborator

since any given macro might contain control-flow primitives.

It was already (meant to be) possible before, with exceptions.

@masak
Copy link
Owner Author

masak commented May 16, 2019

Fair point, although (correct me if I'm wrong), if you're not doing any CATCH on the mainline end, your code will either run to completion (because no exception) or die and be caught somewhere further up the stack. Whereas here, mysteryMacro() might mean that the code breaks out of your loop, or your case statement, or returns from your function.

Again, no big deal. It just means that until we've expanded fully, we don't necessarily have the full control-flow graph.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants