From 7f9b23076d66763297dd5308f80e7fd1e3261416 2021-12-15 16:56:29 From: mh Date: 2021-12-15 16:56:29 Subject: [PATCH] Add some tests for tuple member access --- diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index 8ea043156fec0af942858ff46cb79ce861946c6a..130de897237dfa1854d68ef1225d452e527db3b2 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -488,12 +488,18 @@ impl Prompt { let (deallocate_heap_pos, value_to_push) = match subject { Value::Ref(value_ref) => { let subject = self.store.read_ref(value_ref); - let subject_heap_pos = subject.as_struct(); + let subject_heap_pos = match expr.kind { + SelectKind::StructField(_) => subject.as_struct(), + SelectKind::TupleMember(_) => subject.as_tuple(), + }; (None, Value::Ref(ValueId::Heap(subject_heap_pos, field_idx))) }, _ => { - let subject_heap_pos = subject.as_struct(); + let subject_heap_pos = match expr.kind { + SelectKind::StructField(_) => subject.as_struct(), + SelectKind::TupleMember(_) => subject.as_tuple(), + }; let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, field_idx)); (Some(subject_heap_pos), self.store.clone_value(subject_indexed)) }, diff --git a/src/protocol/eval/store.rs b/src/protocol/eval/store.rs index 4b598afe4fe7b31f566f3e3df2ff7ac336064666..7b1ce034f8d1b31e2da8a8c09b90c0f10ff09bab 100644 --- a/src/protocol/eval/store.rs +++ b/src/protocol/eval/store.rs @@ -189,6 +189,7 @@ impl Store { Value::Array(_) => Value::Array(target_heap_pos), Value::Union(tag, _) => Value::Union(tag, target_heap_pos), Value::Struct(_) => Value::Struct(target_heap_pos), + Value::Tuple(_) => Value::Tuple(target_heap_pos), _ => unreachable!("performed clone_value on heap, but {:?} is not a heap value", value), } } diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index 1ccb132cc8fa8ba49c3e8f6a2f696258c8c88c1f..8a6f7235acfa8c341c8f5f5dfd2ffc8b34d4146a 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -3301,7 +3301,10 @@ impl PassTyping { Literal::Enum(_) | Literal::Union(_) | Literal::Struct(_) => true, _ => false, }, - Expression::Select(_) => true, + Expression::Select(expr) => match expr.kind { + SelectKind::StructField(_) => true, + SelectKind::TupleMember(_) => false, + }, _ => false, }; diff --git a/src/protocol/tests/eval_operators.rs b/src/protocol/tests/eval_operators.rs index de63537538834df9eb6d2c0582077ccc7d5a0168..9f16b0bb9bce077911bcca37746b6b44f9d491ac 100644 --- a/src/protocol/tests/eval_operators.rs +++ b/src/protocol/tests/eval_operators.rs @@ -147,7 +147,7 @@ fn test_binary_integer_operators() { } #[test] -fn test_tuple_operators() { +fn test_tuple_comparison_operators() { Tester::new_single_source_expect_ok("tuple equality", " func test_func() -> bool { auto a1 = (8, 16, 32); @@ -178,6 +178,40 @@ fn test_tuple_operators() { }); } +#[test] +fn test_tuple_select_operator() { + Tester::new_single_source_expect_ok("tuple member assignment", " + func test() -> bool { + auto tuple = (0, 1, 0, 1); + tuple.0 = cast(1); + tuple.1 = cast(2); + tuple.2 = cast(3); + tuple.3 = cast(4); + return cast(tuple.0) + cast(tuple.1) + tuple.2 + cast(tuple.3) == 10; + } + ").for_function("test", |f| { f + .for_variable("tuple", |v| { v.assert_concrete_type("(u8,u16,u32,u64)"); }) + .call_ok(Some(Value::Bool(true))); + }); + + Tester::new_single_source_expect_ok("tuple polymorph member access", " + func sum_variant_a((A,B,C,D) v) -> C { + return cast(v.0) + cast(v.1) + v.2 + cast(v.3); + } + func sum_variant_b((A,B,C,D) i) -> B { + (A,B,C,D) c = (0,0,0,0); + c.0 = i.0; c.1 = i.1; c.2 = i.2; c.3 = i.3; + return cast(c.0) + c.1 + cast(c.2) + cast(c.3); + } + func test() -> bool { + (u8,u16,u32,u64) tuple = (1, 2, 3, 4); + return sum_variant_a(tuple) == 10 && sum_variant_b(tuple) == 10; + } + ").for_function("test", |f| { f + .call_ok(Some(Value::Bool(true))); + }); +} + #[test] fn test_string_operators() { Tester::new_single_source_expect_ok("string concatenation", " diff --git a/src/protocol/tests/parser_inference.rs b/src/protocol/tests/parser_inference.rs index fdcaa200f22ea77044e4313d91c8e660f131b2ac..44dcd85a51a0797fed6e79151940b2c57e4bd322 100644 --- a/src/protocol/tests/parser_inference.rs +++ b/src/protocol/tests/parser_inference.rs @@ -122,7 +122,45 @@ fn test_binary_expr_inference() { }); } +#[test] +fn test_tuple_inference() { + Tester::new_single_source_expect_ok( + "from tuple to variable", + " + func test() -> u32 { + (u8,u16,u32,u64) tuple = (1, 2, 3, 4); + auto a = tuple.0; + auto b = tuple.1; + auto c = tuple.2; + auto d = tuple.3; + return cast(a) + cast(b) + c + cast(d); + } + " + ).for_function("test", |f| { f + .for_variable("a", |v| { v.assert_concrete_type("u8"); }) + .for_variable("b", |v| { v.assert_concrete_type("u16"); }) + .for_variable("c", |v| { v.assert_concrete_type("u32"); }) + .for_variable("d", |v| { v.assert_concrete_type("u64"); }) + .call_ok(Some(Value::UInt32(10))); + }); + Tester::new_single_source_expect_ok( + "from variable to tuple", + " + func test() -> u32 { + auto tuple = (1, 2, 3, 4); + u8 a = tuple.0; + u16 b = tuple.1; + u32 c = tuple.2; + u64 d = tuple.3; + return cast(a) + cast(b) + c + cast(d); + } + " + ).for_function("test", |f| { f + .for_variable("tuple", |v| { v.assert_concrete_type("(u8,u16,u32,u64)"); }) + .call_ok(Some(Value::UInt32(10))); + }); +} #[test] fn test_struct_inference() { diff --git a/src/protocol/tests/parser_validation.rs b/src/protocol/tests/parser_validation.rs index 02f8d7756691d1ba32c0ff764af5f6a0bd3cac4f..0462c7e1e7f81070d022ab9fa36ad21f66d6203c 100644 --- a/src/protocol/tests/parser_validation.rs +++ b/src/protocol/tests/parser_validation.rs @@ -476,6 +476,38 @@ fn test_incorrect_tuple_polymorph_args() { }); } +#[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) 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(