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

Improve error reporting of named argument mismatch #12238

Open
wants to merge 22 commits into
base: develop
Choose a base branch
from

Conversation

radeusgd
Copy link
Member

@radeusgd radeusgd commented Feb 5, 2025

Pull Request Description

Important Notes

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • Screenshots/screencasts have been attached, if there are any visual changes. For interactive or animated visual changes, a screencast is preferred.
  • All code follows the
    Scala,
    Java,
    TypeScript,
    and
    Rust
    style guides. In case you are using a language not listed above, follow the Rust style guide.
  • Unit tests have been written where possible.
  • If meaningful changes were made to logic or tests affecting Enso Cloud integration in the libraries,
    or the Snowflake database integration, a run of the Extra Tests has been scheduled.
    • If applicable, it is suggested to paste a link to a successful run of the Extra Tests.

@radeusgd radeusgd force-pushed the wip/radeusgd/improve-mismatched-named-argument-error branch from c71e95c to 343673d Compare February 5, 2025 17:37
Copy link
Member

@JaroslavTulach JaroslavTulach left a comment

Choose a reason for hiding this comment

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

  • adventurous: I am afraid to touch CurryNode & co. myself
  • changing the type of thrown panic is an incompatible change
    • better to keep the atom and add an info field to it
  • are the benchmarks result OK?
    • Probably they are (as these are just exceptional paths and they are unlikely tested).
  • one minor @CompilationFinal note - otherwise looks decent

@@ -78,7 +78,8 @@ private void initializeOversaturatedCallNode(
InvokeCallableNodeGen.create(
postApplicationSchema.getOversaturatedArguments(),
defaultsExecutionMode,
argumentsExecutionMode);
argumentsExecutionMode,
true);
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need new boolean argument? Isn't postApplicationSchema.getOversaturatedArguments() good enough indicator that there are oversaturated arguments?

Copy link
Member Author

Choose a reason for hiding this comment

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

But the create method/constructor does not see postApplicationSchema, it sees the result of postApplicationSchema.getOversaturatedArguments() which is just a CallArgumentInfo[] schema.

From inside of InvokeCallableNodeGen.create you can no longer tell if the arguments being passed were 'oversaturated' or 'normal' - you just get some schema, that is the same kind as in all different places where InvokeCallableNode is constructed.

That's why this argument is added to be able to distinguish the situations at call-site.

@@ -375,8 +381,17 @@ static Object doPolyglot(
@Fallback
public Object invokeGeneric(
Object callable, VirtualFrame callerFrame, State state, Object[] arguments) {
Atom error = EnsoContext.get(this).getBuiltins().error().makeNotInvokable(callable);
throw new PanicException(error, this);
if (isForOversaturatedArguments && schema.length >= 1 && schema[0].isNamed()) {
Copy link
Member

Choose a reason for hiding this comment

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

Is this a compilation constant? Try CompilerAsserts.partialEvaluationContant or co.

Copy link
Member Author

Choose a reason for hiding this comment

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

Tried in e645b05, is that how it is supposed to be used?

@radeusgd radeusgd changed the title Separate error for named argument mismatch Improve error reporting of named argument mismatch Feb 6, 2025
@radeusgd radeusgd self-assigned this Feb 6, 2025
@radeusgd
Copy link
Member Author

radeusgd commented Feb 6, 2025

Double checked in the GUI:
image

The widgets that rely on cache seem to all work fine - so the tweaking of the annotation invocation code seems to be all good.

@radeusgd
Copy link
Member Author

radeusgd commented Feb 6, 2025

are the benchmarks result OK?

  • Probably they are (as these are just exceptional paths and they are unlikely tested).

I will schedule a run once I ensure that the tests are passing.

@radeusgd
Copy link
Member Author

radeusgd commented Feb 6, 2025

image


Arguments:
- argument_name: The name of the argument that was not found.
Error argument_name
Copy link
Member

Choose a reason for hiding this comment

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

Remove ## PRIVATE use:

Suggested change
Error argument_name
private Error argument_name

possibly remove the documentation comment altogether.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is against the convention that we are using for all the errors.

I think changes in this PR should be consistent with the rest of the codebase - if we want to change the convention let's discuss and consider rewriting the whole Common.enso file.

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't want the constructor to be private, this breaks the very simple check I've implemented like payload.cause == (No_Such_Argument.Error "foo"), at least outside of Base.

The PRIVATE is to avoid cluttering Component Browser only, I guess at some point we may remove that tag from errors.

@@ -310,12 +310,32 @@ type Not_Invokable

Arguments:
- target: The called object.
Error target
- cause: Additional information about what may have caused the error.
Error target cause
Copy link
Member

Choose a reason for hiding this comment

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

This is still an incompatible change for the creators of this object. To make the change compatible use:

Suggested change
Error target cause
Error target cause=Nothing

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok

Copy link
Member Author

Choose a reason for hiding this comment

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

I've tried and unfortunately constructors of builtins do not like default arguments.

With the change above, the code

from Standard.Base import all
import Standard.Base.Errors.Common.Not_Invokable

main =
    without_cause = Not_Invokable.Error "thing"
    IO.println without_cause

still prints

Not_Invokable.Error target='thing' cause=_

I think the defaultness has to be handled in engine and we don't have capability to do that.

Technically, even with the default argument this change would still breaking as any pattern matching of Not_Invokable.Error _ also will now mismatch the amount of fields.

We could try to have 2 constructors for Not_Invokable - the old one and the new one. But I think this is complicating stuff unnecessarily. Also not sure if the builtins machinery would currently allow this, as it would no longer be a UniquelyConstructibleBuiltin.

## If the cache argument was not expected, we ignore the problem
and return just the result of `f value` - held by
the target of the Not_Invokable error.
was_not_expecting_cache_argument = caught_panic.payload.cause == (No_Such_Argument.Error "cache")
Copy link
Member

Choose a reason for hiding this comment

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

Question to myself: Why do I find this unreadable? Would following be better?

f : Function ->
    Panic.catch Not Invocable (f value cache=cache) caught_panic -> case caught.panic.payload.cause of
       No_Such_Argument.Error "cache" -> caught_panic.payload.target
       _ -> Panic.throw caught_panic

Maybe, but caught.panic.payload.cause is still quite long...

f : Function ->
# We pass the self value and the cache to the function.
Panic.catch Not_Invokable (f value cache=cache) caught_panic->
## If the cache argument was not expected, we ignore the problem
Copy link
Member

Choose a reason for hiding this comment

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

Why do you use ## instead of # here?

Suggested change
## If the cache argument was not expected, we ignore the problem
# If the cache argument was not expected, we ignore the problem

Copy link
Member Author

Choose a reason for hiding this comment

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

Because the ## allows to write a multi-line comment, and I don't want to create lines 300+ characters long.

@@ -375,7 +385,16 @@ static Object doPolyglot(
@Fallback
public Object invokeGeneric(
Object callable, VirtualFrame callerFrame, State state, Object[] arguments) {
Atom error = EnsoContext.get(this).getBuiltins().error().makeNotInvokable(callable);
Atom cause = null;
Copy link
Member

Choose a reason for hiding this comment

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

The same code is already present in IndirectInvokeCallableNode once. Can we refactor it to a single method?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmmm, perhaps a good point.

Any suggestions where this common function could live? Put it as static in InvokeCallableNode and call from IndirectInvokeCallableNode?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 97b7d27

@radeusgd
Copy link
Member Author

radeusgd commented Feb 7, 2025

Running benchmarks:

GitHub
Enso Analytics is a self-service data prep and analysis platform designed for data teams. - Benchmark Engine · af3ba05
GitHub
Enso Analytics is a self-service data prep and analysis platform designed for data teams. - Benchmark Standard Libraries · e6af654

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

Successfully merging this pull request may close these issues.

5 participants