Changeset - b708c094d3a2
[Not reviewed]
0 2 0
MH - 4 years ago 2021-12-20 18:21:27
contact@maxhenger.nl
WIP on fixing select variable introduction
2 files changed with 52 insertions and 1 deletions:
0 comments (0 inline, 0 general)
src/protocol/tests/parser_binding.rs
Show inline comments
 
use super::*;
 

	
 
#[test]
 
fn test_correct_binding() {
 
    Tester::new_single_source_expect_ok("binding bare", "
 
        enum TestEnum{ A, B }
 
        union TestUnion{ A(u32), B }
 
        struct TestStruct{ u32 field }
 

	
 
        func foo() -> u32 {
 
            auto lit_enum_a = TestEnum::A;
 
            auto lit_enum_b = TestEnum::B;
 
            auto lit_union_a = TestUnion::A(0);
 
            auto lit_union_b = TestUnion::B;
 
            auto lit_struct = TestStruct{ field: 0 };
 

	
 
            if (let test_enum_a = lit_enum_a)   { auto can_use = test_enum_a; }
 
            if (let test_enum_b = lit_enum_b)   { auto can_use = test_enum_b; }
 
            if (let test_union_a = lit_union_a) { auto can_use = test_union_a; }
 
            if (let test_union_b = lit_union_b) { auto can_use = test_union_b; }
 
            if (let test_struct = lit_struct)   { auto can_use = test_struct; }
 

	
 
            return 0;
 
        }
 
    ").for_function("foo", |f| { f
 
        .for_variable("test_enum_a", |v| { v.assert_concrete_type("TestEnum"); })
 
        .for_variable("test_enum_b", |v| { v.assert_concrete_type("TestEnum"); })
 
        .for_variable("test_union_a", |v| { v.assert_concrete_type("TestUnion"); })
 
        .for_variable("test_union_b", |v| { v.assert_concrete_type("TestUnion"); })
 
        .for_variable("test_struct", |v| { v.assert_concrete_type("TestStruct"); });
 
    });
 
}
 

	
 
#[test]
 
fn test_incorrect_binding() {
 
    Tester::new_single_source_expect_err("binding at statement level", "
 
        func foo() -> bool {
 
            return let a = 5;
 
        }
 
    ").error(|e| { e
 
        .assert_num(1)
 
        .assert_occurs_at(0, "let")
 
        .assert_msg_has(0, "only be used inside the testing expression");
 
    });
 

	
 
    Tester::new_single_source_expect_err("nested bindings", "
 
        struct Struct{ bool field }
 
        func foo() -> bool {
 
            if (let Struct{ field: let a = Struct{ field: false } } = Struct{ field: true }) {
 
                return test;
 
            }
 
            return false;
 
        }
 
    ").error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "let a = ")
 
        .assert_msg_has(0, "nested binding")
 
        .assert_occurs_at(1, "let Struct")
 
        .assert_msg_has(1, "outer binding");
 
    });
 
}
 

	
 
#[test]
 
fn test_incorrect_binding_variable() {
 
    // Note: if variable already exists then it is interpreted at the binding
 
    // expression as value. So the case where "the variable is already defined"
 
    // results in no binding variable.
 

	
 
    Tester::new_single_source_expect_err("binding var in next scope", "
 
        union Option<T>{ Some(T), None }
 
        func foo() -> bool {
 
            auto opt = Option::Some(false);
 
            if (let Option::Some(var) = opt) {
 
                auto var = true; // should mismatch against binding 'var'
 
                return var;
 
            }
 
            return false;
 
        }
 
    ").error(|e| { e
 
        .assert_num(2)
 
        .assert_msg_has(0, "variable name conflicts")
 
        .assert_occurs_at(0, "var = true;")
 
        .assert_occurs_at(1, "var) = opt");
 
    });
 

	
 
    Tester::new_single_source_expect_err("binding var in nested scope", "
 
        union LR<A, B>{ L(A), R(B) }
 
        func foo() -> u32 {
 
            LR<auto, u32> x = LR::L(5);
 
            if (let LR::L(y) = x) {
 
                if (true) {
 
                    auto y = 5;
 
                    return y;
 
                }
 
            }
 
        }
 
    ").error(|e| { e
 
        .assert_num(2)
 
        .assert_msg_has(0, "variable name conflicts")
 
        .assert_occurs_at(0, "y = 5")
 
        .assert_occurs_at(1, "y) = x");
 
    });
 
}
 

	
 
#[test]
 
fn test_boolean_ops_on_binding() {
 
    Tester::new_single_source_expect_ok("apply && to binding result", "
 
        union TestUnion{ Two(u16), Four(u32), Eight(u64) }
 
        func foo() -> u32 {
 
            auto lit_2 = TestUnion::Two(2);
 
            auto lit_4 = TestUnion::Four(4);
 
            auto lit_8 = TestUnion::Eight(8);
 

	
 
            // Testing combined forms of bindings
 
            if (
 
                let TestUnion::Two(test_2) = lit_2 &&
 
                let TestUnion::Four(test_4) = lit_4 &&
 
                let TestUnion::Eight(test_8) = lit_8
 
            ) {
 
                auto valid_2 = test_2;
 
                auto valid_4 = test_4;
 
                auto valid_8 = test_8;
 
            }
 

	
 
            // Testing in combination with regular expressions, and to the correct
 
            // literals
 
            if (let TestUnion::Two(inter_a) = lit_2 && 5 + 2 == 7)               { inter_a = 0; }
 
            if (5 + 2 == 7 && let TestUnion::Two(inter_b) = lit_2)               { inter_b = 0; }
 
            if (2 + 2 == 4 && let TestUnion::Two(inter_c) = lit_2 && 3 + 3 == 8) { inter_c = 0; }
 

	
 
            // Testing with the 'incorrect' target union
 
            if (let TestUnion::Four(nope) = lit_2 && let TestUnion::Two(zilch) = lit_8) { }
 

	
 
            return 0;
 
        }
 
    ").for_function("foo", |f| { f
 
        .for_variable("valid_2", |v| { v.assert_concrete_type("u16"); })
 
        .for_variable("valid_4", |v| { v.assert_concrete_type("u32"); })
 
        .for_variable("valid_8", |v| { v.assert_concrete_type("u64"); })
 
        .for_variable("inter_a", |v| { v.assert_concrete_type("u16"); })
 
        .for_variable("inter_b", |v| { v.assert_concrete_type("u16"); })
 
        .for_variable("inter_c", |v| { v.assert_concrete_type("u16"); });
 
    });
 

	
 
    Tester::new_single_source_expect_err("apply || before binding", "
 
        enum Test{ A, B }
 
        func foo() -> u32 {
 
            if (let a = Test::A || 5 + 2 == 7) {
 
                auto mission_impossible = 5;
 
            }
 
            return 0;
 
        }
 
    ").error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "let a")
 
        .assert_msg_has(0, "only the logical-and operator")
 
        .assert_occurs_at(1, "||")
 
        .assert_msg_has(1, "disallowed operation");
 
    });
 

	
 
    Tester::new_single_source_expect_err("apply || after binding", "
 
        enum Test{ A, B }
 
        func foo() -> u32 {
 
            if (5 + 2 == 7 || let b = Test::B) {
 
                auto magic_number = 7;
 
            }
 
            return 0;
 
        }
 
    ").error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "let b")
 
        .assert_msg_has(0, "only the logical-and operator")
 
        .assert_occurs_at(1, "||")
 
        .assert_msg_has(1, "disallowed operation");
 
    });
 

	
 
    Tester::new_single_source_expect_err("apply || before and after binding", "
 
        enum Test{ A, B }
 
        func foo() -> u32 {
 
            if (1 + 2 == 3 || (let a = Test::A && let b = Test::B) || (2 + 2 == 4)) {
 
                auto darth_vader_says = \"Noooooooooo\";
 
            }
 
            return 0;
 
        }
 
    ").error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "let a")
 
        .assert_msg_has(0, "only the logical-and operator")
 
        .assert_occurs_at(1, "|| (let a")
 
        .assert_msg_has(1, "disallowed operation");
 
    });
 
}
 
\ No newline at end of file
src/protocol/tests/parser_validation.rs
Show inline comments
 
@@ -430,199 +430,208 @@ fn test_correct_tuple_polymorph_args() {
 
            auto a = Option<()>::None;
 
            auto b = Option<(u32, u64)>::None;
 
            auto c = Option<(Option<(u8, s8)>, Option<(s8, u8)>)>::None;
 
            return 0;
 
        }
 
        "
 
    ).for_union("Option", |u| { u
 
        .assert_has_monomorph("Option<()>")
 
        .assert_has_monomorph("Option<(u32,u64)>")
 
        .assert_has_monomorph("Option<(Option<(u8,s8)>,Option<(s8,u8)>)>")
 
        .assert_size_alignment("Option<()>", 1, 1, 0, 0)
 
        .assert_size_alignment("Option<(u32,u64)>", 24, 8, 0, 0) // (u32, u64) becomes size 16, alignment 8. Hence union tag is aligned to 8
 
        .assert_size_alignment("Option<(Option<(u8,s8)>,Option<(s8,u8)>)>", 7, 1, 0, 0); // inner unions are size 3, alignment 1. Two of those with a tag is size 7
 
    });
 
}
 

	
 
#[test]
 
fn test_incorrect_tuple_polymorph_args() {
 
    // Do some mismatching brackets. I don't know what else to test
 
    Tester::new_single_source_expect_err(
 
        "mismatch angle bracket",
 
        "
 
        union Option<T>{ Some(T), None }
 
        func f() -> u32 {
 
            auto a = Option<(u32>)::None;
 
            return 0;
 
        }"
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_msg_has(0, "closing '>'").assert_occurs_at(0, ">)::None")
 
        .assert_msg_has(1, "match this '('").assert_occurs_at(1, "(u32>");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "wrongly placed angle",
 
        "
 
        union O<T>{ S(T), N }
 
        func f() -> u32 {
 
            auto a = O<(<u32>)>::None;
 
            return 0;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_msg_has(0, "expected typename")
 
        .assert_occurs_at(0, "<u32");
 
    });
 
}
 

	
 
#[test]
 
fn test_incorrect_tuple_member_access() {
 
    Tester::new_single_source_expect_err(
 
        "zero-tuple",
 
        "func foo() -> () { () a = (); auto b = a.0; return a; }"
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_msg_has(0, "out of bounds")
 
        .assert_occurs_at(0, "a.0");
 
    });
 

	
 
    // Make the type checker do some shenanigans before we can decide the tuple
 
    // type.
 
    Tester::new_single_source_expect_err(
 
        "sized tuple",
 
        "
 
        func determinator<A,B>((A,B,A) v) -> B { return v.1; }
 
        func tester() -> u64 {
 
            auto v = (0,1,2);
 
            u32 a_u32 = 5;
 
            v.2 = a_u32;
 
            v.8 = 5;
 
            return determinator(v);
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_msg_has(0, "out of bounds")
 
        .assert_occurs_at(0, "v.8");
 
    });
 
}
 

	
 
#[test]
 
fn test_polymorph_array_types() {
 
    Tester::new_single_source_expect_ok(
 
        "array of polymorph in struct",
 
        "
 
        struct Foo<T> { T[] hello }
 
        struct Bar { Foo<u32>[] world }
 
        "
 
    ).for_struct("Bar", |s| { s
 
        .for_field("world", |f| { f.assert_parser_type("Foo<u32>[]"); });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "array of port in struct",
 
        "
 
        struct Bar { in<u32>[] inputs }
 
        "
 
    ).for_struct("Bar", |s| { s
 
        .for_field("inputs", |f| { f.assert_parser_type("in<u32>[]"); });
 
    });
 
}
 

	
 
#[test]
 
fn test_correct_modifying_operators() {
 
    // Not testing the types, just that it parses
 
    Tester::new_single_source_expect_ok(
 
        "valid uses",
 
        "
 
        func f() -> u32 {
 
            auto a = 5;
 
            a += 2; a -= 2; a *= 2; a /= 2; a %= 2;
 
            a <<= 2; a >>= 2;
 
            a |= 2; a &= 2; a ^= 2;
 
            return a;
 
        }
 
        "
 
    );
 
}
 

	
 
#[test]
 
fn test_incorrect_modifying_operators() {
 
    Tester::new_single_source_expect_err(
 
        "wrong declaration",
 
        "func f() -> u8 { auto a += 2; return a; }"
 
    ).error(|e| { e.assert_msg_has(0, "expected '='"); });
 

	
 
    Tester::new_single_source_expect_err(
 
        "inside function",
 
        "func f(u32 a) -> u32 { auto b = 0; auto c = f(a += 2); }"
 
    ).error(|e| { e.assert_msg_has(0, "assignments are statements"); });
 

	
 
    Tester::new_single_source_expect_err(
 
        "inside tuple",
 
        "func f(u32 a) -> u32 { auto b = (a += 2, a /= 2); return 0; }"
 
    ).error(|e| { e.assert_msg_has(0, "assignments are statements"); });
 
}
 

	
 
#[test]
 
fn test_variable_introduction_in_scope() {
 
    Tester::new_single_source_expect_err(
 
        "variable use before declaration",
 
        "func f() -> u8 { return thing; auto thing = 5; }"
 
    ).error(|e| { e.assert_msg_has(0, "unresolved variable"); });
 

	
 
    Tester::new_single_source_expect_err(
 
        "variable use in declaration",
 
        "func f() -> u8 { auto thing = 5 + thing; return thing; }"
 
    ).error(|e| { e.assert_msg_has(0, "unresolved variable"); });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "variable use after declaration",
 
        "func f() -> u8 { auto thing = 5; return thing; }"
 
    );
 

	
 
    Tester::new_single_source_expect_err(
 
        "variable use of closed scope",
 
        "func f() -> u8 { { auto thing = 5; } return thing; }"
 
    ).error(|e| { e.assert_msg_has(0, "unresolved variable"); });
 
}
 

	
 
#[test]
 
fn test_correct_select_statement() {
 
    Tester::new_single_source_expect_ok(
 
        "correct single-use", "
 
        primitive f() {
 
            channel unused_output -> input;
 
            u32 outer_value = 0;
 
            sync select {
 
                outer_value = get(input) -> outer_value = 0;
 
                auto new_value = get(input) -> {
 
                    outer_value = new_value;
 
                }
 
                get(input) + get(input) ->
 
                    outer_value = 8;
 
                get(input) ->
 
                    {}
 
                outer_value %= get(input) -> {
 
                    outer_value *= outer_value;
 
                    auto new_value = get(input);
 
                    outer_value += new_value;
 
                }
 
            }
 
        }
 
        "
 
    );
 
}
 

	
 
#[test]
 
fn test_incorrect_select_statement() {
 
    Tester::new_single_source_expect_err(
 
        "outside sync",
 
        "func f() -> u32 { select {} return 0; }"
 
        "primitive f() { select {} }"
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_occurs_at(0, "select")
 
        .assert_msg_has(0, "inside sync blocks");
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "variable in previous block",
 
        "primitive f() {
 
            channel<u32> tx -> rx;
 
            u32 a = 0; // this one will be shadowed
 
            sync select { auto a = get(rx) -> {} }
 
        }"
 
    );
 
}
 
\ No newline at end of file
0 comments (0 inline, 0 general)