commit b815a0d1dc874df2f06ab680b5ceb74413ae4da8
parent 6cc3ee5c3dc09c4a4b0897e1f2abee120eb9d93d
Author: Sebastian <sebastian@sebsite.pw>
Date: Sun, 5 Feb 2023 00:33:05 -0500
parse: only allow exprs which mutate scope in compound/for
For example, the expression `if (foo) let bar = 0` will now error. This
applies to binding expressions and defer expressions. The one exception
is that binding expressions are allowed as the initializer of a for
expression.
Signed-off-by: Sebastian <sebastian@sebsite.pw>
Diffstat:
6 files changed, 59 insertions(+), 12 deletions(-)
diff --git a/include/parse.h b/include/parse.h
@@ -11,5 +11,6 @@ void parse(struct lexer *lexer, struct ast_subunit *unit);
bool parse_identifier(struct lexer *lexer, struct identifier *ident, bool trailing);
struct ast_type *parse_type(struct lexer *lexer);
struct ast_expression *parse_expression(struct lexer *lexer);
+struct ast_expression *parse_statement(struct lexer *lexer);
#endif
diff --git a/src/parse.c b/src/parse.c
@@ -1912,7 +1912,7 @@ parse_switch_expression(struct lexer *lexer)
struct ast_expression_list *cur = &_case->exprs;
struct ast_expression_list **next = &cur->next;
while (exprs) {
- cur->expr = parse_expression(lexer);
+ cur->expr = parse_statement(lexer);
want(lexer, T_SEMICOLON, &tok);
switch (lex(lexer, &tok)) {
@@ -1997,7 +1997,7 @@ parse_match_expression(struct lexer *lexer)
struct ast_expression_list *cur = &_case->exprs;
struct ast_expression_list **next = &cur->next;
while (exprs) {
- cur->expr = parse_expression(lexer);
+ cur->expr = parse_statement(lexer);
want(lexer, T_SEMICOLON, &tok);
switch (lex(lexer, &tok)) {
@@ -2251,7 +2251,7 @@ parse_compound_expression(struct lexer *lexer)
bool more = true;
while (more) {
- cur->expr = parse_expression(lexer);
+ cur->expr = parse_statement(lexer);
want(lexer, T_SEMICOLON, &tok);
@@ -2289,17 +2289,12 @@ parse_expression(struct lexer *lexer)
struct ast_expression *value;
switch (lex(lexer, &tok)) {
- case T_LET:
- case T_CONST:
- unlex(lexer, &tok);
- return parse_binding_list(lexer, false);
case T_STATIC:
- return parse_static_expression(lexer, true);
+ return parse_static_expression(lexer, false);
case T_BREAK:
case T_CONTINUE:
case T_RETURN:
case T_YIELD:
- case T_DEFER:
case T_FOR:
case T_IF:
case T_LBRACE:
@@ -2314,9 +2309,6 @@ parse_expression(struct lexer *lexer)
unlex(lexer, &tok);
value = parse_control_expression(lexer);
break;
- case T_DEFER:
- value = parse_deferred_expression(lexer);
- break;
case T_FOR:
unlex(lexer, &tok);
value = parse_for_expression(lexer);
@@ -2409,6 +2401,25 @@ parse_expression(struct lexer *lexer)
}
}
+struct ast_expression *
+parse_statement(struct lexer *lexer)
+{
+ struct token tok;
+ switch (lex(lexer, &tok)) {
+ case T_LET:
+ case T_CONST:
+ unlex(lexer, &tok);
+ return parse_binding_list(lexer, false);
+ case T_STATIC:
+ return parse_static_expression(lexer, true);
+ case T_DEFER:
+ return parse_deferred_expression(lexer);
+ default:
+ unlex(lexer, &tok);
+ return parse_expression(lexer);
+ }
+}
+
static char *
parse_attr_symbol(struct lexer *lexer)
{
diff --git a/tests/14-switch.ha b/tests/14-switch.ha
@@ -61,10 +61,18 @@ fn str_switching() void = {
assert(result == true);
};
+fn binding() void = {
+ switch (1) {
+ case =>
+ let x = 42;
+ };
+};
+
export fn main() void = {
basics();
termination();
tagged_result();
str_switching();
+ binding();
// TODO: Test exhaustiveness and dupe detection
};
diff --git a/tests/16-defer.ha b/tests/16-defer.ha
@@ -1,4 +1,5 @@
use rt;
+use rt::{compile, exited, EXIT_SUCCESS};
let x: int = 10;
@@ -50,6 +51,18 @@ fn control() void = {
assert(x == 1);
};
+fn reject() void = {
+ let failures = [
+ "export fn main() void = defer 0;",
+ "export fn main() void = { if (true) defer 0; };",
+ "export fn main() void = { for (defer 0; true; true) void; };",
+ ];
+
+ for (let i = 0z; i < len(failures); i += 1) {
+ assert(compile(failures[i]) as exited != EXIT_SUCCESS);
+ };
+};
+
fn noreturn() void = {
defer x = 30;
for (true) {
@@ -69,5 +82,6 @@ export fn main() void = {
scope();
loops();
control();
+ reject();
noreturn();
};
diff --git a/tests/18-match.ha b/tests/18-match.ha
@@ -230,6 +230,14 @@ fn alignment_conversion() void = {
assert(x as int == 4321);
};
+fn binding() void = {
+ let x: (int | void) = void;
+ match (x) {
+ case =>
+ let x = 42;
+ };
+};
+
export fn main() void = {
tagged();
termination();
@@ -241,5 +249,6 @@ export fn main() void = {
transitivity();
numeric();
alignment_conversion();
+ binding();
// TODO: Test exhaustiveness and dupe detection
};
diff --git a/tests/34-declarations.ha b/tests/34-declarations.ha
@@ -229,6 +229,10 @@ fn reject() void = {
"let a: int = 4; type x = struct { y: str, z: a};",
"let a: int = 4; type x = union { y: str, z: a};",
"let a: int = 4; fn x(y: str, z: a) void = { void; };",
+
+ // binding not directly in compound expression
+ "export fn main() void = let a = 4;",
+ "export fn main() void = { if (true) let a = 4; };",
];
for (let i = 0z; i < len(failures); i += 1) {