Skip to content

chapel-py: expose Dyno error data in Python API, improve error messages.#28391

Merged
DanilaFe merged 32 commits intochapel-lang:mainfrom
DanilaFe:expose-error-data
Feb 12, 2026
Merged

chapel-py: expose Dyno error data in Python API, improve error messages.#28391
DanilaFe merged 32 commits intochapel-lang:mainfrom
DanilaFe:expose-error-data

Conversation

@DanilaFe
Copy link
Contributor

@DanilaFe DanilaFe commented Feb 11, 2026

This all started because the language server was not giving me detailed enough information about why calls are not resolving.

Screenshot 2026-02-10 at 4 22 20 PM

Why are there no matching candidates?!

I think there might be some kind of method or setting in my editor to show detailed information (which would include the failing candidates). However, if only a particular argument is causing the problem (as it is in the case above), why not highlight that specific argument?

This was not possible in chapel-py's previous state of things. It exposed the parent Error class to Python, which included .message() and the additional notes. However, these notes are in plain English, and make it hard to work backwards to find the (e.g.) actual that caused the issue. However, from the get-go (as described in #20701), our errors were designed to separate the content of the error from its presentation. To this end, different error sub-classes contain bundles (tuples) of information, from which we can determine the erroneous actual.

The goal of this PR, then, was to plumb this wealth of additional information into chapel-py proper. I did this by:

  • Using the established "generated types" approach to create a Python class corresponding to each error message type. This approach was previously applied to AST nodes, types, and params. I modify generated-types-list.h to adapt all the error messages we have listed in error-classes-list.h. Notably GeneralError is not a generated type in plain Dyno, but I treat it as such in chapel-py for uniformity. This required a tiny bit of boilerplate on Dyno's front, adding ->toBlaError() methods to ErrorBase.
  • To expose each individual error's info bundles, I needed to provide a Python info method for each error message. The type of this info method differs for every error message, but more importantly, not all information exposed by error messages can be converted into Python data. To make progress, and to guard against future error messages adding new, unsupported error information, I wrote some template code to produce 'Python-safe' tuples from arbitrary ->info data.
    • These tuples replace unsupported types (ones that aren't explicitly "blessed" as Python-compatible) with zero-element tuples. There might be other options (std::monostate?), but we already have support for tuples, so using them was the path of least resistance.
    • Unlike "regular" methods (as in uast-methods.h), which can be precise about whether their return type can be nullptr or not via the Nilable wrapper, the error-classes-list.h macro header does not provide such information. Thus, we must assume any pointer type contained in the ->info() can be nullptr. This requires a recursive transformation that tags pointer types as such, implemented by CanConvert::transform.
    • Finally, I used the error-classes-list.h header to generate each unique ->info() methods within the method table. All this was big and complicated enough that I broke out a separate error-methods.h header.

The net result is a Python class hierarchy with info methods that produce tuples of the data we required.
Screenshot 2026-02-10 at 5 18 26 PM

To make my specific improvement, I then exposed ApplicabilityResult, a list of which is included in the NoMatchingCandidates error. This ApplicabilityResult includes the actual of the call that could not be passed. This conversion is nigh-trivial, since our PythonClassWithContext abstraction provides working defaults with nearly no additional boilerplate.

With this, I was finally able to write my logic in chapel.lsp to provide more specific locations for errors in which a single actual caused all candidate matching failures. I also went ahead and provided nicer error locations for a few other error messages.

Before After
Screenshot 2026-02-10 at 5 25 30 PM Screenshot 2026-02-10 at 5 25 17 PM

My future work for this is to explore using the newly-provided error information for other things, such as fixits for simple errors. This PR is complicated enough now that I do not intend to do that here.

Reviewed by @jabraham17 -- thanks!

Testing

  • dyno tests
  • CLS tests

Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
When converting to Python, we strip off some data if we can't
pass it. That means we can't restore that data back from
Python.

Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
@DanilaFe DanilaFe marked this pull request as ready for review February 11, 2026 01:55
@DanilaFe DanilaFe requested a review from jabraham17 February 11, 2026 01:55
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Comment on lines +246 to +258
/** Different error types have different arguments in their '->info()' tuple,
not all of which can be converetd to Python. Also, the C preprocessor
doesn't allow us to use commas in macro arguments, so we can't just write
std::tuple<EINFO> to get a tuple of an error's info. Instead, use template
specialization here to define a ErrorInfoBundle with a type alias that
is fully convertible.

It seems to be a good idea to keep the tuple returned by Python's
version of `->info()` always the same size as the original tuple. This
way, as more components of the error info become convertible,
they can be added to the returned tuple without changing user code that
unpacks the tuple. Thus, we pad the tuple with 'dummy' elements for
non-convertible types.
Copy link
Member

Choose a reason for hiding this comment

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

I won't pretend to full understand this code. But I see no obvious errors

if as_ is not None:
location = as_.location()
elif isinstance(error, chapel.TertiaryUseImportUnstable):
node = error.info()[1]
Copy link
Member

Choose a reason for hiding this comment

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

nit, use tuple unpacking like the other statements

related_info = None
location = error.location()
if isinstance(error, chapel.NoMatchingCandidates):
(call, _, appress, _) = error.info()
Copy link
Member

Choose a reason for hiding this comment

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

It took me way too long to grok that appress -> ApplicabilityResult. Consider app_results and app_result

Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
…ense

...and since I'm not currently providing related information.

Signed-off-by: Danila Fedorin <daniel.fedorin@hpe.com>
@DanilaFe DanilaFe merged commit c1c52c3 into chapel-lang:main Feb 12, 2026
11 checks passed
DanilaFe added a commit that referenced this pull request Mar 4, 2026
There have been some indications that after
#28391, it was harder to
compile chapel-py due to memory constraints. This is plausible, since we
have added a whole variety of new generated methods, and a whole new
class hierarchy. To help address this, this PR splits the file that
generates all the methods from the `metho-tables.h` into separate source
files.

## Testing
- [x] chapel-py builds
- [x] CLS tests still pass
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.

2 participants