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

Properly support tuple field accessors typing #69

Closed
LPTK opened this issue Jan 14, 2022 · 4 comments · Fixed by #90
Closed

Properly support tuple field accessors typing #69

LPTK opened this issue Jan 14, 2022 · 4 comments · Fixed by #90

Comments

@LPTK
Copy link
Contributor

LPTK commented Jan 14, 2022

There are currently at least two ways in which they are broken:

  1. While they work on simple types thanks to the case in ConstraintSolver:

    t = (1, 2, 3)
    //│ t: (1, 2, 3,)
    //│  = [ 1, 2, 3 ]
    
    t._1
    //│ res: 1
    //│    = undefined

    they don't work for more complex cases, because the normal forms don't know about them:

    t = (1, 2, 3) with {x = 1}
    // t = (1, 2, 3)
    //│ t: (1, 2, 3,) & {x: 1}
    //│  = Array { '0': 1, '1': 2, '2': 3, x: 1 }
    
    t._1
    //│ ╔══[ERROR] Type mismatch in field selection:
    //│ ║  l.301: 	t._1
    //│ ║         	^^^^
    //│ ╟── expression of type `(1, 2, 3,) & {x: 1}` does not match type `{_1: ?a}`
    //│ ║  l.294: 	t = (1, 2, 3) with {x = 1}
    //│ ║         	     ^^^^^^^^^^^^^^^^^^^^^
    //│ ╟── but it flows into reference with expected type `{_1: ?a}`
    //│ ║  l.301: 	t._1
    //│ ╙──       	^
  2. We can subvert them, causing a soundness problem:

    t = (1, 2, 3) with {_1 = "oops"}
    //│ t: (1, 2, 3,) & {_1: "oops"}
    //│  = Array { '0': 1, '1': 2, '2': 3, _1: 'oops' }
    
    (t: (int,int,int,))._1
    //│ res: int
    //│    = 'oops'
@LPTK
Copy link
Contributor Author

LPTK commented Jan 14, 2022

Related JS-gen issue: #65 (comment)

@LPTK
Copy link
Contributor Author

LPTK commented Jan 20, 2022

I realize the right way of doing this is to change the way field removal types act on tuples (and arrays).

Currently it leaves tuples untouched:

case _: TypeVariable | _: ClassTag | _: RecordType => rw

We should add a field in TupleType and ArrayType to remember which fields have been hidden by a Without type applied on such types.

(Cc @Meowcolm024.)

@LPTK
Copy link
Contributor Author

LPTK commented Feb 7, 2022

Another improvement request: properly reason about intersected tuple-record types, as mentioned in #79 (comment).

For example:

def test: (string, 1) & { _1: "hello" }
//│ test: (string, 1,) & {_1: "hello", _2: 1}

The above should simplify to test: ("hello", 1,).

Moreover in:

test: { _1: 'a }
//│ res: {_1: "hello"}

test: ('a, 1)
//│ res: (string, 1,)

I would have expected to get type ("hello", 1,) for the latter.

Similarly, the following could "simplify" to res: Array[1] & {_1: 1}:

error: Array[1] & { _1: int }
//│ res: Array[1] & {_1: int}

@LPTK LPTK mentioned this issue Feb 7, 2022
@LPTK
Copy link
Contributor Author

LPTK commented Mar 8, 2022

I realize the right way of doing this is to change the way field removal types act on tuples (and arrays).

Note: To make things simpler, we didn't go with this approach. Instead, we just convert these types to records upon removing fields from them, which is not optimal ((1, 2) with {_1 = 3} is typed as {_1: 3, _2: 2} whereas it could be typed as (3, 2)), but probably sufficient for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant