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:
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
};