resolver: validate placeholder expression values

68871bf512dee78311c3f1ec87b93295434b32206d053b8da83fcf6054fae496
Alexis Sellier committed ago 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 +
}