diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-12-11 13:25:00 -0500 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-12-11 13:25:00 -0500 |
| commit | 7e9a976fe52bdce8e7e69335b85514bc48549f70 (patch) | |
| tree | b7e5bba47e7ae745c13302b3e962d1602e4f0ffb /types.c | |
| parent | 6c5c75961957db64e0074864ce0d3b7357f5a03a (diff) | |
Optional promotion in comparisons
Diffstat (limited to 'types.c')
| -rw-r--r-- | types.c | 21 |
1 files changed, 15 insertions, 6 deletions
@@ -325,6 +325,9 @@ PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t) PUREFUNC bool can_promote(type_t *actual, type_t *needed) { + if (!actual || !needed) + return false; + // No promotion necessary: if (type_eq(actual, needed)) return true; @@ -354,13 +357,19 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) if (actual->tag == PointerType && can_promote(Match(actual, PointerType)->pointed, needed)) return true; - // Optional num -> num - if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) - return can_promote(Match(actual, OptionalType)->type, needed); + if (actual->tag == OptionalType) { + // Ambiguous `none` to concrete optional + if (Match(actual, OptionalType)->type == NULL) + return (needed->tag == OptionalType); - // Optional promotion: - if (needed->tag == OptionalType && can_promote(actual, Match(needed, OptionalType)->type)) - return true; + // Optional num -> num + if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) + return can_promote(Match(actual, OptionalType)->type, needed); + + // Optional promotion: + if (needed->tag == OptionalType && Match(needed, OptionalType)->type != NULL && can_promote(actual, Match(needed, OptionalType)->type)) + return true; + } if (needed->tag == PointerType && actual->tag == PointerType) { auto needed_ptr = Match(needed, PointerType); |
