/* * Checks type compatibility. * * Copyright © 2025 Samuel Lidén Borell * * SPDX-License-Identifier: EUPL-1.2+ OR LGPL-2.1-or-later */ #include #include "compiler.h" static void integral_type_compat( const struct TypeRefNumeric *na, const struct TypeRefNumeric *nb, enum TypeCompatMode mode); static int range_cmp( const struct TypeRefNumeric *na, const struct TypeRefNumeric *nb); void check_type_compat( const struct TypeRef *tr_a, const struct TypeRef *tr_b, enum TypeCompatMode mode) { enum TypeRefKind kind_a, kind_b; assert(tr_a != NULL); assert(tr_b != NULL); if (mode != TC_ASSIGN) { if (tr_a->kind > tr_b->kind) { /* Normalize order, putting TR_CLASS last */ const struct TypeRef *temp = tr_a; tr_a = tr_b; tr_b = temp; } } kind_a = tr_a->kind; kind_b = tr_b->kind; if (kind_a != kind_b) { /* TODO allow e.g. comparison with `none`, as long as the type is an optional one. */ if (kind_a == TR_UNKNOWN || kind_b == TR_UNKNOWN) { /* TODO TR_UNKNOWN means unimplemented. E_IDENT...E_CALL are unimplemented */ return; } error("Incompatible types"); } assert(kind_a == kind_b); switch (kind_a) { case TR_UNKNOWN: unreachable(); case TR_BOOL: case TR_INT: integral_type_compat(tr_a->u.num, tr_b->u.num, mode); break; case TR_CLASS: /* TODO require identical types */ /* TODO compare type parameters */ /* TODO check for optional types */ break; } } static void integral_type_compat( const struct TypeRefNumeric *na, const struct TypeRefNumeric *nb, enum TypeCompatMode mode) { if (na == NULL && mode == TC_ASSIGN) { /* Result type is a conditional statement (always fully unconstrained) */ return; } assert(na != NULL); assert(nb != NULL); switch (mode) { case TC_ASSIGN: /* Check lower bound */ if (!na->min_neg && nb->min_neg) { /* eg 1.. = -1.. */ error(nb->max_neg ? /* eg 1.. = -2..-1 */ "Expression is negative (use `signed`)" : "Expression might be negative (use `signed`)"); } else if (na->min_neg && nb->min_neg && na->min < nb->min) { /* eg -1.. = -2.. */ error(na->min < nb->max ? /* eg -1.. = -2..-3 */ "Expression is below minimum bound" : "Expression might go below minimum bound"); } else if (!na->min_neg && !nb->min_neg && na->min > nb->min) { /* eg 2.. = 1.. */ error(na->min > nb->max ? /* eg 3.. = 1..2 */ "Expression is below minimum bound" : "Expression might go below minimum bound"); } /* Check upper bound */ if (na->max_neg && !nb->max_neg) { /* eg ..-1 = ..1 */ error(!nb->min_neg ? /* eg ..-1 = 1..2 */ "Expression is non-negative" : "Expression might be non-negative"); } else if (na->max_neg && nb->max_neg && na->max > nb->max) { /* eg ..-2 = ..-1 */ error(na->max > nb->min ? /* eg ..-3 = -2..-1 */ "Expression is above maximum bound" : "Expression might go above maximum bound"); } else if (!na->max_neg && !nb->max_neg && na->max < nb->max) { /* eg ..1 = ..2 */ error(na->max < nb->min ? /* eg ..1 = 2..3 */ "Expression is above maximum bound" : "Expression might go above maximum bound"); } break; case TC_COMPARE: { int cmp; if (is_const(na) && is_const(nb)) { /* Comparison between constants. Assume it's intentional */ break; } cmp = range_cmp(na, nb); if (cmp < 0) { warning(na->max_neg && !nb->min_neg ? "Left is always < 0 but right is >= 0" : "Right operand is always greater than left operand"); } else if (cmp > 0) { warning(!na->min_neg && nb->max_neg ? "Left is always >= 0 but right is < 0" : "Right operand is always less than left operand"); } break; } default: unreachable(); } } /** Returns -1 if na is always < nb, 1 if na is always > nb or 0 if there's an overlap between the ranges of na and nb. */ static int range_cmp( const struct TypeRefNumeric *na, const struct TypeRefNumeric *nb) { if ( /* eg ..-1 cmp 1.. -or- eg ..-2 cmp -1.. */ (na->max_neg && (!nb->min_neg || na->max > nb->min)) || /* eg ..1 cmp 2.. */ (!na->max_neg && !nb->min_neg && na->max < nb->min)) { return -1; } else if ( /* eg 1.. cmp ..-1 -or- eg 2.. cmp ..1 */ (!na->min_neg && (nb->max_neg || na->min > nb->max)) || /* eg -1.. cmp ..-2 */ (na->min_neg && nb->min_neg && na->min < nb->max)) { return 1; } else { /* Overlap */ return 0; } }