aboutsummaryrefslogtreecommitdiff
path: root/typecheck.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-05-24 00:03:46 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-05-24 00:03:46 -0400
commit308946e794f05da9f7010797f5911bcf4e131c3e (patch)
tree8ab109d49a2b0314faacc577e21de1c60e227788 /typecheck.c
parentbf3cdc3dfa4dcd0d17f182fe875d718cb4a4272f (diff)
Switch optional detection to use 'when .. is @..' instead of 'if .. :=
..'; also fixed a bug with stack memory in doctests
Diffstat (limited to 'typecheck.c')
-rw-r--r--typecheck.c39
1 files changed, 22 insertions, 17 deletions
diff --git a/typecheck.c b/typecheck.c
index 9964c9d9..16841e8f 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -845,22 +845,7 @@ type_t *get_type(env_t *env, ast_t *ast)
case If: {
auto if_ = Match(ast, If);
- type_t *true_t;
- if (if_->condition->tag == Declare) {
- auto decl = Match(if_->condition, Declare);
- env_t *scope = fresh_scope(env);
- type_t *var_t = get_type(env, decl->value);
- if (var_t->tag == PointerType) {
- auto ptr = Match(var_t, PointerType);
- var_t = Type(PointerType, .pointed=ptr->pointed, .is_optional=false, .is_stack=ptr->is_stack, .is_readonly=ptr->is_readonly);
- }
- CORD var = Match(decl->var, Var)->name;
- set_binding(scope, CORD_to_const_char_star(var), new(binding_t, .type=var_t));
- true_t = get_type(scope, if_->body);
- } else {
- true_t = get_type(env, if_->body);
- }
-
+ type_t *true_t = get_type(env, if_->body);
if (if_->else_body) {
type_t *false_t = get_type(env, if_->else_body);
type_t *t_either = type_or_type(true_t, false_t);
@@ -877,8 +862,28 @@ type_t *get_type(env_t *env, ast_t *ast)
case When: {
auto when = Match(ast, When);
type_t *subject_t = get_type(env, when->subject);
+ if (subject_t->tag == PointerType) {
+ if (!Match(subject_t, PointerType)->is_optional)
+ code_err(when->subject, "This %T pointer type is not optional, so this 'when' statement is tautological", subject_t);
+
+ bool handled_at = false;
+ for (when_clause_t *clause = when->clauses; clause; clause = clause->next) {
+ const char *tag_name = Match(clause->tag_name, Var)->name;
+ if (!streq(tag_name, "@"))
+ code_err(clause->tag_name, "'when' clauses on optional pointers only support @var, not tags like '%s'", tag_name);
+ if (handled_at)
+ code_err(clause->tag_name, "This 'when' statement has already handled the case of non-null pointers!");
+ handled_at = true;
+ }
+ if (!handled_at)
+ code_err(ast, "This 'when' statement doesn't handle non-null pointers");
+ if (!when->else_body)
+ code_err(ast, "This 'when' statement doesn't handle null pointers");
+ return Type(VoidType);
+ }
+
if (subject_t->tag != EnumType)
- code_err(when->subject, "'when' statements are only for enum types, not %T", subject_t);
+ code_err(when->subject, "'when' statements are only for enum types and optional pointers, not %T", subject_t);
tag_t * const tags = Match(subject_t, EnumType)->tags;