commit b6cb76a4dcaba027ca45436106978e35dd6f568a
parent d70b91ef8f36c60eb179d932cf520dd11d5b9a87
Author: Ember Sawady <ecs@d2evs.net>
Date: Mon, 23 Jan 2023 16:18:48 +0000
Fix tagged unions containing t and !t
Or more broadly, any types which are identical besides type flags
Signed-off-by: Ember Sawady <ecs@d2evs.net>
Diffstat:
6 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/include/types.h b/include/types.h
@@ -175,7 +175,7 @@ const struct type_tuple *type_get_value(
const struct type *type, uintmax_t index);
const struct type *tagged_select_subtype(
- const struct type *tagged, const struct type *subtype);
+ const struct type *tagged, const struct type *subtype, bool strip);
bool tagged_subset_compat(const struct type *to, const struct type *from);
const char *type_storage_unparse(enum type_storage storage);
diff --git a/src/check.c b/src/check.c
@@ -127,7 +127,7 @@ lower_implicit_cast(const struct type *to, struct expression *expr)
if (type_dealias(to)->storage == STORAGE_TAGGED) {
const struct type *interim =
- tagged_select_subtype(to, expr->result);
+ tagged_select_subtype(to, expr->result, true);
if (interim) {
expr = lower_implicit_cast(interim, expr);
}
@@ -1467,7 +1467,7 @@ check_expr_cast(struct context *ctx,
// secondary type must be a strict subset or a
// member of the primary type
if (!((tagged_subset_compat(primary, secondary)
- || tagged_select_subtype(primary, secondary))
+ || tagged_select_subtype(primary, secondary, true))
&& !tagged_subset_compat(secondary, primary))) {
error(ctx, aexpr->cast.type->loc, expr,
"Type is not a valid member of "
diff --git a/src/eval.c b/src/eval.c
@@ -624,7 +624,7 @@ eval_cast(struct context *ctx, struct expression *in, struct expression *out)
}
return EVAL_OK;
case STORAGE_TAGGED:
- subtype = tagged_select_subtype(to, val.result);
+ subtype = tagged_select_subtype(to, val.result, true);
out->constant.tagged.value =
xcalloc(1, sizeof(struct expression));
if (subtype) {
diff --git a/src/gen.c b/src/gen.c
@@ -1285,7 +1285,7 @@ gen_type_assertion_or_test(struct gen_context *ctx, const struct expression *exp
bfailed = bpassed;
}
struct gen_value result = {0};
- if (tagged_select_subtype(expr->cast.value->result, want)) {
+ if (tagged_select_subtype(expr->cast.value->result, want, true)) {
result = gen_nested_match_tests(ctx, base, bpassed,
bfailed, tag, want);
} else if (tagged_subset_compat(expr->cast.value->result, want)) {
@@ -1351,7 +1351,7 @@ gen_expr_cast_tagged_at(struct gen_context *ctx,
{
assert(expr->type == EXPR_CAST);
const struct type *to = expr->result, *from = expr->cast.value->result;
- const struct type *subtype = tagged_select_subtype(to, from);
+ const struct type *subtype = tagged_select_subtype(to, from, true);
if (!subtype && tagged_align_compat(from, to)) {
// Case 1: from is a union whose members are a subset of to, and
@@ -1425,7 +1425,7 @@ cast_prefers_at(const struct expression *expr)
}
// tagged => *; subtype compatible
if (type_dealias(from)->storage == STORAGE_TAGGED
- && tagged_select_subtype(from, to)) {
+ && tagged_select_subtype(from, to, true)) {
return false;
}
// * => tagged
@@ -1540,7 +1540,7 @@ gen_expr_cast(struct gen_context *ctx, const struct expression *expr)
if (expr->cast.kind != C_CAST) {
bool is_valid_tagged, is_valid_pointer;
is_valid_tagged = type_dealias(from)->storage == STORAGE_TAGGED
- && (tagged_select_subtype(from, to)
+ && (tagged_select_subtype(from, to, true)
|| tagged_subset_compat(from, to));
is_valid_pointer = type_dealias(from)->storage == STORAGE_POINTER
&& (type_dealias(to)->storage == STORAGE_POINTER
@@ -2404,7 +2404,7 @@ nested_tagged_offset(const struct type *tu, const struct type *target)
const struct type *test = tu;
struct qbe_value offset = constl(tu->align);
do {
- test = tagged_select_subtype(tu, target);
+ test = tagged_select_subtype(tu, target, false);
if (!test) {
break;
}
@@ -2451,7 +2451,7 @@ gen_nested_match_tests(struct gen_context *ctx, struct gen_value object,
if (type_dealias(subtype)->storage != STORAGE_TAGGED) {
break;
}
- test = tagged_select_subtype(subtype, type);
+ test = tagged_select_subtype(subtype, type, false);
if (!test) {
break;
}
@@ -2540,7 +2540,7 @@ gen_match_with_tagged(struct gen_context *ctx,
struct qbe_value bmatch = mklabel(ctx, &lmatch, "matches.%d");
struct qbe_value bnext = mklabel(ctx, &lnext, "next.%d");
const struct type *subtype =
- tagged_select_subtype(objtype, _case->type);
+ tagged_select_subtype(objtype, _case->type, false);
enum match_compat compat = COMPAT_SUBTYPE;
if (subtype) {
gen_nested_match_tests(ctx, object,
diff --git a/src/types.c b/src/types.c
@@ -462,7 +462,8 @@ strip_flags(const struct type *t, struct type *secondary)
}
const struct type *
-tagged_select_subtype(const struct type *tagged, const struct type *subtype)
+tagged_select_subtype(const struct type *tagged, const struct type *subtype,
+ bool strip)
{
tagged = type_dealias(tagged);
assert(tagged->storage == STORAGE_TAGGED);
@@ -474,10 +475,7 @@ tagged_select_subtype(const struct type *tagged, const struct type *subtype)
const struct type *selected = NULL;
for (const struct type_tagged_union *tu = &tagged->tagged;
tu; tu = tu->next) {
- struct type _tustripped;
- const struct type *tustripped =
- strip_flags(tu->type, &_tustripped);
- if (tustripped->id == stripped->id) {
+ if (tu->type->id == subtype->id) {
return tu->type;
}
@@ -490,6 +488,18 @@ tagged_select_subtype(const struct type *tagged, const struct type *subtype)
}
}
+ if (strip) {
+ for (const struct type_tagged_union *tu = &tagged->tagged;
+ tu; tu = tu->next) {
+ struct type _tustripped;
+ const struct type *tustripped =
+ strip_flags(tu->type, &_tustripped);
+ if (tustripped->id == stripped->id) {
+ return tu->type;
+ }
+ }
+ }
+
if (nassign == 1) {
return selected;
}
@@ -858,7 +868,7 @@ type_is_assignable(const struct type *to, const struct type *from)
&& to->array.members == from->array.members;
}
case STORAGE_TAGGED:
- return tagged_select_subtype(to, from_orig) != NULL
+ return tagged_select_subtype(to, from_orig, true) != NULL
|| tagged_subset_compat(to, from);
// The following types are only assignable from themselves, and are
// handled above:
@@ -891,13 +901,13 @@ is_castable_with_tagged(const struct type *to, const struct type *from)
}
}
if (type_dealias(to)->storage == STORAGE_TAGGED) {
- const struct type *subtype = tagged_select_subtype(to, from);
+ const struct type *subtype = tagged_select_subtype(to, from, true);
if (subtype != NULL) {
return subtype;
}
}
if (type_dealias(from)->storage == STORAGE_TAGGED) {
- const struct type *subtype = tagged_select_subtype(from, to);
+ const struct type *subtype = tagged_select_subtype(from, to, true);
if (subtype != NULL) {
return subtype;
}
diff --git a/tests/26-regression.ha b/tests/26-regression.ha
@@ -69,4 +69,9 @@ export fn main() void = {
case &global => void;
case => abort();
};
+
+ match (void: (void | !void)) {
+ case void => void;
+ case !void => abort();
+ };
};