aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2024-06-23 21:18:44 +0200
committerBad Diode <bd@badd10de.dev>2024-06-23 21:18:44 +0200
commit25444d18759bb7dde28dc6cb7a1554a08a125e34 (patch)
tree8e934c5e72bf124e6a558acc1dfa2607740652a1
parent42046a947e1ad636a1ee23be58ce0be2d8abbab2 (diff)
downloadbdl-25444d18759bb7dde28dc6cb7a1554a08a125e34.tar.gz
bdl-25444d18759bb7dde28dc6cb7a1554a08a125e34.zip
Add non-exhaustive typechecking for cond/match expr
-rw-r--r--src/main.c48
-rw-r--r--src/parser.c14
-rw-r--r--tests/semantics.bad13
3 files changed, 64 insertions, 11 deletions
diff --git a/src/main.c b/src/main.c
index bfaca4e..d442b98 100644
--- a/src/main.c
+++ b/src/main.c
@@ -304,6 +304,51 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) {
304 node->type = cstr("nil"); 304 node->type = cstr("nil");
305 return node->type; 305 return node->type;
306 } break; 306 } break;
307 case NODE_COND:
308 case NODE_MATCH: {
309 if (node->match_expr) {
310 type_inference(a, node->match_expr, scope);
311 }
312 Type previous = cstr("");
313 for (sz i = 0; i < array_size(node->match_cases); i++) {
314 Node *expr = node->match_cases[i];
315 Type next = type_inference(a, expr, scope);
316 if (i != 0 && !str_eq(next, previous)) {
317 emit_semantic_error(
318 a, node,
319 cstr("non-matching types for case expressions"));
320 return cstr("");
321 }
322 previous = next;
323 }
324 // TODO: proper typecheck matchcase expressions here...
325 node->type = previous;
326 return node->type;
327 } break;
328 case NODE_CASE_MATCH: {
329 if (node->case_value) {
330 type_inference(a, node->case_value, scope);
331 }
332 if (node->case_expr->kind != NODE_BLOCK) {
333 scope = typescope_alloc(a, scope);
334 }
335 node->type = type_inference(a, node->case_expr, scope);
336 return node->type;
337 } break;
338 case NODE_CASE_COND: {
339 if (node->case_value) {
340 Type cond = type_inference(a, node->case_value, scope);
341 if (!str_eq(cond, cstr("bool"))) {
342 emit_semantic_error(a, node,
343 cstr("non-boolean case condition"));
344 }
345 }
346 if (node->case_expr->kind != NODE_BLOCK) {
347 scope = typescope_alloc(a, scope);
348 }
349 node->type = type_inference(a, node->case_expr, scope);
350 return node->type;
351 } break;
307 case NODE_TRUE: 352 case NODE_TRUE:
308 case NODE_FALSE: { 353 case NODE_FALSE: {
309 node->type = cstr("bool"); 354 node->type = cstr("bool");
@@ -564,7 +609,8 @@ analyzer_symbols(Analyzer *a, Node *node, Scope *scope) {
564 symmap_insert(&map->val.fields, field_name, s, a->storage); 609 symmap_insert(&map->val.fields, field_name, s, a->storage);
565 } 610 }
566 } break; 611 } break;
567 case NODE_CASE: { 612 case NODE_CASE_MATCH:
613 case NODE_CASE_COND: {
568 analyzer_symbols(a, node->case_value, scope); 614 analyzer_symbols(a, node->case_value, scope);
569 if (node->case_expr) { 615 if (node->case_expr) {
570 if (node->case_expr->kind != NODE_BLOCK) { 616 if (node->case_expr->kind != NODE_BLOCK) {
diff --git a/src/parser.c b/src/parser.c
index 9079df9..b82579b 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -47,8 +47,9 @@ typedef enum NodeKind {
47 NODE_STRUCT, 47 NODE_STRUCT,
48 NODE_IF, 48 NODE_IF,
49 NODE_MATCH, 49 NODE_MATCH,
50 NODE_CASE,
51 NODE_COND, 50 NODE_COND,
51 NODE_CASE_MATCH,
52 NODE_CASE_COND,
52 NODE_ENUM, 53 NODE_ENUM,
53 NODE_BREAK, 54 NODE_BREAK,
54 NODE_CONTINUE, 55 NODE_CONTINUE,
@@ -111,8 +112,9 @@ Str node_str[] = {
111 [NODE_STRUCT] = cstr("STRUCT DEF"), 112 [NODE_STRUCT] = cstr("STRUCT DEF"),
112 [NODE_IF] = cstr("IF"), 113 [NODE_IF] = cstr("IF"),
113 [NODE_MATCH] = cstr("MATCH"), 114 [NODE_MATCH] = cstr("MATCH"),
114 [NODE_CASE] = cstr("CASE"),
115 [NODE_COND] = cstr("COND"), 115 [NODE_COND] = cstr("COND"),
116 [NODE_CASE_MATCH] = cstr("CASE"),
117 [NODE_CASE_COND] = cstr("CASE"),
116 [NODE_ENUM] = cstr("ENUM"), 118 [NODE_ENUM] = cstr("ENUM"),
117 [NODE_BREAK] = cstr("BREAK"), 119 [NODE_BREAK] = cstr("BREAK"),
118 [NODE_CONTINUE] = cstr("CONTINUE"), 120 [NODE_CONTINUE] = cstr("CONTINUE"),
@@ -609,7 +611,8 @@ parse_keyword(Parser *parser) {
609 parse_consume(parser, TOK_LCURLY, 611 parse_consume(parser, TOK_LCURLY,
610 cstr("expected block of match cases")); 612 cstr("expected block of match cases"));
611 while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { 613 while (!parse_match(parser, TOK_RCURLY) && !parser->panic) {
612 Node *tmp = node_alloc(parser, NODE_CASE, parser->previous); 614 Node *tmp =
615 node_alloc(parser, NODE_CASE_MATCH, parser->previous);
613 if (!tmp) return; 616 if (!tmp) return;
614 // Are we on the default case. 617 // Are we on the default case.
615 if (!parse_match(parser, TOK_ELSE)) { 618 if (!parse_match(parser, TOK_ELSE)) {
@@ -668,12 +671,11 @@ parse_keyword(Parser *parser) {
668 parse_consume(parser, TOK_LCURLY, 671 parse_consume(parser, TOK_LCURLY,
669 cstr("expected '{' on cond expression")); 672 cstr("expected '{' on cond expression"));
670 while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { 673 while (!parse_match(parser, TOK_RCURLY) && !parser->panic) {
671 Node *tmp = node_alloc(parser, NODE_CASE, parser->previous); 674 Node *tmp =
675 node_alloc(parser, NODE_CASE_COND, parser->previous);
672 if (!tmp) return; 676 if (!tmp) return;
673 // Are we on the default case. 677 // Are we on the default case.
674 if (!parse_match(parser, TOK_ELSE)) { 678 if (!parse_match(parser, TOK_ELSE)) {
675 parse_consume(parser, TOK_CASE,
676 cstr("expected case statement"));
677 parse_expr(parser, PREC_LOW); 679 parse_expr(parser, PREC_LOW);
678 tmp->case_value = array_pop(parser->nodes); 680 tmp->case_value = array_pop(parser->nodes);
679 } 681 }
diff --git a/tests/semantics.bad b/tests/semantics.bad
index a938979..5b5c971 100644
--- a/tests/semantics.bad
+++ b/tests/semantics.bad
@@ -18,15 +18,20 @@
18; 44 18; 44
19; } 19; }
20 20
21let single = if (true) { 21; let single = if (true) {
22 123 22; 123
23} 23; }
24 24
25; while (!true) { 25; while (!true) {
26; "asjdflasdjf" 26; "asjdflasdjf"
27; } 27; }
28 28
29set single = 32 29; set single = 32
30
31cond {
32 1 == 2 = "hello"
33 else = "dong"
34}
30 35
31; enum test { 36; enum test {
32; a = 1 37; a = 1