resolver: validate placeholder expression values
68871bf512dee78311c3f1ec87b93295434b32206d053b8da83fcf6054fae496
1 parent
05ad7df3
lib/std/lang/resolver.rad
+3 -1
| 552 | 552 | InvalidAlignmentValue(u32), |
|
| 553 | 553 | /// Invalid module path. |
|
| 554 | 554 | InvalidModulePath, |
|
| 555 | 555 | /// Invalid identifier. |
|
| 556 | 556 | InvalidIdentifier(*ast::Node), |
|
| 557 | + | /// Placeholder used where a value expression is required. |
|
| 558 | + | PlaceholderExpression, |
|
| 557 | 559 | /// Invalid scope access. |
|
| 558 | 560 | InvalidScopeAccess, |
|
| 559 | 561 | /// Referenced an unknown array field. |
|
| 560 | 562 | ArrayFieldUnknown(*[u8]), |
|
| 561 | 563 | /// Referenced an unknown slice field. |
| 4077 | 4079 | negative: false, |
|
| 4078 | 4080 | })); |
|
| 4079 | 4081 | return setNodeType(self, node, Type::Int); |
|
| 4080 | 4082 | }, |
|
| 4081 | 4083 | case ast::NodeValue::Placeholder => { |
|
| 4082 | - | return setNodeType(self, node, hint); |
|
| 4084 | + | throw emitError(self, node, ErrorKind::PlaceholderExpression); |
|
| 4083 | 4085 | }, |
|
| 4084 | 4086 | else => { |
|
| 4085 | 4087 | throw emitError(self, node, ErrorKind::UnexpectedNode(node)); |
|
| 4086 | 4088 | } |
|
| 4087 | 4089 | } |
lib/std/lang/resolver/printer.rad
+3 -0
| 443 | 443 | io::print("unexpected return statement"); |
|
| 444 | 444 | } |
|
| 445 | 445 | case super::ErrorKind::UnexpectedNode(n) => { |
|
| 446 | 446 | io::print("unexpected expression"); |
|
| 447 | 447 | } |
|
| 448 | + | case super::ErrorKind::PlaceholderExpression => { |
|
| 449 | + | io::print("placeholder cannot be used as a value"); |
|
| 450 | + | } |
|
| 448 | 451 | case super::ErrorKind::UnexpectedModuleName => { |
|
| 449 | 452 | io::print("unexpected module name"); |
|
| 450 | 453 | } |
|
| 451 | 454 | case super::ErrorKind::FnMissingBody => { |
|
| 452 | 455 | io::print("function is missing a body"); |
lib/std/lang/resolver/tests.rad
+2 -12
| 1 | 1 | //! Resolver tests. |
|
| 2 | 2 | ||
| 3 | 3 | /// Region identity, borrowing, and nominal application tests. |
|
| 4 | 4 | @test mod regions; |
|
| 5 | + | /// Placeholder binding, pattern, and expression tests. |
|
| 6 | + | @test mod placeholderTests; |
|
| 5 | 7 | ||
| 6 | 8 | use std::mem; |
|
| 7 | 9 | use std::testing; |
|
| 8 | 10 | use std::lang::alloc; |
|
| 9 | 11 | use std::lang::ast; |
| 5749 | 5751 | let result = try resolveProgramStr(&mut a, program); |
|
| 5750 | 5752 | try expectNoErrors(&result); |
|
| 5751 | 5753 | } |
|
| 5752 | 5754 | } |
|
| 5753 | 5755 | ||
| 5754 | - | /// Test array pattern with placeholder elements. |
|
| 5755 | - | /// Pattern syntax: `[_, y]` ignores first element. |
|
| 5756 | - | @test unsafe fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) { |
|
| 5757 | - | let mut testArena389 = testArena(); |
|
| 5758 | - | let testStorage389: 'test389 = &mut testArena389 in { |
|
| 5759 | - | let mut a = testResolver(testStorage389); |
|
| 5760 | - | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }"; |
|
| 5761 | - | let result = try resolveProgramStr(&mut a, program); |
|
| 5762 | - | try expectNoErrors(&result); |
|
| 5763 | - | } |
|
| 5764 | - | } |
|
| 5765 | - | ||
| 5766 | 5756 | /// Test identifier pattern that binds the whole value. |
|
| 5767 | 5757 | /// Pattern syntax: `x` matches any value and binds it. |
|
| 5768 | 5758 | @test unsafe fn testResolveMatchIdentPattern() throws (testing::TestError) { |
|
| 5769 | 5759 | let mut testArena390 = testArena(); |
|
| 5770 | 5760 | let testStorage390: 'test390 = &mut testArena390 in { |
lib/std/lang/resolver/tests/placeholderTests.rad
added
+35 -0
| 1 | + | //! Placeholder resolver tests. |
|
| 2 | + | ||
| 3 | + | use std::testing; |
|
| 4 | + | use std::lang::resolver; |
|
| 5 | + | ||
| 6 | + | /// Test array pattern with placeholder elements. |
|
| 7 | + | /// Pattern syntax: `[_, y]` ignores first element. |
|
| 8 | + | @test unsafe fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) { |
|
| 9 | + | let mut testArena389 = super::testArena(); |
|
| 10 | + | let testStorage389: 'test389 = &mut testArena389 in { |
|
| 11 | + | let mut a = super::testResolver(testStorage389); |
|
| 12 | + | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }"; |
|
| 13 | + | let result = try super::resolveProgramStr(&mut a, program); |
|
| 14 | + | try super::expectNoErrors(&result); |
|
| 15 | + | } |
|
| 16 | + | } |
|
| 17 | + | ||
| 18 | + | /// Placeholders cannot supply values to expressions. |
|
| 19 | + | @test unsafe fn testPlaceholderValueRejected() throws (testing::TestError) { |
|
| 20 | + | for program in [ |
|
| 21 | + | "fn value() -> u32 { return _; }", |
|
| 22 | + | "fn take(value: u32) {} fn run() { take(_); }", |
|
| 23 | + | "record R: Copy { value: u32 } fn run() { let value = R { value: _ }; }", |
|
| 24 | + | "fn run() { let value: [u32; 1] = [_]; }", |
|
| 25 | + | "union U: Copy { Value(u32) } fn run() { let value = U::Value(_); }", |
|
| 26 | + | "union U: Copy { Value(u32) } fn run(value: &?U) { match value { case U::Value(_) => {} else => {} } }", |
|
| 27 | + | ] { |
|
| 28 | + | let mut arena = super::testArena(); |
|
| 29 | + | let storage: 'placeholder = &mut arena in { |
|
| 30 | + | let mut res = super::testResolver(storage); |
|
| 31 | + | let result = try super::resolveProgramStr(&mut res, program); |
|
| 32 | + | try super::expectErrorKind(&result, resolver::ErrorKind::PlaceholderExpression); |
|
| 33 | + | } |
|
| 34 | + | } |
|
| 35 | + | } |
std.lib.test
+1 -0
| 11 | 11 | lib/std/lang/il/tests.rad |
|
| 12 | 12 | lib/std/lang/parser/tests.rad |
|
| 13 | 13 | lib/std/lang/module/tests.rad |
|
| 14 | 14 | lib/std/lang/scanner/tests.rad |
|
| 15 | 15 | lib/std/lang/resolver/tests.rad |
|
| 16 | + | lib/std/lang/resolver/tests/placeholderTests.rad |
|
| 16 | 17 | lib/std/lang/il/binary/tests.rad |
|
| 17 | 18 | lib/std/lang/il/binary/decodeTests.rad |
|
| 18 | 19 | lib/std/arch/rv64/image/tests.rad |
|
| 19 | 20 | lib/std/arch/rv64/shared/tests.rad |
|
| 20 | 21 | lib/std/arch/rv64/bounds.rad |
test/tests/placeholder.value.reject.rad
added
+15 -0
| 1 | + | //! rejects: placeholder cannot be used as a value |
|
| 2 | + | ||
| 3 | + | /// Value used to check a placeholder constructor argument. |
|
| 4 | + | union Value: Copy { |
|
| 5 | + | /// Numeric value with one payload field. |
|
| 6 | + | Number(u32), |
|
| 7 | + | } |
|
| 8 | + | ||
| 9 | + | /// Attempt to match an optional value with a constructor expression. |
|
| 10 | + | fn invalid(value: &?Value) -> bool { |
|
| 11 | + | match value { |
|
| 12 | + | case Value::Number(_) => return true, |
|
| 13 | + | else => return false, |
|
| 14 | + | } |
|
| 15 | + | } |