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

Implementation questions #5

Closed
Jack-Works opened this issue Jan 24, 2021 · 12 comments
Closed

Implementation questions #5

Jack-Works opened this issue Jan 24, 2021 · 12 comments

Comments

@Jack-Works
Copy link

I don't really understand what [?Yield, ?Await, ~Return] means. So, can I have yield or await in do expression? Can I have a return statement?

@bakkot
Copy link
Owner

bakkot commented Jan 24, 2021

It means you can have yield and await if the surrounding context allows them, and you cannot have return. This is documented (albeit briefly) in the Grammar Notation section of the spec, and I wrote up a somewhat lengthier description here.

@Jack-Works
Copy link
Author

Jack-Works commented Jan 24, 2021

I'd like to transpile do expression down to ES2020 in this way, is it likely to have a bug in it?

  1. Create a temp variable X for the do expression result
  2. If there is any await statement, mark async as true. (This finding is not across the declaration boundary)
  3. If there is any yield statement, mark generator as true. (This finding is not across the declaration boundary)
  4. Recursively traverse the statements that might contain the result expression (also not cross the boundary), change that expression to X = expr.
  5. If async is true and generator is true, replace the do expression with ((yield* (async function*() { [ do_expression_visited_block ] }).call(this)), X)
  6. If async is true and generator is false, replace the do expression with ((await (async () => { [ do_expression_visited_block ] })()), X)
  7. If async is false and generator is true, replace the do expression with ((yield* (function*() { [ do_expression_visited_block ] }).call(this)), X)
  8. If async is false and generator is false, replace the do expression with ((function () { [do_expression_visited_block ] })(), X)

@bakkot
Copy link
Owner

bakkot commented Jan 24, 2021

There's a couple corner cases where there isn't really a single "result expression" - for example, do { 0; if (foo) { 1; } } should be either 1 or undefined, never 0. And I think the async transformation will have extra microtask ticks, but I don't know how you'd avoid that. Otherwise looks reasonable.

@Jack-Works
Copy link
Author

For the if case I can add a default else block to set the result to undefined. Is there any other cases like this?

For async transformation, I have no idea but I guess it is acceptable to have extra microtasks.

@bakkot
Copy link
Owner

bakkot commented Jan 24, 2021

Is there any other cases like this?

  • switch has the same property, of course. A switch without a default, or for which any of the branches is just break or which fall off the end of the switch, should produce undefined in those cases.

  • an if branch or else or try or catch which is entirely empty, or which contains only empty blocks and empty statements (bare ;), produces undefined.

  • a labelled if (or else or try or catch) which immediately breaks itself produces undefined (as in 42; label: if (true) break label;).

It may be simpler to just put an X = void 0 before ifs, switches, and try/catch statements which fall into any of the above categories, rather than trying to work out how to set it only in those branches where it's necessary.

A further corner case: expressions in a finally don't contribute to the completion value. try { 42; } finally { 43; } produces 42, not 43.

@Jack-Works
Copy link
Author

Another question, for case, with or without default clause is exactly the same (the extra steps in default version is join them into a List). But this is different, is it intentional or a bug?
image

@bakkot
Copy link
Owner

bakkot commented Jan 24, 2021

That's a bug. The second one should match the first. Fixed in 994a5cc.

@Jack-Works
Copy link
Author

It is a Syntax Error if ContainsUndefinedBreakTarget of Block with argument « » is true.
It is a Syntax Error if ContainsUndefinedContinueTarget of Block with arguments « » and « » is true.

means break and continue cannot across the do expression block boundary?

label: while (true) x = do { break label }

Is it an early error?

@bakkot
Copy link
Owner

bakkot commented Jan 24, 2021

Yup. See the readme.

@Jack-Works
Copy link
Author

Oh, I found my transpile cannot handle this case. I think it's better to ban it as a bad practice in the type level (or make it not hoist in the spec).

function x() {
    const y = do {
        var z = 1;
        z;
    }
    z; // should be 1
}

@bakkot
Copy link
Owner

bakkot commented Jan 24, 2021

Ah, yeah, you would also have to hoist var declarations to the surrounding function. I don't think TypeScript should ban it; there's nothing obviously wrong with it (at least no more than any other nested var usage).

@Jack-Works
Copy link
Author

thanks for your answer, most of the work are done in microsoft/TypeScript#42437. there is also an online playground for it in the link

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

No branches or pull requests

2 participants