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 super error mishandling uncurried function #6694

Merged

Conversation

JonoPrest
Copy link
Contributor

Closes #6662

Super errors were not handling uncurried functions properly.

let apply = (fn: (. unit) => option<int>) => fn(. ())

let _ = apply(Some(1))

outputted:

The constructor Some does not belong to type function$
  
  This variant expression is expected to have type . unit => option<int>

whereas it should have outputted

  This value might need to be wrapped in a function that takes an extra
  parameter of type unit
                                                                                                                                            
  Here's the original error message
  This has type: option<'a>
  But it's expected to have type: (. unit) => option<int>

There were a number of place where uncurried functions were not being handled here and I suspect there's a number of gremlins still around.

Comment on lines +10 to +15
This value might need to be [1;33mwrapped in a function that takes an extra
parameter[0m of type unit

[1;33mHere's the original error message[0m
This has type: [1;31moption<'a>[0m
But it's expected to have type: [1;33m(. unit) => option<int>[0m
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This now matches the super error that was outputted when a curried function was defined

let typeIsUncurriedFun = Ast_uncurried_utils.typeIsUncurriedFun

let typeExtractUncurriedFun (typ : Parsetree.core_type) =
let coreTypeExtractUncurriedFun (typ : Parsetree.core_type) =
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed from typeExtractUncurriedFun coreTypeExtractUncurriedFun to follow naming convention in this file. I I have redefined typeExtractUncurriedFun below. (coreTypeIsUncurriedFun is named above fore the same paramater type, and for functions with Types.type_expr as a param use typeIsUncurredFun and typeExtractUncurriedFun respectively)

Comment on lines +207 to +208
&& Path.same expectedPath Predef.path_option = false
&& Path.same expectedPath Predef.path_uncurried = false ->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here was another gremlin I came across where you get a suggestion to unwrap your option. For the example in the tests/descriptioon you get:

  This value might need to be wrapped in a function that takes an extra
  parameter of type unit
                                                                                                                                            
  Here's the original error message
  This has type: option<'a>
  But it's expected to have type: (. unit) => option<int>
                                                                                                                                            
  Possible solutions:
  - Unwrap the option to its underlying value using `yourValue->Belt.Option.getWithDefault(someDefaultValue)`

Where the possible solution is not meant to be for functions but uncurried functions still pattern match on this case.

Comment on lines +310 to +312
(p0, p, {type_kind=Type_variant cstrs})
when not (Ast_uncurried.typeIsUncurriedFun ty)
-> (p0, p, cstrs)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was the first issue, leading to the initial bad super error being displayed. The uncurried function type would extract as a concrete variant since it matched this case when it should not.

Copy link
Member

Choose a reason for hiding this comment

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

I'm sure you've read and know more about this, but instead of throwing an exception with Not_found if uncurried, what about an implementation that extracts the uncurried case? Something like this.

if Ast_uncurried_utils.typeIsUncurriedFun ty then
   extract_concrete_variant env (Ast_uncurried.typeExtractUncurriedFun ty)
else (p0, p, cstrs)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey @mununki thanks for the comment! Yes I think that feel a lot nicer except that in practice it would make no difference to this function since if it is an uncurried function the extracted type would not be Type_variant? So happy to add the refactor to make it more future proof but otherwise it should never hit a case where it is an uncurried function and it can extract a concrete variant

Copy link
Member

Choose a reason for hiding this comment

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

Sorry for my misunderstanding, if so why do we need that when clause by then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for my misunderstanding, if so why do we need that when clause by then?

It's because an uncurried function is represented by this pattern

Tconstr (Pident {name = "function$"}, [tArg; _], _)

where tArg is the underlying type of Tarrow

So when trying to extract the concrete type as a variant, it matches the case of Tconstr, but is not actually variant it's essentially just a wrapper around the underlying function. So we get a false positive.

Comment on lines 667 to 669
| t when Ast_uncurried_utils.typeIsUncurriedFun t ->
let typ = Ast_uncurried.typeExtractUncurriedFun t in
collect_missing_arguments env typ type2
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was the second issue where it should have given the suggestion:

  This value might need to be wrapped in a function that takes an extra
  parameter of type unit
                                                                                                                                            
  Here's the original error message
  This has type: option<'a>
  But it's expected to have type: (. unit) => option<int>
                                                                                                                                            

it only outputted

  This has type: option<'a>
  But it's expected to have type: (. unit) => option<int>

since it would not match on the cases which treat it as a function. We first need to extract the type of the uncurried function.

@JonoPrest JonoPrest force-pushed the fix-unhandled-uncurried-fn-types branch from 889c2d8 to 2b74747 Compare March 24, 2024 22:14
Copy link
Collaborator

@cristianoc cristianoc left a comment

Choose a reason for hiding this comment

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

Thanks for the fix. This looks great!

Looks like something went off when updating the changelog.

@JonoPrest JonoPrest force-pushed the fix-unhandled-uncurried-fn-types branch from 0a38ecf to 6b9d9dc Compare March 25, 2024 08:28
@JonoPrest
Copy link
Contributor Author

Thanks for the fix. This looks great!

Looks like something went off when updating the changelog.

Thanks @cristianoc, I think there was an issue when I rebased on GH and then added another commit locally. I've fixed up the commit rebased and pushed.

@zth
Copy link
Collaborator

zth commented Mar 25, 2024

@JonoPrest impressive work, well done! 😄

@zth zth merged commit c8c4b3a into rescript-lang:11.0_release Mar 25, 2024
14 checks passed
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.

4 participants