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 MCS work when left-hand operand is erroneous #1257

Closed
wants to merge 4 commits into from

Conversation

zerbina
Copy link
Collaborator

@zerbina zerbina commented Mar 28, 2024

Summary

When using the method-call syntax, templates/macros with untyped
parameters are now properly resolved to for a.f, a.f(b), and
a.f = b, when a is itself not semantically valid. The manual is
updated accordingly.

Fixes #1255.

Details

In builtinFieldAccess, the typed left-hand expression was directly
written back to the input AST, meaning that in case of an error, the
resulting nkDotCall already contained an error, preventing recovery
via an untyped template/macro.

The input AST is now only modified if the left-hand expression is not
an error, so that the dot-call rewrite gets the original AST. A
regression test covering the three basic dot expression rewrites is
added.

Since the first operand in a .(a, b) (originating from a.b) can
now be untyped, resolveOverloads now needs to ensure that said
operand is typed before attempting to access the expression's type.


Notes for Reviewers

  • builtinFieldAccess should not modify the input AST at all, but that requires a larger refactor

zerbina added 4 commits March 28, 2024 02:04
Eagerly updating the input AST in `builtinFieldAccess` prevents `x.y`
succeeding as `y(x)` when `x` is an error. The input AST is now only
modified (ideally it shouldn't be at all) when the LHS is not an
error, meaning that `y(x)` gets the untyped `x` when `x` turned out to
be an error.

In addition, `i` is renamed to `rhs`, making what it represents more
clear.
`semOverloadedCall` expected the first operand of a call that was
originally a dot expression to be typed, but this is now not always
the case anymore.
It's gone now. In general, such limitations shouldn't be specified in
the manual -- they're bugs!
@zerbina zerbina added bug Something isn't working compiler/sem Related to semantic-analysis system of the compiler labels Mar 28, 2024
@zerbina zerbina requested a review from saem March 28, 2024 13:48
discard

# must not result in an "undeclared identifier" error
nonexistentName.f
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is susceptible to double evaluation of compile time effects? (static: echo "hi"; nonexistentName).f

Longer term with both untyped overloads being analysed first and perhaps restricting compile time effects/making them transactional maybe that's not an issue? There might be precedence issues with the former. 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yep, it is susceptible to the double evaluation, as well as leakage of symbol table changes ((var x = 0; nonexistentName).f).

I figured that, while not ideal, this is an improvement over now, where (static: ...; nonexistentName).f doesn't compile at all.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm trying to think of whether this will allow something to work that never should have. If it doesn't then that's an improvement, otherwise we're accepting known invalid programs, which would be a regression.


Possible rewrite: I thought about this possible rewrite, but I think it leads to a bunch more issues than necessarily all that much better a solution.

An alternative/complementary approach I was thinking of was what if we did some untyped AST rewriting to solve the precedence issues, which I believe this is?

For example, (var x = 0; nonexistentName).f, can be thought of as (var x = 0; nonexistentName.f). Pretty sure we can come up with a rewrite rule(s) over untyped AST that has the desired effect. I guess the only "issue" is that an un/typed leading parameter used in an MCS won't receive the entire parenthetical syntax, but that wouldn't happen now anyways.

The rewrite rules might be:

  1. If there is only one constituent unwrap
  2. If there is more than one, wrap the last one in parenthesis, and move the dot operator and RHS in, then apply the rules again

After this change we're saying that an untyped dot operator (pre-rewriting rule) has higher priority than the untyped parenthetical expression.

Once evaluating untyped parameter overloads first rule goes in, we can defer the rewrite until the untyped are handled, meaning we'll be able to see the entire LHS in those cases.

@zerbina
Copy link
Collaborator Author

zerbina commented Mar 29, 2024

@saem and I continued the discussion on Matrix, and the conclusion was that a template like template f(x: untyped) should generally not match for x.f, at least for now.

Therefore, the method-call syntax limitation as described in the manual is going to stay.

@zerbina zerbina closed this Mar 29, 2024
@zerbina zerbina deleted the sem-fix-mcs-with-errors branch March 31, 2024 16:21
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.

UFCS issue iterator into macro
2 participants