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

fix(sem): errors in lambdas no longer crash #1437

Merged

Conversation

saem
Copy link
Collaborator

@saem saem commented Aug 28, 2024

Summary

Errors in lambdas requiring inference ( proc(e: auto) = echo e ), no
longer crash the compiler. Instead errors within the lambda body are
reported and lambda inference fails as it should.

Details

semInferredLambda failed to wrap its output when there were errors.
This would reach transf and result in a compiler crash by hitting an
unreachable assertion.

Now errors in the body are reported immediately (to provide context for
inference failure wrt matching the body) and AST output from the
inference attempt is correctly wrapped such that inference appropriately
fails.

Fixes #1381

In addition, some minor refactoring was done to avoid shadowing the
input parameter n and making it look like we were mutating the
input.

Summary
---------

Errors in lambdas requiring inference (`proc(e: auto) echo e`), no
longer crash the compiler. Instead errors within the lambda body are
reported and lambda inference fails as it should.

Details
-------

`semInferredLambda` failed to wrap its output when there were errors.
This would reach `transf` and result in a compiler crash by hitting an
`unreachable` assertion.

Now errors in the body are reported immediately (to provide context for
inference failure) and AST output from the inference attempt is
correctly wrapped such that inference appropriately fails.
@saem saem added bug Something isn't working compiler/sem Related to semantic-analysis system of the compiler labels Aug 28, 2024
@zerbina zerbina self-requested a review August 29, 2024 14:16
Copy link
Collaborator

@zerbina zerbina left a comment

Choose a reason for hiding this comment

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

I left a few suggestions regarding corrections/improvements.

One problem there is with the change as it is, is that errors in the body of generic lambda expressions will now be reported twice: once in semInferredLambda, and once when the wrapper nkError is reported.

My inability to come up with a satisfying solution is why I didn't make a PR myself. What could work is only wrapping the s.ast in an error (but not result). sigmatch would then detect that case, localReport the error, and fail the overload.

It is of course also possible to just output the errors twice, though there should be an XXX/TODO/FIXME mentioning that then, I think.


Two remarks regarding the PR description:

  • inferring the auto types in the lambda expression is already done by the time semInferredLambda is called, so an error happening there is - strictly speaking - not an inference failure
  • the tree returned by instantiateTypesInBody is detached from the input AST, so there was already no input AST mutation (besides altering the symbol) going on

compiler/sem/semstmts.nim Outdated Show resolved Hide resolved
compiler/sem/semstmts.nim Show resolved Hide resolved
saem and others added 3 commits August 29, 2024 08:56
…dy_error.nim

Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com>
…dy_error.nim

Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com>
Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com>
@saem
Copy link
Collaborator Author

saem commented Aug 29, 2024

I left a few suggestions regarding corrections/improvements.

One problem there is with the change as it is, is that errors in the body of generic lambda expressions will now be reported twice: once in semInferredLambda, and once when the wrapper nkError is reported.

Doubled error reporting compared to a crash is an improvement in my mind.

My inability to come up with a satisfying solution is why I didn't make a PR myself. What could work is only wrapping the s.ast in an error (but not result). sigmatch would then detect that case, localReport the error, and fail the overload.

I think not having result be an error is the wrong intention for the procedure, if anything s.ast shouldn't be wrapped. As far as the signature goes the procedure initially matches enough, but the body has to sem in order for the inference to be considered successful, the body is sort of a contract on the auto parameter.

It is of course also possible to just output the errors twice, though there should be an XXX/TODO/FIXME mentioning that then, I think.

Two remarks regarding the PR description:

  • inferring the auto types in the lambda expression is already done by the time semInferredLambda is called, so an error happening there is - strictly speaking - not an inference failure

In my mind I consider the body of the lambda to be a part of the contract on the auto parameter.

  • the tree returned by instantiateTypesInBody is detached from the input AST, so there was already no input AST mutation (besides altering the symbol) going on

Good point, will update.

@saem
Copy link
Collaborator Author

saem commented Aug 29, 2024

So I've given this some more thought and I think the fundamental issue is that there is no way to differentiate between a failure of the contract on auto parameter via the body vs a programmer error.

If I keep following this reasoning then it seems to me that anything with auto can't exactly participate in overload resolution, or rather it needs to be "terminal" in overload resolution, because of the ambiguity above. We can't have it silently match something else, just because there is an error in the body as we can't tell if that's a contract failure or a programmer error.

So what that tells me we need to produce an error in order to halt further analysis of overloads.

Does that sound about right?

@zerbina
Copy link
Collaborator

zerbina commented Aug 29, 2024

Doubled error reporting compared to a crash is an improvement in my mind.

Of course. It wasn't mentioned anywhere, so I figured I'd at least bring it up, and like I said, double-reporting works as a solution for now.

I think not having result be an error is the wrong intention for the procedure

I agree. I was just describing a temporary compromise that could be made.

@zerbina
Copy link
Collaborator

zerbina commented Aug 29, 2024

If I keep following this reasoning then it seems to me that anything with auto can't exactly participate in overload resolution

This is more of a remark than anything else, but as far as typeRel and proc type inference are concerned, a lambda with auto parameters is no different from the symbol of a generic routine. That is:

proc generic[T](x: T) = discard

proc p(x: proc(x: int)) = discard

p generic
# is equivalent to (for typeRel):
p proc(x: auto) = ...

My opinion is that within the scope of overload resolution / parameter type inference, both should behave the same way.

Also, to be clear, this is not something that I think is blocking this PR.

@zerbina
Copy link
Collaborator

zerbina commented Aug 30, 2024

Does that sound about right?

Sorry for not answering earlier. I thought about this for a bit, but wasn't able to come up with a satisfactory answer.

Building upon what you said, there are currently three different cases for lambda body errors:

  1. an error occurred during the generic pre-pass (e.g., a symbol couldn't be bound)
  2. analysis of the body fails due to the inferred type of a parameter
  3. analysis of the body fails due to a programmer error (e.g., illegal break)

Number 1 could be detected early on (but currently isn't), while 2 and 3 are - like you said - not really distinguishable (depending on what falls under number 2).

Matching something else does seem fine to me when semInferredLambda failed for one overload since, because:

  • if there's a non-inference error, the other overloads would fail too
  • an overload with untyped might exist and could recover from the error

If semInferredLambda fails due to an inference-related error (e.g., when arg is Type: {.error.}), another non-untyped overload might still succeed.

compiler/sem/sigmatch.nim Outdated Show resolved Hide resolved
@saem
Copy link
Collaborator Author

saem commented Aug 30, 2024

/merge

Copy link

Merge requested by: @saem

Contents after the first section break of the PR description has been removed and preserved below:


Notes for Reviewers

  • not an ideal fix with the eager output, but I considered it a reasonable compromise to keep the change small

@chore-runner chore-runner bot added this pull request to the merge queue Aug 30, 2024
Merged via the queue into nim-works:devel with commit 5d3fbf4 Aug 30, 2024
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working compiler/sem Related to semantic-analysis system of the compiler
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Crash in transf due to malformed proc passed as argument
2 participants