harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit e249adce2743ff9232689c2eaba9012fd37b5a5f
parent fbbfbf01d15fa9423774975489d671995896313f
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 24 Jan 2021 14:57:17 -0500

check: set match/switch result to tag of results

Diffstat:
Minclude/type_store.h | 3+++
Msrc/check.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/type_store.c | 38+++++++++++++++++++++++++++++---------
Mtests/14-switch.ha | 17+++++++++++++++++
Mtests/18-match.ha | 18++++++++++++++++++
5 files changed, 123 insertions(+), 11 deletions(-)

diff --git a/include/type_store.h b/include/type_store.h @@ -44,4 +44,7 @@ const struct type *type_store_lookup_slice(struct type_store *store, const struct type *type_store_lookup_alias(struct type_store *store, const struct identifier *ident, const struct type *secondary); +const struct type *type_store_lookup_tagged(struct type_store *store, + struct type_tagged_union *tags); + #endif diff --git a/src/check.c b/src/check.c @@ -907,6 +907,10 @@ check_expr_match(struct context *ctx, type->storage == TYPE_STORAGE_TAGGED_UNION || is_ptr, "match value must be tagged union or nullable pointer type"); + struct type_tagged_union result_type = {0}; + struct type_tagged_union *tagged = &result_type, + **next_tag = &tagged->next; + struct match_case **next = &expr->match.cases, *_case = NULL; for (struct ast_match_case *acase = aexpr->match.cases; acase; acase = acase->next) { @@ -979,8 +983,12 @@ check_expr_match(struct context *ctx, if (expr->result == NULL) { expr->result = _case->value->result; + tagged->type = expr->result; } else if (expr->result != _case->value->result) { - assert(0); // TODO: Form tagged union + tagged = *next_tag = + xcalloc(1, sizeof(struct type_tagged_union)); + next_tag = &tagged->next; + tagged->type = _case->value->result; } } @@ -989,6 +997,25 @@ check_expr_match(struct context *ctx, expr->terminates = true; } + if (result_type.next) { + expr->result = type_store_lookup_tagged( + ctx->store, &result_type); + + struct match_case *_case = expr->match.cases; + while (_case) { + _case->value = lower_implicit_cast( + expr->result, _case->value); + _case = _case->next; + } + + struct type_tagged_union *tu = result_type.next; + while (tu) { + struct type_tagged_union *next = tu->next; + free(tu); + tu = next; + } + } + trleave(TR_CHECK, NULL); } @@ -1188,6 +1215,10 @@ check_expr_switch(struct context *ctx, const struct type *type = value->result; expr->_switch.value = value; + struct type_tagged_union result_type = {0}; + struct type_tagged_union *tagged = &result_type, + **next_tag = &tagged->next; + // TODO: Test for dupes, exhaustiveness struct switch_case **next = &expr->_switch.cases, *_case = NULL; for (struct ast_switch_case *acase = aexpr->_switch.cases; @@ -1227,8 +1258,12 @@ check_expr_switch(struct context *ctx, if (expr->result == NULL) { expr->result = _case->value->result; + tagged->type = expr->result; } else if (expr->result != _case->value->result) { - assert(0); // TODO: Form tagged union + tagged = *next_tag = + xcalloc(1, sizeof(struct type_tagged_union)); + next_tag = &tagged->next; + tagged->type = _case->value->result; } } @@ -1236,6 +1271,25 @@ check_expr_switch(struct context *ctx, expr->result = &builtin_type_void; expr->terminates = true; } + + if (result_type.next) { + expr->result = type_store_lookup_tagged( + ctx->store, &result_type); + + struct switch_case *_case = expr->_switch.cases; + while (_case) { + _case->value = lower_implicit_cast( + expr->result, _case->value); + _case = _case->next; + } + + struct type_tagged_union *tu = result_type.next; + while (tu) { + struct type_tagged_union *next = tu->next; + free(tu); + tu = next; + } + } } static void diff --git a/src/type_store.c b/src/type_store.c @@ -523,17 +523,10 @@ tagged_cmp(const void *ptr_a, const void *ptr_b) } static void -tagged_init_from_atype(struct type_store *store, - struct type *type, const struct ast_type *atype) +tagged_init(struct type *type, struct type_tagged_union **tu, size_t nmemb) { - size_t nmemb = sum_atagged_memb(store, &atype->tagged_union); - struct type_tagged_union **tu = - xcalloc(nmemb, sizeof(struct type_tagged_union *)); - size_t i = 0; - collect_atagged_memb(store, tu, &atype->tagged_union, &i); - // Prune duplicates - for (i = 1; i < nmemb; ++i) + for (size_t i = 1; i < nmemb; ++i) for (size_t j = 0; j < i; ++j) { if (tu[j]->type->id == tu[i]->type->id) { assert(0); // TODO: prune @@ -574,6 +567,17 @@ tagged_init_from_atype(struct type_store *store, } } +static void +tagged_init_from_atype(struct type_store *store, + struct type *type, const struct ast_type *atype) +{ + size_t nmemb = sum_atagged_memb(store, &atype->tagged_union); + struct type_tagged_union **tu = + xcalloc(nmemb, sizeof(struct type_tagged_union *)); + size_t i = 0; + collect_atagged_memb(store, tu, &atype->tagged_union, &i); + tagged_init(type, tu, nmemb); +} static const struct type *type_store_lookup_type(struct type_store *store, const struct type *type); @@ -862,3 +866,19 @@ type_store_lookup_alias(struct type_store *store, } return type; } + +const struct type * +type_store_lookup_tagged(struct type_store *store, + struct type_tagged_union *tags) +{ + struct type type = { + .storage = TYPE_STORAGE_TAGGED_UNION, + }; + size_t nmemb = sum_tagged_memb(store, tags); + struct type_tagged_union **tu = + xcalloc(nmemb, sizeof(struct type_tagged_union *)); + size_t i = 0; + collect_tagged_memb(store, tu, tags, &i); + tagged_init(&type, tu, nmemb); + return type_store_lookup_type(store, &type); +} diff --git a/tests/14-switch.ha b/tests/14-switch.ha @@ -24,8 +24,25 @@ fn termination() void = { assert(y == 1337); }; +fn tagged_result() void = { + let x = 42; + let y: (int | uint) = switch (x) { + 42 => 1337, + * => 1337u, + }; + assert(y is int); + + x = 24; + y = switch (x) { + 42 => 1337, + * => 1337u, + }; + assert(y is uint); +}; + export fn main() void = { basics(); termination(); + tagged_result(); // TODO: Test exhaustiveness and dupe detection }; diff --git a/tests/18-match.ha b/tests/18-match.ha @@ -63,10 +63,28 @@ fn alias() void = { }; }; +fn tagged_result() void = { + let x: (int | uint) = 42; + let y: (int | uint) = match (x) { + x: int => x, + x: uint => x, + }; + assert(y is int); + + x = 42u; + y = match (x) { + x: int => x, + x: uint => x, + }; + assert(y is uint); +}; + export fn main() void = { tagged(); termination(); default(); pointer(); alias(); + tagged_result(); + // TODO: Test exhaustiveness and dupe detection };