roc-lang / roc

A fast, friendly, functional language.
https://roc-lang.org
Universal Permissive License v1.0
4.39k stars 308 forks source link

Optional record field type error #5444

Open bhansconnect opened 1 year ago

bhansconnect commented 1 year ago

Apparently using an optional record field as both optional and required can break type checking:

put this in repl or any file and roc check it:

f : { val ? U64 } -> { val: U64 } 
f = \{val ? 7} -> {val}

g : {} -> { val: U64 } 
g = \{} -> f {val: 9}

h : {} -> { val: U64 } 
h = \{} -> f {}

will get:

── TYPE MISMATCH ───────────────────────────────────────────────────────────────

This 1st argument to f has an unexpected type:

11│      h = \{} -> f {}
                      ^^

The argument is a record of type:

    {}

But f needs its 1st argument to be:

    { val : Int Unsigned64 }

Tip: Looks like the val field is missing.
jecaro commented 10 months ago

Having worked a bit on the issue, I am logging here my progress. I came up with a simpler repro that can be added to crates/compiler/solve/tests/solve_expr.rs

    #[test]
    fn optional_field_bug() {
        infer_eq_without_problem(
            indoc!(
                r#"
                f : { val ? Bool }  -> Bool
                f = \{val ? Bool.false} -> val

                g = f {val: Bool.true}

                f
                "#
            ),
            "{ val ? Bool } -> Bool",
        );
    }

That test fails with:

---- solve_expr::optional_field_bug stdout ----
thread 'solve_expr::optional_field_bug' panicked at 'assertion failed: `(left == right)`

Diff < left / right > :
<{ val : Bool } -> Bool
>{ val ? Bool } -> Bool

Somehow it seems that type-checking the g function changes the signature of f. Other interesting observations are: