Skip to content

Start work on improving subtype error messages #7404

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

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

zth
Copy link
Collaborator

@zth zth commented Apr 24, 2025

This improves the subtype error messages to make them understandable/actionable where relevant.

In this first iteration I've focused on adding optional context to each subtype error. We use that to, where possible, provide better subtype errors, that are actionable.

This does not cover all cases, and there's plenty of follow ups (some listed below). But, it's a start, and validates the concept that we can do this this way.

Follow ups

Will put these in a separate issue to be tracked

  • Variant constructor inline record payloads
  • Variant constructor tuple payloads
  • Record representation
  • Polyvariant :> variant

Then there's the larger follow up of tracking where in a nested subtype check an error is happening. Example: Records in records, where a record further down the type structure is not a subtype.

@zth zth force-pushed the better-subtype-error-messages branch 2 times, most recently from 55476b9 to 1093261 Compare May 23, 2025 11:02
issues: Record_coercion.record_field_subtype_violation list;
}

exception Subtype of type_pairs * type_pairs * subtype_context option
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Adding subtype_context is the real change here. It tries to track the context a subtype issue was found in, so better error messages can be presented.

| Tarrow (l1, t1, u1, _, _), Tarrow (l2, t2, u2, _, _)
when Asttypes.Noloc.same_arg_label l1 l2 ->
let cstrs = subtype_rec env ((t2, t1) :: trace) t2 t1 cstrs in
subtype_rec env ((u1, u2) :: trace) u1 u2 cstrs
| Ttuple tl1, Ttuple tl2 -> subtype_list env trace tl1 tl2 cstrs
| Ttuple tl1, Ttuple tl2 ->
(* TODO(subtype-errors) Tuple as context *)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can leave these TODO:s in here since I'll do another pass on this to extend it more soonish.

Copy link

pkg-pr-new bot commented May 23, 2025

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript-lang/rescript@7404

@rescript/darwin-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/darwin-x64@7404

@rescript/darwin-arm64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/darwin-arm64@7404

@rescript/linux-arm64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/linux-arm64@7404

@rescript/win32-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/win32-x64@7404

@rescript/linux-x64

npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/linux-x64@7404

commit: 8c13179

@zth zth changed the title [WIP] Improve subtype error messages Start work on improving subtype error messages May 23, 2025
@zth zth force-pushed the better-subtype-error-messages branch from fcffc7e to e3a2b95 Compare May 23, 2025 12:02
@zth zth marked this pull request as ready for review May 23, 2025 12:02
@zth zth requested review from cristianoc, cknitt and tsnobip and removed request for cristianoc May 23, 2025 12:03
@zth
Copy link
Collaborator Author

zth commented May 23, 2025

@cristianoc would you mind having a look at the general approach here, to make sure it's good enough?

@tsnobip @cknitt would you mind looking at the error messages, like you have in the previous PRs? 🙏

Copy link
Member

@tsnobip tsnobip left a comment

Choose a reason for hiding this comment

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

@zth doing God's work! Keep them coming!

Comment on lines +12 to +13
The record x cannot be coerced to the record y because:
- The field x is optional in record x, but is not optional in record y
Copy link
Member

@tsnobip tsnobip May 23, 2025

Choose a reason for hiding this comment

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

the error message is very detailed and insightful, nice! That reminds me that the other way around is not yet possible unfortunately, you can't coerce to a type that has more optional fields than the original type.

module Foo = {
  type t = {foo: int}
}

module Bar = {
  type t = {foo: int, bar?: int}
}

let foo = {Foo.foo: 1}

let bar = (foo :> Bar.t) // error

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Would that be reasonable to support?

Copy link
Member

@tsnobip tsnobip May 23, 2025

Choose a reason for hiding this comment

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

definitely low-priority but I once had the need for such a thing and I can't think of any argument against it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@cristianoc you see any issues with supporting it?

Type x is not a subtype of y

The record x cannot be coerced to the record y because:
- Field x runtime representation is configured to be "z" (via the @as attribute) in record y, but in record x it is configured to be "w" (via the @as attribute). Runtime representations must match.
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 really nice!

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.

3 participants