Skip to content

Label name not checked and incorrect code emitted for label in external definition #7323

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

Closed
cknitt opened this issue Mar 7, 2025 · 2 comments · Fixed by #7338
Closed

Label name not checked and incorrect code emitted for label in external definition #7323

cknitt opened this issue Mar 7, 2025 · 2 comments · Fixed by #7338
Assignees
Labels

Comments

@cknitt
Copy link
Member

cknitt commented Mar 7, 2025

This:

@send external reduce: (array<'b>, ('a, 'b) => 'a, ~initialValue: 'a=?) => 'a = "reduce"
let reduce = (arr, initialValue, f) => reduce(arr, f, ~initialValue)

compiles fine:

function reduce(arr, initialValue, f) {
  return arr.reduce(f, initialValue);
}

However, when I change the label name:

@send external reduce: (array<'b>, ('a, 'b) => 'a, ~wrongLabelName: 'a=?) => 'a = "reduce"
let reduce = (arr, initialValue, f) => reduce(arr, f, ~initialValue)
  1. the code compiles (which it shouldn't, because the label name does not match)
  2. I get the following incorrect output (v12):
function reduce(arr, initialValue, f) {
  return arr.reduce(f)(initialValue);
}
@cknitt
Copy link
Member Author

cknitt commented Mar 7, 2025

In v11, the problem exists, too, and the output is

function reduce(arr, initialValue, f) {
  return Curry._1(arr.reduce(f), initialValue);
}

@cristianoc
Copy link
Collaborator

Here's a small repro without externals:

let r: (string, ~wrongLabelName: int=?) => 'a = (_s, ~wrongLabelName=3) => {
  let _ = wrongLabelName
  assert(false)
}

let tst1 = r("", ~initialValue=2)
let tst2 = r("")(~initialValue=2)

We have that tst1 is treated the same as tst2.

Though with the following type annotation, only tst1 fails type checking, so they're not always treated the same:

let r: (string, ~wrongLabelName: int=?) => ((~initialValue:int) => int)

cristianoc added a commit that referenced this issue Mar 13, 2025
Fixes #7323

When typing application there's a special treatment for polymorphic types, where the arity and kinds of arguments are inferred.
For example: `f => f(~lbl1, ~lbl2)` assigns a polymorphic type `'a` to `f` initially which is then instantated to `(~lbl1:t1, ~lbl2:t2) => t3`.

That same mechanism currently applies when a function is annotated to return a polymorphic type such as `(string, ~wrongLabelName: int=?) => 'a`, where the `'a` is further instantiated to extend the function type with additional arguments.
This mechanism is OK for curried function, but incorrect for uncurried functions: while e.g. `'a => 'b` with curried function designates any function where the first argument is unlabeled, with uncurried function it only designates functions of arity 1.
So when processing application, `'b` should not be expanded further.

Two examples of problematic code that now gives type error:
```res
let r: (string, ~wrongLabelName: int=?) => 'a = (_s, ~wrongLabelName=3) => {
  let _ = wrongLabelName
  assert(false)
}

let tst1 = r("", ~initialValue=2)
let tst2 = r("")(~initialValue=2)
```

and

```res
let f  = (_, ~def=3) => assert(false)

let g1 = f(1,2)
let g2 = f(1)(2)
```
cristianoc added a commit that referenced this issue Mar 14, 2025
* Fix issue with typing application and polymorphic types.

Fixes #7323

When typing application there's a special treatment for polymorphic types, where the arity and kinds of arguments are inferred.
For example: `f => f(~lbl1, ~lbl2)` assigns a polymorphic type `'a` to `f` initially which is then instantated to `(~lbl1:t1, ~lbl2:t2) => t3`.

That same mechanism currently applies when a function is annotated to return a polymorphic type such as `(string, ~wrongLabelName: int=?) => 'a`, where the `'a` is further instantiated to extend the function type with additional arguments.
This mechanism is OK for curried function, but incorrect for uncurried functions: while e.g. `'a => 'b` with curried function designates any function where the first argument is unlabeled, with uncurried function it only designates functions of arity 1.
So when processing application, `'b` should not be expanded further.

Two examples of problematic code that now gives type error:
```res
let r: (string, ~wrongLabelName: int=?) => 'a = (_s, ~wrongLabelName=3) => {
  let _ = wrongLabelName
  assert(false)
}

let tst1 = r("", ~initialValue=2)
let tst2 = r("")(~initialValue=2)
```

and

```res
let f  = (_, ~def=3) => assert(false)

let g1 = f(1,2)
let g2 = f(1)(2)
```

* Cleanup and type errot tests.
@github-project-automation github-project-automation bot moved this from Ready to Done in ReScript development Mar 14, 2025
fhammerschmidt pushed a commit that referenced this issue Apr 4, 2025
* Fix issue with typing application and polymorphic types.

Fixes #7323

When typing application there's a special treatment for polymorphic types, where the arity and kinds of arguments are inferred.
For example: `f => f(~lbl1, ~lbl2)` assigns a polymorphic type `'a` to `f` initially which is then instantated to `(~lbl1:t1, ~lbl2:t2) => t3`.

That same mechanism currently applies when a function is annotated to return a polymorphic type such as `(string, ~wrongLabelName: int=?) => 'a`, where the `'a` is further instantiated to extend the function type with additional arguments.
This mechanism is OK for curried function, but incorrect for uncurried functions: while e.g. `'a => 'b` with curried function designates any function where the first argument is unlabeled, with uncurried function it only designates functions of arity 1.
So when processing application, `'b` should not be expanded further.

Two examples of problematic code that now gives type error:
```res
let r: (string, ~wrongLabelName: int=?) => 'a = (_s, ~wrongLabelName=3) => {
  let _ = wrongLabelName
  assert(false)
}

let tst1 = r("", ~initialValue=2)
let tst2 = r("")(~initialValue=2)
```

and

```res
let f  = (_, ~def=3) => assert(false)

let g1 = f(1,2)
let g2 = f(1)(2)
```

* Cleanup and type errot tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants