-
Notifications
You must be signed in to change notification settings - Fork 9
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
Improve callstack errors, check for recursion. #126
Conversation
3e01529
to
16fdd36
Compare
(env-gasmodel "table") | ||
(env-gaslimit 10) ; ensures test does not run forever in case recursion breaks | ||
|
||
(expect-failure "Recursion should fail @ runtime" (knot2.callF knot1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's add a case for direct self-recursion as well. Also, is mutual recursion covered for both modrefs and two concrete mutually recursive calls?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, a case with direct self recursion will not compile.
checkRecursion = do | ||
RecursionCheck currentCalled <- usesEvalState esCheckRecursion NE.head | ||
let qn = fqnToQualName (_sfName sf) | ||
when (S.member qn currentCalled) $ throwExecutionError info (RuntimeRecursionDetected qn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So correct me if i'm wrong, but the process is as follows:
When we see a fqn that's new, we check the current frame calling context to see if there's already a reference to it anywhere, treating the NonEmpty RecursionCheck
state as a second stack that tracks names that it's already seen, and if it has been seen already, abort, but if not, load it into the associated recursion tracking context if it's not.
This could get pretty costly. We'll need to find a way to gas this because any significantly complicated defun
is going to have a lot of recursion checks no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My gut feel is this shouldn't be too costly relative to the other stuff CEK machine is doing, but it'd make a great benchmark case! I can take a look at that separately soon-ish.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could get pretty costly.
Why would this be costly? The set will not grow very large in practice, and it just checks a qualified name.
We'll need to find a way to gas this because any significantly complicated defun is going to have a lot of recursion checks no?
We already charge an overhead for function application. The overhead covers the cost of a few things anyway:
- Pushing the arguments onto the environment
- Pushing a stackframe into the context stack
- Recursion checking.
In practice, that set will be small, ankd even if it were 10000 functions deep, it would not be slow enough for it to matter in the grand scheme of things, I believe, but we will simply use the benchmarks for function app to verify this 👍
This PR improves the state of error messages in core, as well as adds a runtime recursion check for module references.
Given this repl file:
Pre-this PR, the following is a repl snippet of the error emitted by loading the file:
Post-this, the error improves to
PR checklist: