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

Initialize AtomConstructor's fields via local vars #3330

Merged
merged 13 commits into from
Mar 21, 2022

Conversation

hubertp
Copy link
Contributor

@hubertp hubertp commented Mar 9, 2022

Pull Request Description

The mechanism follows a similar approach to what is being in functions
with default arguments.
Additionally since InstantiateAtomNode wasn't a subtype of EnsoRootNode it
couldn't be used in the application, which was the primary reason for
issue #181449213.
Alternatively InstantiateAtomNode could have been enhanced to extend
EnsoRootNode rather than RootNode to carry scope info but the former
seemed simpler.

See test cases for previously crashing and invalid cases.

Important Notes

Checklist

Please include the following checklist in your PR:

  • The documentation has been updated if necessary.
  • All code conforms to the Scala, Java, and Rust style guides.
  • All code has been tested:
    • Unit tests have been written where possible.
    • If GUI codebase was changed: Enso GUI was tested when built using BOTH ./run dist and ./run watch.

@hubertp hubertp marked this pull request as draft March 9, 2022 11:24
@hubertp hubertp requested a review from kustosz March 9, 2022 11:25
Copy link
Contributor

@kustosz kustosz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hubertp
Copy link
Contributor Author

hubertp commented Mar 14, 2022

@kustosz can you have another look, please? I ended up partially reverting the previous solution to be able to carry information about the already evaluated arguments. It works but I'm not sure this is "the right way" to do it.

Copy link
Contributor

@kustosz kustosz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't right. Look at how default arguments are handled inside functions – there's a bunch of assignments of the form

<arg_name> = ReadArgumentNode(arg_pos)

and then anything that wants to get its hands on arg_name just uses the variable, not the argument. It's logically similar to what you've done, but it avoids by-name lookups and other such slow operations. In order for that to work, you'll have to fix the compiler, such that the variables in constructors get bound properly and are not dynamic symbol.

Also, please switch to develop and run sbt runtime/bench. It's our main benchmark suite for the interpreter and it's persistent – when you run it again you'll get a comparison, please make sure to run it on each PR. Just by looking at the changes in DynamicSymbolNode, I'm 99% percent sure some of these benchmarks are at least 10X slower with your changes (anything to do with method dispatch will suffer). My gut instinct is, when you implement this properly, there will be no noticeable performance impact.

@hubertp hubertp force-pushed the wip/hubert/default-arg-crash-181449213 branch from 4839e0a to b8e932b Compare March 16, 2022 14:41
The mechanism follows a similar approach to what is being in functions
with default arguments.
Additionally since InstantiateAtomNode wasn't a subtype of EnsoRootNode it
couldn't be used in the application, which was the primary reason for
issue #181449213.
Alternatively InstantiateAtomNode could have been enhanced to extend
EnsoRootNode rather than RootNode to carry scope info but the former
seemed simpler.

See test cases for previously crashing and invalid cases.
@hubertp hubertp force-pushed the wip/hubert/default-arg-crash-181449213 branch from b8e932b to 311cab0 Compare March 16, 2022 15:17
@hubertp hubertp marked this pull request as ready for review March 16, 2022 15:32
@hubertp hubertp requested a review from kustosz March 16, 2022 15:32
@hubertp
Copy link
Contributor Author

hubertp commented Mar 16, 2022

This is now out of the draft state and ready for the final review.

@hubertp hubertp changed the title Use ClosureRootNode to build AtomConstructor function Initialize AtomConstructor's fields via local vars Mar 16, 2022
Copy link
Contributor

@kustosz kustosz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much better, but I still don't like the stylistic choice here, InstantiateNode knows too much, even though there's no reason to change it all. If you just look at InstantiateNode before, what was it saying about the API? All it said was "look, I have a bunch of ExpressionNode children, I don't care what they are, but they are supposed to produce my field values". Now it's a mess of this node knowing about some 2 phases etc. And then this choice comes right back to bite you, when you have to write this:

    for (int i=0; i<args.length; i++) {
      assignments[i] = ConstantObjectNode.build(null);
      reads[i] = ReadArgumentNode.build(i, null);
    }

Clearly there's something wrong if you have to lie like this.

OK, so the alternative:

  1. Don't change the structure of InstantiateNode.
  2. When building it from code, generate the arg read/assign exprs separately. Then generate an instantiate node with var read children (instead of arg read). Then return a BlockNode of all assignments, returning the instantiate node. So, in pseudo code, the whole thing becomes:
do
  var0 = readArg(0)
  ...
  varN = readArg(N)
  return Instantiate(Constructor, var0, ..., varN)
end

which is much cleaner (I can't even write sensical pseudocode for the other solution).
3. Bonus: when there are no defaulted args (e.g. in builtin constructors), BUT ALSO, as an optimization (premature and probably useless, don't do it!) in actual code paths , the original solution of generating return Instantiate(Constructor, readArg(0), ..., readArg(N)) still works fine, without any nasty hacks.

Copy link
Contributor

@kustosz kustosz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much better! Approval is conditional on:

  1. Fixing the minor doc issue
  2. Your confirmation of having run the benchmarks and there not being a slowdown :)

@hubertp
Copy link
Contributor Author

hubertp commented Mar 18, 2022

Benchmarks passed locally. @4e6 @radeusgd @jdunkerley 👍 ?

@hubertp hubertp added the CI: Ready to merge This PR is eligible for automatic merge label Mar 18, 2022
Copy link
Contributor

@4e6 4e6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just a comment about formatting.

AliasAnalysis,
"No occurrence on an argument definition."
)
.unsafeAs[AliasAnalysis.Info.Occurrence]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please run scalafmt on this file

@mergify mergify bot merged commit 66e2135 into develop Mar 21, 2022
@mergify mergify bot deleted the wip/hubert/default-arg-crash-181449213 branch March 21, 2022 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: Ready to merge This PR is eligible for automatic merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants