mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
TypeInfo gained a span. Hex lexing fixed. Basic block returns fixed. Removal of Ast.exit. Enum parameter list parses. Enum analysis improved. Exit deduction improved. Switch over a bool allowed. Switch analysis improved. Added -% operator. Updated implicit casts. Use of bigint. Signed-unsigned comparisons.
This commit is contained in:
@@ -256,6 +256,7 @@ UnaryOp unary_op[TOKEN_LAST + 1] = {
|
||||
[TOKEN_BIT_NOT] = UNARYOP_BITNEG,
|
||||
[TOKEN_NOT] = UNARYOP_NOT,
|
||||
[TOKEN_MINUS] = UNARYOP_NEG,
|
||||
[TOKEN_MINUS_MOD] = UNARYOP_NEGMOD,
|
||||
[TOKEN_PLUSPLUS] = UNARYOP_INC,
|
||||
[TOKEN_MINUSMINUS] = UNARYOP_DEC,
|
||||
};
|
||||
@@ -411,9 +412,6 @@ void fprint_type_recursive(FILE *file, Type *type, int indent)
|
||||
case TYPE_IXX:
|
||||
fprintf_indented(file, indent, "(comp time int)\n");
|
||||
break;
|
||||
case TYPE_UXX:
|
||||
fprintf_indented(file, indent, "(comp time uint)\n");
|
||||
break;
|
||||
case TYPE_FXX:
|
||||
fprintf_indented(file, indent, "(comp time float)\n");
|
||||
break;
|
||||
@@ -530,31 +528,8 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
|
||||
break;
|
||||
case EXPR_CONST:
|
||||
fprintf_indented(file, indent, "(const ");
|
||||
switch (expr->const_expr.type)
|
||||
{
|
||||
case CONST_NIL:
|
||||
fprintf(file, "nil\n");
|
||||
break;
|
||||
case CONST_BOOL:
|
||||
fprintf(file, expr->const_expr.b ? "true\n" : "false\n");
|
||||
break;
|
||||
case CONST_INT:
|
||||
if (expr->type->type_kind >= TYPE_U8 && expr->type->type_kind <= TYPE_UXX)
|
||||
{
|
||||
fprintf(file, "%llu\n", (unsigned long long)expr->const_expr.i);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "%lld\n", (long long)expr->const_expr.i);
|
||||
}
|
||||
break;
|
||||
case CONST_FLOAT:
|
||||
fprintf(file, "%Lf\n", expr->const_expr.f);
|
||||
break;
|
||||
case CONST_STRING:
|
||||
fprintf(file, "%s\n", expr->const_expr.string.chars);
|
||||
break;
|
||||
}
|
||||
expr_const_fprint(file, &expr->const_expr);
|
||||
fprintf(file, "\n");
|
||||
fprint_expr_common(file, expr, indent + 1);
|
||||
break;
|
||||
case EXPR_BINARY:
|
||||
|
||||
@@ -129,7 +129,7 @@ void bigint_init_bigint(BigInt *dest, const BigInt *src)
|
||||
{
|
||||
return bigint_init_unsigned(dest, 0);
|
||||
}
|
||||
else if (src->digit_count == 1)
|
||||
if (src->digit_count == 1)
|
||||
{
|
||||
dest->digit_count = 1;
|
||||
dest->digit = src->digit;
|
||||
@@ -1878,6 +1878,65 @@ void bigint_print(BigInt *bigint, uint64_t base)
|
||||
}
|
||||
}
|
||||
|
||||
const char *bigint_to_error_string(const BigInt *bigint, uint64_t base)
|
||||
{
|
||||
if (bigint->digit_count == 0)
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
if (bigint->digit_count == 1 && base == 10)
|
||||
{
|
||||
char *res = NULL;
|
||||
asprintf(&res, "%" PRIu64, bigint->digit);
|
||||
return res;
|
||||
}
|
||||
size_t len = bigint->digit_count * 64;
|
||||
char *start = malloc_arena(len);
|
||||
char *buf = start;
|
||||
|
||||
BigInt digit_bi = { 0 };
|
||||
BigInt a1 = { 0 };
|
||||
BigInt a2 = { 0 };
|
||||
|
||||
BigInt *a = &a1;
|
||||
BigInt *other_a = &a2;
|
||||
bigint_init_bigint(a, bigint);
|
||||
|
||||
BigInt base_bi = { 0 };
|
||||
bigint_init_unsigned(&base_bi, 10);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
bigint_rem(&digit_bi, a, &base_bi);
|
||||
uint8_t digit = (uint8_t)bigint_as_unsigned(&digit_bi);
|
||||
*(buf++) = digit_to_char(digit, false);
|
||||
bigint_div_trunc(other_a, a, &base_bi);
|
||||
{
|
||||
BigInt *tmp = a;
|
||||
a = other_a;
|
||||
other_a = tmp;
|
||||
}
|
||||
if (bigint_cmp_zero(a) == CMP_EQ)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// reverse
|
||||
char *out = malloc_arena(buf - start + 2);
|
||||
char *current = out;
|
||||
if (bigint->is_negative)
|
||||
{
|
||||
*(current++) = '-';
|
||||
}
|
||||
for (char *ptr = buf - 1; ptr >= start; ptr--)
|
||||
{
|
||||
*(current++) = *ptr;
|
||||
}
|
||||
*(current++) = '\0';
|
||||
return current;
|
||||
}
|
||||
|
||||
void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base)
|
||||
{
|
||||
if (bigint->digit_count == 0)
|
||||
|
||||
@@ -6,16 +6,6 @@
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
typedef struct _BigInt
|
||||
{
|
||||
unsigned digit_count;
|
||||
bool is_negative;
|
||||
union {
|
||||
uint64_t digit;
|
||||
uint64_t *digits;
|
||||
};
|
||||
} BigInt;
|
||||
|
||||
typedef enum _CmpRes
|
||||
{
|
||||
CMP_LT,
|
||||
@@ -54,6 +44,7 @@ bool bigint_eql(BigInt a, BigInt b);
|
||||
CmpRes bigint_cmp(const BigInt *op1, const BigInt *op2);
|
||||
CmpRes bigint_cmp_zero(const BigInt *op);
|
||||
uint32_t bigint_hash(BigInt x);
|
||||
const char *bigint_to_error_string(const BigInt *bigint, uint64_t base);
|
||||
void bigint_print(BigInt *bigint, uint64_t base);
|
||||
void bigint_fprint(FILE *file, BigInt *bigint, uint64_t base);
|
||||
uint64_t bigint_as_unsigned(const BigInt *bigint);
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include "bigint.h"
|
||||
|
||||
#define EXIT_T_MISMATCH() return sema_type_mismatch(left, canonical, cast_type)
|
||||
|
||||
#define IS_EXPLICT()
|
||||
#define RETURN_NON_CONST_CAST(kind) do { if (left->expr_kind == EXPR_CONST) { insert_cast(left, kind, canonical); return true; } } while (0)
|
||||
|
||||
static inline void insert_cast(Expr *expr, CastKind kind, Type *canonical)
|
||||
{
|
||||
@@ -48,33 +50,28 @@ bool erro(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
EXIT_T_MISMATCH();
|
||||
}
|
||||
|
||||
bool ptxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
bool ptxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_NIL);
|
||||
left->const_expr.type = sign_from_type(canonical);
|
||||
left->const_expr.i = 0;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_PTRXI, canonical);
|
||||
RETURN_NON_CONST_CAST(CAST_PTRXI);
|
||||
|
||||
assert(left->const_expr.kind == TYPE_POINTER);
|
||||
expr_const_set_int(&left->const_expr, 0, type->type_kind);
|
||||
left->type = type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ptbo(Expr* left, Type *ignored, Type *canonical, Type *type, CastType cast_type)
|
||||
bool ptbo(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_NIL);
|
||||
left->const_expr.type = CONST_BOOL;
|
||||
left->const_expr.b = false;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_PTRBOOL, canonical);
|
||||
RETURN_NON_CONST_CAST(CAST_PTRBOOL);
|
||||
|
||||
assert(left->const_expr.kind == TYPE_POINTER);
|
||||
left->const_expr.b = false;
|
||||
|
||||
left->type = type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,18 +133,15 @@ bool ptpt(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastTyp
|
||||
{
|
||||
return sema_type_mismatch(left, type, cast_type);
|
||||
}
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_NIL);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_PTRPTR, canonical);
|
||||
RETURN_NON_CONST_CAST(CAST_PTRPTR);
|
||||
assert(left->const_expr.kind == TYPE_POINTER);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strpt(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
|
||||
// TODO
|
||||
insert_cast(left, CAST_PTRPTR, canonical);
|
||||
return true;
|
||||
@@ -163,284 +157,272 @@ bool stpt(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool boxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
void const_int_to_fp_cast(Expr *left, Type *canonical, Type *type)
|
||||
{
|
||||
if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
long double f = bigint_as_float(&left->const_expr.i);
|
||||
switch (canonical->type_kind)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_BOOL);
|
||||
left->const_expr.type = CONST_INT;
|
||||
left->const_expr.i = left->const_expr.b ? 1 : 0;
|
||||
left->type = type;
|
||||
return true;
|
||||
case TYPE_F32:
|
||||
left->const_expr.f = (float)f;
|
||||
break;
|
||||
case TYPE_F64:
|
||||
left->const_expr.f = (double)f;
|
||||
break;
|
||||
default:
|
||||
left->const_expr.f = f;
|
||||
break;
|
||||
}
|
||||
insert_cast(left, CAST_BOOLINT, canonical);
|
||||
left->type = type;
|
||||
left->const_expr.kind = canonical->type_kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bool into a signed or unsigned int. Explicit conversions only.
|
||||
* @return true unless this is not an explicit conversion.
|
||||
*/
|
||||
bool boxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
RETURN_NON_CONST_CAST(CAST_BOOLINT);
|
||||
assert(left->const_expr.kind == TYPE_BOOL);
|
||||
expr_const_set_int(&left->const_expr, left->const_expr.b ? 1 : 0, canonical->type_kind);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bofp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
/**
|
||||
* Bool into a float. Explicit conversions only.
|
||||
* @return true unless this is not an explicit conversion.
|
||||
*/
|
||||
bool bofp(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_NIL);
|
||||
left->const_expr.type = CONST_FLOAT;
|
||||
left->const_expr.f = left->const_expr.b ? 1.0 : 0.0;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_BOOLFP, canonical);
|
||||
RETURN_NON_CONST_CAST(CAST_BOOLFP);
|
||||
|
||||
assert(left->const_expr.kind == TYPE_BOOL);
|
||||
expr_const_set_float(&left->const_expr, left->const_expr.b ? 1.0 : 0.0, canonical->type_kind);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xibo(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
/**
|
||||
* Convert from any into to bool.
|
||||
* @return true for any implicit conversion except assign and assign add.
|
||||
*/
|
||||
bool xibo(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_INT);
|
||||
left->const_expr.type = CONST_BOOL;
|
||||
left->const_expr.b = left->const_expr.i != 0;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_INTBOOL, canonical);
|
||||
RETURN_NON_CONST_CAST(CAST_INTBOOL);
|
||||
|
||||
expr_const_set_bool(&left->const_expr, bigint_cmp_zero(&left->const_expr.i) != CMP_EQ);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fpbo(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
/**
|
||||
* Convert from any float to bool
|
||||
* @return true for any implicit conversion except assign and assign add
|
||||
*/
|
||||
bool fpbo(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type >= CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_FLOAT);
|
||||
left->const_expr.type = CONST_BOOL;
|
||||
left->const_expr.b = left->const_expr.f != 0;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_INTBOOL, canonical);
|
||||
RETURN_NON_CONST_CAST(CAST_FPBOOL);
|
||||
|
||||
expr_const_set_bool(&left->const_expr, left->const_expr.f != 0.0);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert from any fp to fp
|
||||
* @return true for all except implicit assign (but allowing assign add)
|
||||
*/
|
||||
bool fpfp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
bool is_narrowing = from->builtin.bytesize < canonical->builtin.bytesize && from->type_kind != TYPE_FXX;
|
||||
|
||||
// Is this correct? TODO
|
||||
if (is_narrowing && cast_type == CAST_TYPE_IMPLICIT_ASSIGN) EXIT_T_MISMATCH();
|
||||
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_FLOAT);
|
||||
if (type->type_kind == TYPE_F32)
|
||||
{
|
||||
left->const_expr.f = (float)left->const_expr.f;
|
||||
}
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_FPFP, canonical);
|
||||
RETURN_NON_CONST_CAST(CAST_FPFP);
|
||||
|
||||
expr_const_set_float(&left->const_expr, left->const_expr.f, canonical->type_kind);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fpui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
/**
|
||||
* Convert from any floating point to int
|
||||
* @return true only on explicit conversions.
|
||||
*/
|
||||
bool fpxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
RETURN_NON_CONST_CAST(CAST_FPUI);
|
||||
|
||||
assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_U64);
|
||||
long double d = left->const_expr.f;
|
||||
BigInt temp;
|
||||
if (canonical->type_kind >= TYPE_U8)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_FLOAT);
|
||||
assert(canonical->type_kind >= TYPE_U8 && canonical->type_kind <= TYPE_U64);
|
||||
left->const_expr.i = (uint64_t)left->const_expr.f;
|
||||
left->type = type;
|
||||
return true;
|
||||
bigint_init_unsigned(&temp, (uint64_t)d);
|
||||
bigint_truncate(&left->const_expr.i, &temp, canonical->builtin.bitsize, false);
|
||||
}
|
||||
insert_cast(left, CAST_FPUI, canonical);
|
||||
else
|
||||
{
|
||||
bigint_init_signed(&temp, (int64_t)d);
|
||||
bigint_truncate(&left->const_expr.i, &temp, canonical->builtin.bitsize, true);
|
||||
}
|
||||
left->const_expr.kind = canonical->type_kind;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fpsi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
|
||||
/**
|
||||
* Convert from compile time int to any signed or unsigned int
|
||||
* @return true unless the conversion was lossy.
|
||||
*/
|
||||
bool ixxxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
bool is_signed = canonical->type_kind < TYPE_U8;
|
||||
int bitsize = canonical->builtin.bitsize;
|
||||
if (cast_is_implicit(cast_type) && !bigint_fits_in_bits(&left->const_expr.i, bitsize, is_signed))
|
||||
{
|
||||
assert(left->const_expr.type == CONST_FLOAT);
|
||||
assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_I64);
|
||||
left->const_expr.i = (uint64_t)((int64_t)left->const_expr.f);
|
||||
left->type = type;
|
||||
return true;
|
||||
SEMA_ERROR(left, "'%s' does not fit into '%s'", expr_const_to_error_string(&left->const_expr), canonical->name);
|
||||
return false;
|
||||
}
|
||||
insert_cast(left, CAST_FPUI, canonical);
|
||||
BigInt temp;
|
||||
bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, is_signed);
|
||||
left->const_expr.i = temp;
|
||||
left->const_expr.kind = canonical->type_kind;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t type_mask[4] = { 0xFF, 0xFFFF, 0xFFFFFFFFU, 0xFFFFFFFFFFFFFFFFLLU};
|
||||
static int64_t int_type_max[4] = { INT8_MAX, INT16_MAX, INT32_MAX, INT64_MAX };
|
||||
static uint64_t uint_type_max[4] = { UINT8_MAX, UINT16_MAX, UINT32_MAX, UINT64_MAX };
|
||||
static int64_t int_type_min[4] = { INT8_MIN, INT16_MIN, INT32_MIN, INT64_MIN };
|
||||
|
||||
/**
|
||||
* Cast signed int -> signed int
|
||||
* @return true if this is a widening, an explicit cast or if it is an implicit assign add
|
||||
*/
|
||||
bool sisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize && from->type_kind != TYPE_IXX;
|
||||
bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize;
|
||||
|
||||
if (is_narrowing && (cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT)) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
int64_t i = (int64_t)left->const_expr.i;
|
||||
int index = canonical->type_kind - TYPE_I8;
|
||||
if (from->type_kind == TYPE_IXX)
|
||||
{
|
||||
if ((i > int_type_max[index]) || (i < int_type_min[index]))
|
||||
{
|
||||
SEMA_ERROR(left, "'%lld' does not fit into '%s'", i, canonical->name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
assert(left->const_expr.type == CONST_INT);
|
||||
assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_I64);
|
||||
uint64_t mask = type_mask[index];
|
||||
if (i < 0)
|
||||
{
|
||||
left->const_expr.i = ~((~((uint64_t)i)) & mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
left->const_expr.i &= mask;
|
||||
}
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
assert(from->type_kind != TYPE_IXX);
|
||||
insert_cast(left, CAST_SISI, canonical);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool siui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
bool is_narrowing = from->builtin.bytesize < canonical->builtin.bytesize && from->type_kind != TYPE_IXX;
|
||||
if (is_narrowing && (cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT)) EXIT_T_MISMATCH();
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_INT);
|
||||
assert(canonical->type_kind >= TYPE_U8 && canonical->type_kind <= TYPE_U64);
|
||||
left->const_expr.i &= type_mask[canonical->type_kind - TYPE_U8];
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_SIUI, canonical);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xiptr(Expr* left, Type *from, Type *canonical, Type *type)
|
||||
{
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_INT);
|
||||
assert(canonical->type_kind >= TYPE_U8 && canonical->type_kind <= TYPE_U64);
|
||||
assert(left->const_expr.i == 0);
|
||||
left->const_expr.type = CONST_NIL;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_XIPTR, canonical);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sifp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
// TODO
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_INT);
|
||||
assert(canonical->type_kind >= TYPE_F32 && canonical->type_kind <= TYPE_FXX);
|
||||
if (left->const_expr.i > (uint64_t)INT64_MAX)
|
||||
{
|
||||
left->const_expr.f = -((long double)(~left->const_expr.i));
|
||||
}
|
||||
else
|
||||
{
|
||||
left->const_expr.f = left->const_expr.i;
|
||||
}
|
||||
left->const_expr.type = CONST_FLOAT;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_SIFP, canonical);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(canonical->type_kind >= TYPE_I8 && canonical->type_kind <= TYPE_I64);
|
||||
int index = canonical->type_kind - TYPE_I8;
|
||||
uint64_t mask = type_mask[index];
|
||||
if (cast_type != CAST_TYPE_EXPLICIT && from->type_kind == TYPE_UXX)
|
||||
{
|
||||
if (left->const_expr.i > (uint64_t)int_type_max[index])
|
||||
{
|
||||
SEMA_ERROR(left, "Cannot implicitly convert value '%llu' into %s - it will not fit.", left->const_expr.i, type_to_error_string(type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (left->const_expr.i > (uint64_t)INT64_MAX)
|
||||
{
|
||||
left->const_expr.i = ~((~left->const_expr.i) & mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
left->const_expr.i &= mask;
|
||||
}
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_UISI, canonical);
|
||||
|
||||
RETURN_NON_CONST_CAST(CAST_SISI);
|
||||
|
||||
BigInt temp;
|
||||
bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, true);
|
||||
left->const_expr.i = temp;
|
||||
left->const_expr.kind = canonical->type_kind;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast unsigned int -> unsigned int
|
||||
* @return true if this was not a narrowing implicit assign or narrowing implicit assign add
|
||||
*/
|
||||
bool uiui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_INT && type_is_unsigned(from));
|
||||
assert(canonical->type_kind >= TYPE_U8 && canonical->type_kind <= TYPE_U64);
|
||||
int index = canonical->type_kind - TYPE_U8;
|
||||
if (cast_type != CAST_TYPE_EXPLICIT && from->type_kind == TYPE_UXX)
|
||||
{
|
||||
if (left->const_expr.i > uint_type_max[index])
|
||||
{
|
||||
SEMA_ERROR(left, "Cannot implicitly convert value '%llu' into %s - it will not fit.", type_to_error_string(type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
left->const_expr.i &= type_mask[index];
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_UIUI, canonical);
|
||||
bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize;
|
||||
|
||||
if (is_narrowing && (cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT)) EXIT_T_MISMATCH();
|
||||
|
||||
RETURN_NON_CONST_CAST(CAST_UIUI);
|
||||
|
||||
BigInt temp;
|
||||
bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, false);
|
||||
left->const_expr.i = temp;
|
||||
left->const_expr.kind = canonical->type_kind;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool uifp(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
/**
|
||||
* Cast unsigned int -> signed int
|
||||
* @return true if this is an explicit cast or if it is an implicit assign add or if it is a widening cast.
|
||||
*/
|
||||
bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
TODO
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
assert(left->const_expr.type == CONST_INT && type_is_unsigned(from));
|
||||
assert(canonical->type_kind >= TYPE_F32 && canonical->type_kind <= TYPE_F64);
|
||||
left->const_expr.f = left->const_expr.i;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
insert_cast(left, CAST_UIFP, canonical);
|
||||
bool is_widening = from->builtin.bytesize < canonical->builtin.bytesize;
|
||||
|
||||
if (!is_widening && cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
|
||||
RETURN_NON_CONST_CAST(CAST_UISI);
|
||||
|
||||
BigInt temp;
|
||||
bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, true);
|
||||
left->const_expr.i = temp;
|
||||
left->const_expr.kind = canonical->type_kind;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cast signed int -> unsigned int
|
||||
* @return true if this was an implicit add or or explicit cast.
|
||||
*/
|
||||
bool siui(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (cast_type != CAST_TYPE_IMPLICIT_ASSIGN_ADD && cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
|
||||
|
||||
RETURN_NON_CONST_CAST(CAST_SIUI);
|
||||
|
||||
BigInt temp;
|
||||
bigint_truncate(&temp, &left->const_expr.i, canonical->builtin.bitsize, false);
|
||||
left->const_expr.i = temp;
|
||||
left->const_expr.kind = canonical->type_kind;
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cast a signed or unsigned integer -> floating point
|
||||
* @return true always
|
||||
*/
|
||||
bool sifp(Expr *left, Type *canonical, Type *type)
|
||||
{
|
||||
RETURN_NON_CONST_CAST(CAST_SIFP);
|
||||
const_int_to_fp_cast(left, canonical, type);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a signed or unsigned integer -> floating point
|
||||
* @return true always
|
||||
*/
|
||||
bool uifp(Expr *left, Type *canonical, Type *type)
|
||||
{
|
||||
RETURN_NON_CONST_CAST(CAST_UIFP);
|
||||
const_int_to_fp_cast(left, canonical, type);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ixxfp(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
assert(type_is_float(canonical));
|
||||
assert(left->expr_kind == EXPR_CONST);
|
||||
const_int_to_fp_cast(left, canonical, type);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a compile time into to a boolean.
|
||||
* @return true always
|
||||
*/
|
||||
bool ixxbo(Expr *left, Type *type)
|
||||
{
|
||||
assert(left->expr_kind == EXPR_CONST);
|
||||
expr_const_set_bool(&left->const_expr, bigint_cmp_zero(&left->const_expr.i) != CMP_EQ);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ussi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (type->type_kind != TYPE_ENUM) return sema_type_mismatch(left, type, CAST_TYPE_EXPLICIT);
|
||||
@@ -451,7 +433,7 @@ bool ussi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
// TODO
|
||||
Expr *value = left->identifier_expr.decl->enum_constant.expr;
|
||||
assert(value->expr_kind == EXPR_CONST);
|
||||
assert(value->const_expr.type == CONST_INT);
|
||||
// assert(value->const_expr.type == CONST_INT);
|
||||
left->const_expr.i = value->const_expr.i;
|
||||
// TODO narrowing
|
||||
}
|
||||
@@ -469,9 +451,21 @@ bool uius(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
TODO
|
||||
}
|
||||
|
||||
bool xipt(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
/**
|
||||
* Cast comptime, signed or unsigned -> pointer.
|
||||
* @return true unless the constant value evaluates to zero.
|
||||
*/
|
||||
bool xipt(Expr *left, Type *canonical, Type *type)
|
||||
{
|
||||
TODO
|
||||
RETURN_NON_CONST_CAST(CAST_XIPTR);
|
||||
if (bigint_cmp_zero(&left->const_expr.i) != CMP_EQ)
|
||||
{
|
||||
SEMA_ERROR(left, "Only constants evaluating to zero can be cast to pointers.");
|
||||
return false;
|
||||
}
|
||||
expr_const_set_nil(&left->const_expr);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usus(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
@@ -547,8 +541,6 @@ bool cast_to_runtime(Expr *expr)
|
||||
{
|
||||
case TYPE_IXX:
|
||||
return cast(expr, type_long, CAST_TYPE_IMPLICIT);
|
||||
case TYPE_UXX:
|
||||
return cast(expr, type_ulong, CAST_TYPE_IMPLICIT);
|
||||
case TYPE_FXX:
|
||||
return cast(expr, type_double, CAST_TYPE_IMPLICIT);
|
||||
default:
|
||||
@@ -585,16 +577,15 @@ CastKind cast_to_bool_kind(Type *type)
|
||||
return CAST_ERROR;
|
||||
case TYPE_BOOL:
|
||||
UNREACHABLE
|
||||
case TYPE_IXX:
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
case TYPE_I32:
|
||||
case TYPE_I64:
|
||||
case TYPE_IXX:
|
||||
case TYPE_U8:
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
case TYPE_UXX:
|
||||
return CAST_INTBOOL;
|
||||
case TYPE_F32:
|
||||
case TYPE_F64:
|
||||
@@ -618,44 +609,49 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
case TYPE_META_TYPE:
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
if (type_is_integer(canonical)) return boxi(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_float(canonical)) return bofp(expr, from_type, canonical, to_type, cast_type);
|
||||
// Bool may convert into integers and floats but only explicitly.
|
||||
if (type_is_integer(canonical)) return boxi(expr, canonical, to_type, cast_type);
|
||||
if (type_is_float(canonical)) return bofp(expr, canonical, to_type, cast_type);
|
||||
break;
|
||||
case TYPE_ERROR_UNION:
|
||||
TODO
|
||||
case TYPE_IXX:
|
||||
// Compile time integers may convert into ints, floats, bools
|
||||
if (type_is_integer(canonical)) return ixxxi(expr, canonical, to_type, cast_type);
|
||||
if (type_is_float(canonical)) return ixxfp(expr, canonical, to_type, cast_type);
|
||||
if (canonical == type_bool) return ixxbo(expr, to_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return xipt(expr, canonical, to_type);
|
||||
break;
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
case TYPE_I32:
|
||||
case TYPE_I64:
|
||||
case TYPE_IXX:
|
||||
if (type_is_unsigned_integer(canonical)) return siui(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_unsigned_integer(canonical)) return siui(expr, canonical, to_type, cast_type);
|
||||
if (type_is_signed_integer(canonical)) return sisi(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_float(canonical)) return sifp(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical == type_bool) return xibo(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return ptxi(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_float(canonical)) return sifp(expr, canonical, to_type);
|
||||
if (canonical == type_bool) return xibo(expr, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return xipt(expr, canonical, to_type);
|
||||
break;
|
||||
case TYPE_U8:
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
case TYPE_UXX:
|
||||
if (type_is_unsigned_integer(canonical)) return uiui(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_signed_integer(canonical)) return uisi(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_float(canonical)) return uifp(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical == type_bool) return xibo(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return ptxi(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_float(canonical)) return uifp(expr, canonical, to_type);
|
||||
if (canonical == type_bool) return xibo(expr, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return xipt(expr, canonical, to_type);
|
||||
break;
|
||||
case TYPE_F32:
|
||||
case TYPE_F64:
|
||||
case TYPE_FXX:
|
||||
if (type_is_unsigned_integer(canonical)) return fpui(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_signed_integer(canonical)) return fpsi(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical == type_bool) return fpbo(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_integer(canonical)) return fpxi(expr, canonical, to_type, cast_type);
|
||||
if (canonical == type_bool) return fpbo(expr, canonical, to_type, cast_type);
|
||||
if (type_is_float(canonical)) return fpfp(expr, from_type, canonical, to_type, cast_type);
|
||||
break;
|
||||
case TYPE_POINTER:
|
||||
if (type_is_integer(canonical)) return ptxi(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_BOOL) return ptbo(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_integer(canonical)) return ptxi(expr, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_BOOL) return ptbo(expr, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return ptpt(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_FUNC) return ptfu(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_VARARRAY) return ptva(expr, from_type, canonical, to_type, cast_type);
|
||||
@@ -667,7 +663,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
if (type_is_integer(canonical)) return erxi(expr, from_type, canonical, to_type, cast_type);
|
||||
break;
|
||||
case TYPE_FUNC:
|
||||
if (type_is_integer(canonical)) return ptxi(expr, from_type, canonical, to_type, cast_type);
|
||||
if (type_is_integer(canonical)) return ptxi(expr, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return fupt(expr, from_type, canonical, to_type, cast_type);
|
||||
break;
|
||||
case TYPE_STRUCT:
|
||||
|
||||
@@ -32,6 +32,33 @@ typedef struct _Type Type;
|
||||
|
||||
typedef bool(*CastFunc)(Expr *, Type *, Type *, Type *, CastType cast_type);
|
||||
|
||||
typedef struct _BigInt
|
||||
{
|
||||
unsigned digit_count;
|
||||
bool is_negative;
|
||||
union {
|
||||
uint64_t digit;
|
||||
uint64_t *digits;
|
||||
};
|
||||
} BigInt;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
BigInt i;
|
||||
bool b;
|
||||
struct
|
||||
{
|
||||
char* chars;
|
||||
int len;
|
||||
} string;
|
||||
};
|
||||
TypeKind kind;
|
||||
} ExprConst;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SourceLoc loc;
|
||||
@@ -132,18 +159,24 @@ typedef struct
|
||||
struct _Type
|
||||
{
|
||||
TypeKind type_kind : 8;
|
||||
struct _Type *canonical;
|
||||
Type *canonical;
|
||||
const char *name;
|
||||
struct _Type **type_cache;
|
||||
Type **type_cache;
|
||||
void *backend_type;
|
||||
void *backend_debug_type;
|
||||
union
|
||||
{
|
||||
// Error, Struct, Union, Typedef
|
||||
Decl *decl;
|
||||
// int, float, bool
|
||||
TypeBuiltin builtin;
|
||||
// Type[], Type[*], Type[123]
|
||||
TypeArray array;
|
||||
// func Type1(Type2, Type3, ...) throws Err1, Err2, ...
|
||||
TypeFunc func;
|
||||
// Type*
|
||||
Type *pointer;
|
||||
// Type.type
|
||||
Type *child;
|
||||
};
|
||||
};
|
||||
@@ -153,6 +186,7 @@ struct _TypeInfo
|
||||
ResolveStatus resolve_status : 2;
|
||||
Type *type;
|
||||
TypeInfoKind kind;
|
||||
SourceRange span;
|
||||
union
|
||||
{
|
||||
TypeUnresolved unresolved;
|
||||
@@ -217,7 +251,6 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Decl *parent;
|
||||
Expr *expr;
|
||||
Expr **args;
|
||||
uint64_t ordinal;
|
||||
@@ -403,21 +436,7 @@ typedef struct
|
||||
PostUnaryOp operator;
|
||||
} ExprPostUnary;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
uint64_t i;
|
||||
bool b;
|
||||
struct
|
||||
{
|
||||
char* chars;
|
||||
int len;
|
||||
} string;
|
||||
};
|
||||
ConstType type : 3;
|
||||
} ExprConst;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -700,7 +719,6 @@ typedef struct _Ast
|
||||
{
|
||||
SourceRange span;
|
||||
AstKind ast_kind : 8;
|
||||
ExitType exit : 3;
|
||||
union
|
||||
{
|
||||
AstAttribute attribute;
|
||||
@@ -872,7 +890,7 @@ extern Type *type_bool, *type_void, *type_string, *type_voidptr;
|
||||
extern Type *type_float, *type_double;
|
||||
extern Type *type_char, *type_short, *type_int, *type_long, *type_isize;
|
||||
extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize;
|
||||
extern Type *type_compint, *type_compuint, *type_compfloat;
|
||||
extern Type *type_compint, *type_compfloat;
|
||||
extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong;
|
||||
extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong;
|
||||
|
||||
@@ -899,7 +917,6 @@ static inline Ast *new_ast(AstKind kind, SourceRange range)
|
||||
memset(ast, 0, sizeof(Ast));
|
||||
ast->span = range;
|
||||
ast->ast_kind = kind;
|
||||
ast->exit = EXIT_NONE;
|
||||
return ast;
|
||||
}
|
||||
|
||||
@@ -953,24 +970,22 @@ static inline bool builtin_may_bit_negate(Type *canonical)
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
case TYPE_UXX:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline ConstType sign_from_type(Type *type)
|
||||
{
|
||||
assert(type->canonical == type);
|
||||
return (type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX) ? CONST_INT : CONST_INT;
|
||||
}
|
||||
|
||||
bool cast_implicit(Expr *expr, Type *to_type);
|
||||
bool cast(Expr *expr, Type *to_type, CastType cast_type);
|
||||
bool cast_binary_arithmetic(Expr *left, Expr *right, const char *action);
|
||||
CastKind cast_to_bool_kind(Type *type);
|
||||
bool cast_to_runtime(Expr *expr);
|
||||
static inline bool cast_is_implicit(CastType cast_type)
|
||||
{
|
||||
return cast_type == CAST_TYPE_IMPLICIT_ASSIGN_ADD || cast_type == CAST_TYPE_IMPLICIT || cast_type == CAST_TYPE_IMPLICIT_ASSIGN;
|
||||
}
|
||||
|
||||
void llvm_codegen(Context *context);
|
||||
void llvm_set_struct_size_alignment(Decl *decl);
|
||||
@@ -1027,6 +1042,14 @@ static inline void expr_replace(Expr *expr, Expr *replacement)
|
||||
*expr = *replacement;
|
||||
expr->span = loc;
|
||||
}
|
||||
void expr_const_set_int(ExprConst *expr, uint64_t v, TypeKind kind);
|
||||
void expr_const_set_float(ExprConst *expr, long double d, TypeKind kind);
|
||||
void expr_const_set_bool(ExprConst *expr, bool b);
|
||||
void expr_const_set_nil(ExprConst *expr);
|
||||
void expr_const_fprint(FILE *__restrict file, ExprConst *expr);
|
||||
bool expr_const_int_overflowed(const ExprConst *expr);
|
||||
bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op);
|
||||
const char *expr_const_to_error_string(const ExprConst *expr);
|
||||
|
||||
void fprint_ast(FILE *file, Ast *ast);
|
||||
void fprint_decl(FILE *file, Decl *dec);
|
||||
@@ -1131,27 +1154,35 @@ void type_append_signature_name(Type *type, char *dst, size_t *offset);
|
||||
Type *type_find_max_type(Type *type, Type *other);
|
||||
|
||||
static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; }
|
||||
static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX; }
|
||||
static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_UXX; }
|
||||
static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_I64; }
|
||||
static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U64; }
|
||||
static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64; }
|
||||
static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64; }
|
||||
static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; }
|
||||
static inline bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; }
|
||||
bool type_may_have_method_functions(Type *type);
|
||||
static inline bool type_is_integer(Type *type)
|
||||
{
|
||||
assert(type == type->canonical);
|
||||
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_UXX;
|
||||
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_U64;
|
||||
}
|
||||
|
||||
static inline bool type_is_signed_integer(Type *type)
|
||||
static inline bool type_is_any_integer(Type *type)
|
||||
{
|
||||
assert(type == type->canonical);
|
||||
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX;
|
||||
}
|
||||
|
||||
static inline bool type_is_signed_integer(Type *type)
|
||||
{
|
||||
assert(type == type->canonical);
|
||||
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64;
|
||||
}
|
||||
|
||||
static inline bool type_is_unsigned_integer(Type *type)
|
||||
{
|
||||
assert(type == type->canonical);
|
||||
return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_UXX;
|
||||
return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64;
|
||||
}
|
||||
|
||||
static inline bool type_info_poison(TypeInfo *type)
|
||||
@@ -1167,22 +1198,24 @@ static inline bool type_is_float(Type *type)
|
||||
return type->type_kind >= TYPE_F32 && type->type_kind <= TYPE_FXX;
|
||||
}
|
||||
|
||||
static inline TypeInfo *type_info_new(TypeInfoKind kind)
|
||||
static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceRange range)
|
||||
{
|
||||
TypeInfo *type_info = malloc_arena(sizeof(TypeInfo));
|
||||
memset(type_info, 0, sizeof(TypeInfo));
|
||||
type_info->kind = kind;
|
||||
type_info->span = range;
|
||||
type_info->resolve_status = RESOLVE_NOT_DONE;
|
||||
return type_info;
|
||||
}
|
||||
|
||||
static inline TypeInfo *type_info_new_base(Type *type)
|
||||
static inline TypeInfo *type_info_new_base(Type *type, SourceRange range)
|
||||
{
|
||||
TypeInfo *type_info = malloc_arena(sizeof(TypeInfo));
|
||||
memset(type_info, 0, sizeof(TypeInfo));
|
||||
type_info->kind = TYPE_INFO_IDENTIFIER;
|
||||
type_info->resolve_status = RESOLVE_DONE;
|
||||
type_info->type = type;
|
||||
type_info->span = range;
|
||||
return type_info;
|
||||
}
|
||||
|
||||
@@ -1210,6 +1243,7 @@ static inline bool type_is_number(Type *type)
|
||||
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_FXX;
|
||||
}
|
||||
|
||||
|
||||
#define TYPE_MODULE_UNRESOLVED(_module, _name) ({ Type *__type = type_new(TYPE_USER_DEFINED); \
|
||||
__type->name_loc = _name; __type->unresolved.module = _module; __type; })
|
||||
#define TYPE_UNRESOLVED(_name) ({ TypeInfo *__type = type_new(TYPE_USER_DEFINED); __type->name_loc = _name; __type; })
|
||||
|
||||
@@ -40,6 +40,7 @@ typedef enum
|
||||
BINARYOP_BIT_AND,
|
||||
BINARYOP_AND,
|
||||
BINARYOP_OR,
|
||||
// Don't change the ordering for GT to EQ or things will break
|
||||
BINARYOP_GT,
|
||||
BINARYOP_GE,
|
||||
BINARYOP_LT,
|
||||
@@ -178,14 +179,6 @@ typedef enum _CastType
|
||||
CAST_TYPE_IMPLICIT_ASSIGN_ADD,
|
||||
} CastType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONST_NIL,
|
||||
CONST_BOOL,
|
||||
CONST_INT,
|
||||
CONST_FLOAT,
|
||||
CONST_STRING,
|
||||
} ConstType;
|
||||
|
||||
|
||||
typedef enum
|
||||
@@ -214,8 +207,9 @@ typedef enum
|
||||
// Ordering here is in priority if two branches should have the same exit.
|
||||
typedef enum
|
||||
{
|
||||
EXIT_NONE,
|
||||
EXIT_NONE = 0,
|
||||
EXIT_BREAK,
|
||||
EXIT_NEXT,
|
||||
EXIT_GOTO,
|
||||
EXIT_CONTINUE,
|
||||
EXIT_THROW,
|
||||
@@ -507,12 +501,11 @@ typedef enum
|
||||
TYPE_I16,
|
||||
TYPE_I32,
|
||||
TYPE_I64,
|
||||
TYPE_IXX,
|
||||
TYPE_U8,
|
||||
TYPE_U16,
|
||||
TYPE_U32,
|
||||
TYPE_U64,
|
||||
TYPE_UXX,
|
||||
TYPE_IXX,
|
||||
TYPE_F32,
|
||||
TYPE_F64,
|
||||
TYPE_FXX,
|
||||
@@ -532,6 +525,12 @@ typedef enum
|
||||
TYPE_LAST = TYPE_META_TYPE
|
||||
} TypeKind;
|
||||
|
||||
#define ALL_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: \
|
||||
case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: case TYPE_IXX
|
||||
#define ALL_SIGNED_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64
|
||||
#define ALL_UNSIGNED_INTS TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64
|
||||
#define ALL_FLOATS TYPE_F32: case TYPE_F64: case TYPE_FXX
|
||||
|
||||
#define TYPE_KINDS (TYPE_LAST + 1)
|
||||
|
||||
typedef enum
|
||||
@@ -540,6 +539,7 @@ typedef enum
|
||||
UNARYOP_DEREF,
|
||||
UNARYOP_ADDR,
|
||||
UNARYOP_NEG,
|
||||
UNARYOP_NEGMOD,
|
||||
UNARYOP_BITNEG,
|
||||
UNARYOP_NOT,
|
||||
UNARYOP_INC,
|
||||
|
||||
@@ -318,11 +318,10 @@ static Token scan_binary(Lexer *lexer)
|
||||
|
||||
static inline Token scan_hex(Lexer *lexer)
|
||||
{
|
||||
char x = next(lexer); // skip the x
|
||||
if (!is_hex(next(lexer)))
|
||||
{
|
||||
return error_token(lexer, "'0%c' starts a hexadecimal number, "
|
||||
"but it was followed by '%c' which is not part of a hexadecimal number.", x, prev(lexer));
|
||||
return error_token(lexer, "'0x' starts a hexadecimal number, "
|
||||
"but it was followed by '%c' which is not part of a hexadecimal number.", prev(lexer));
|
||||
}
|
||||
while (is_hex_or_(peek(lexer))) next(lexer);
|
||||
bool is_float = false;
|
||||
|
||||
@@ -107,7 +107,6 @@ LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_IXX:
|
||||
case TYPE_UXX:
|
||||
case TYPE_FXX:
|
||||
case TYPE_META_TYPE:
|
||||
UNREACHABLE
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
#include "compiler_internal.h"
|
||||
#include "bigint.h"
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right)
|
||||
{
|
||||
@@ -228,12 +229,20 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
|
||||
return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(type_bool->backend_type, 1, 0), "not");
|
||||
case UNARYOP_BITNEG:
|
||||
return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot");
|
||||
case UNARYOP_NEGMOD:
|
||||
return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "negmod");
|
||||
case UNARYOP_NEG:
|
||||
// TODO improve how unsigned numbers are negated.
|
||||
if (type_is_float(expr->unary_expr.expr->type->canonical))
|
||||
{
|
||||
return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg");
|
||||
}
|
||||
return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg");
|
||||
if (type_is_unsigned(expr->unary_expr.expr->type->canonical))
|
||||
{
|
||||
return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg");
|
||||
}
|
||||
// TODO insert trap
|
||||
return LLVMBuildNSWNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg");
|
||||
case UNARYOP_ADDR:
|
||||
return gencontext_emit_address(context, expr->unary_expr.expr);
|
||||
case UNARYOP_DEREF:
|
||||
@@ -301,6 +310,128 @@ static inline LLVMValueRef gencontext_emit_struct_value_expr(GenContext *context
|
||||
}
|
||||
|
||||
|
||||
static LLVMValueRef gencontext_emit_int_comparison(GenContext *context, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op)
|
||||
{
|
||||
bool lhs_signed = type_is_signed(lhs_type);
|
||||
bool rhs_signed = type_is_signed(rhs_type);
|
||||
if (lhs_signed != rhs_signed)
|
||||
{
|
||||
// Swap sides if needed.
|
||||
if (!lhs_signed)
|
||||
{
|
||||
Type *temp = lhs_type;
|
||||
lhs_type = rhs_type;
|
||||
rhs_type = temp;
|
||||
LLVMValueRef temp_val = lhs_value;
|
||||
lhs_value = rhs_value;
|
||||
rhs_value = temp_val;
|
||||
switch (binary_op)
|
||||
{
|
||||
case BINARYOP_GE:
|
||||
binary_op = BINARYOP_LE;
|
||||
break;
|
||||
case BINARYOP_GT:
|
||||
binary_op = BINARYOP_LT;
|
||||
break;
|
||||
case BINARYOP_LE:
|
||||
binary_op = BINARYOP_GE;
|
||||
break;
|
||||
case BINARYOP_LT:
|
||||
binary_op = BINARYOP_GT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
lhs_signed = true;
|
||||
rhs_signed = false;
|
||||
}
|
||||
}
|
||||
if (!lhs_signed)
|
||||
{
|
||||
assert(lhs_signed == rhs_signed);
|
||||
// Right and left side are both unsigned.
|
||||
switch (binary_op)
|
||||
{
|
||||
case BINARYOP_EQ:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
|
||||
case BINARYOP_NE:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
|
||||
case BINARYOP_GE:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntUGE, lhs_value, rhs_value, "ge");
|
||||
case BINARYOP_GT:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntUGT, lhs_value, rhs_value, "gt");
|
||||
case BINARYOP_LE:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntULE, lhs_value, rhs_value, "le");
|
||||
case BINARYOP_LT:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntULT, lhs_value, rhs_value, "lt");
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
// Left side is signed.
|
||||
LLVMValueRef comp_value;
|
||||
LLVMValueRef check_value;
|
||||
switch (binary_op)
|
||||
{
|
||||
case BINARYOP_EQ:
|
||||
comp_value = LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
|
||||
break;
|
||||
case BINARYOP_NE:
|
||||
comp_value = LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
|
||||
break;
|
||||
case BINARYOP_GE:
|
||||
comp_value = LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, rhs_value, "ge");
|
||||
break;
|
||||
case BINARYOP_GT:
|
||||
comp_value = LLVMBuildICmp(context->builder, LLVMIntSGT, lhs_value, rhs_value, "gt");
|
||||
break;
|
||||
case BINARYOP_LE:
|
||||
comp_value = LLVMBuildICmp(context->builder, LLVMIntSLE, lhs_value, rhs_value, "le");
|
||||
break;
|
||||
case BINARYOP_LT:
|
||||
comp_value = LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, rhs_value, "lt");
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
// If right side is also signed then this is fine.
|
||||
if (rhs_signed) return comp_value;
|
||||
|
||||
// Otherwise, special handling for left side signed, right side unsigned.
|
||||
LLVMValueRef zero = LLVMConstInt(llvm_type(lhs_type), 0, true);
|
||||
switch (binary_op)
|
||||
{
|
||||
case BINARYOP_EQ:
|
||||
// Only true if lhs >= 0
|
||||
check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, zero, "check");
|
||||
return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-eq");
|
||||
case BINARYOP_NE:
|
||||
// Always true if lhs < 0
|
||||
check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, zero, "check");
|
||||
return LLVMBuildOr(context->builder, check_value, comp_value, "siui-ne");
|
||||
case BINARYOP_GE:
|
||||
// Only true if rhs >= 0 when regarded as a signed integer
|
||||
check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, rhs_value, zero, "check");
|
||||
return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-ge");
|
||||
case BINARYOP_GT:
|
||||
// Only true if rhs >= 0 when regarded as a signed integer
|
||||
check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, rhs_value, zero, "check");
|
||||
return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-gt");
|
||||
case BINARYOP_LE:
|
||||
// Always true if rhs < 0 when regarded as a signed integer
|
||||
check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, rhs_value, zero, "check");
|
||||
return LLVMBuildOr(context->builder, check_value, comp_value, "siui-le");
|
||||
case BINARYOP_LT:
|
||||
// Always true if rhs < 0 when regarded as a signed integer
|
||||
check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, rhs_value, zero, "check");
|
||||
return LLVMBuildOr(context->builder, check_value, comp_value, "siui-lt");
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVMValueRef lhs_addr, BinaryOp binary_op)
|
||||
{
|
||||
@@ -325,9 +456,12 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
}
|
||||
|
||||
rhs_value = gencontext_emit_expr(context, rhs);
|
||||
|
||||
bool is_float = type_is_float(type);
|
||||
|
||||
Type *lhs_type = expr->binary_expr.left->type->canonical;
|
||||
if (type_is_integer(lhs_type) && binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)
|
||||
{
|
||||
return gencontext_emit_int_comparison(context, lhs_type, rhs->type->canonical, lhs_value, rhs_value, binary_op);
|
||||
}
|
||||
bool is_float = type_is_float(lhs_type);
|
||||
switch (binary_op)
|
||||
{
|
||||
case BINARYOP_ERROR:
|
||||
@@ -335,7 +469,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
case BINARYOP_MULT:
|
||||
if (is_float) return LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul");
|
||||
// TODO insert trap
|
||||
if (type_is_unsigned_integer(lhs->type->canonical))
|
||||
if (type_is_unsigned_integer(lhs_type))
|
||||
{
|
||||
return LLVMBuildNUWMul(context->builder, lhs_value, rhs_value, "umul");
|
||||
}
|
||||
@@ -347,7 +481,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul");
|
||||
case BINARYOP_SUB:
|
||||
case BINARYOP_SUB_MOD:
|
||||
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||
if (lhs_type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff");
|
||||
rhs_value = LLVMBuildNeg(context->builder, rhs_value, "");
|
||||
@@ -357,24 +491,24 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
return gencontext_emit_sub_int(context, lhs->type->canonical, binary_op == BINARYOP_SUB_MOD, lhs_value, rhs_value);
|
||||
case BINARYOP_ADD:
|
||||
case BINARYOP_ADD_MOD:
|
||||
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||
if (lhs_type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
assert(type_is_integer(rhs->type->canonical));
|
||||
return LLVMBuildGEP2(context->builder, llvm_type(lhs->type), lhs_value, &rhs_value, 1, "ptradd");
|
||||
return LLVMBuildGEP2(context->builder, llvm_type(lhs_type), lhs_value, &rhs_value, 1, "ptradd");
|
||||
}
|
||||
if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd");
|
||||
return gencontext_emit_add_int(context, lhs->type->canonical, binary_op == BINARYOP_ADD_MOD, lhs_value, rhs_value);
|
||||
return gencontext_emit_add_int(context, lhs_type, binary_op == BINARYOP_ADD_MOD, lhs_value, rhs_value);
|
||||
case BINARYOP_DIV:
|
||||
if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv");
|
||||
return type_is_unsigned(type)
|
||||
return type_is_unsigned(lhs_type)
|
||||
? LLVMBuildUDiv(context->builder, lhs_value, rhs_value, "udiv")
|
||||
: LLVMBuildSDiv(context->builder, lhs_value, rhs_value, "sdiv");
|
||||
case BINARYOP_MOD:
|
||||
return type_is_unsigned(type)
|
||||
return type_is_unsigned(lhs_type)
|
||||
? LLVMBuildURem(context->builder, lhs_value, rhs_value, "umod")
|
||||
: LLVMBuildSRem(context->builder, lhs_value, rhs_value, "smod");
|
||||
case BINARYOP_SHR:
|
||||
return type_is_unsigned(type)
|
||||
return type_is_unsigned(lhs_type)
|
||||
? LLVMBuildLShr(context->builder, lhs_value, rhs_value, "lshr")
|
||||
: LLVMBuildAShr(context->builder, lhs_value, rhs_value, "ashr");
|
||||
case BINARYOP_SHL:
|
||||
@@ -386,33 +520,25 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
case BINARYOP_BIT_XOR:
|
||||
return LLVMBuildXor(context->builder, lhs_value, rhs_value, "xor");
|
||||
case BINARYOP_EQ:
|
||||
assert(!type_is_integer(lhs_type));
|
||||
// Unordered?
|
||||
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq");
|
||||
return LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq");
|
||||
case BINARYOP_NE:
|
||||
assert(!type_is_integer(lhs_type));
|
||||
// Unordered?
|
||||
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq");
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq");
|
||||
case BINARYOP_GE:
|
||||
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge");
|
||||
return type_is_unsigned(type)
|
||||
? LLVMBuildICmp(context->builder, LLVMIntUGE, lhs_value, rhs_value, "ge")
|
||||
: LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, rhs_value, "ge");
|
||||
assert(!type_is_integer(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge");
|
||||
case BINARYOP_GT:
|
||||
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt");
|
||||
return type_is_unsigned(type)
|
||||
? LLVMBuildICmp(context->builder, LLVMIntUGT, lhs_value, rhs_value, "gt")
|
||||
: LLVMBuildICmp(context->builder, LLVMIntSGT, lhs_value, rhs_value, "gt");
|
||||
assert(!type_is_integer(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt");
|
||||
case BINARYOP_LE:
|
||||
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le");
|
||||
return type_is_unsigned(type)
|
||||
? LLVMBuildICmp(context->builder, LLVMIntULE, lhs_value, rhs_value, "le")
|
||||
: LLVMBuildICmp(context->builder, LLVMIntSLE, lhs_value, rhs_value, "le");
|
||||
assert(!type_is_integer(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le");
|
||||
case BINARYOP_LT:
|
||||
if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt");
|
||||
return type_is_unsigned(type)
|
||||
? LLVMBuildICmp(context->builder, LLVMIntULT, lhs_value, rhs_value, "lt")
|
||||
: LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, rhs_value, "lt");
|
||||
assert(!type_is_integer(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt");
|
||||
case BINARYOP_AND:
|
||||
case BINARYOP_OR:
|
||||
UNREACHABLE
|
||||
@@ -543,17 +669,24 @@ static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *e
|
||||
LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMTypeRef type = llvm_type(expr->type);
|
||||
switch (expr->const_expr.type)
|
||||
switch (expr->const_expr.kind)
|
||||
{
|
||||
case CONST_INT:
|
||||
return LLVMConstInt(type, expr->const_expr.i, type_is_unsigned(expr->type->canonical) ? false : true);
|
||||
case CONST_FLOAT:
|
||||
case ALL_INTS:
|
||||
if (type_is_unsigned(expr->type->canonical))
|
||||
{
|
||||
return LLVMConstInt(type, bigint_as_unsigned(&expr->const_expr.i), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return LLVMConstInt(type, (uint64_t)bigint_as_signed(&expr->const_expr.i), false);
|
||||
}
|
||||
case ALL_FLOATS:
|
||||
return LLVMConstReal(type, (double) expr->const_expr.f);
|
||||
case CONST_NIL:
|
||||
case TYPE_POINTER:
|
||||
return LLVMConstNull(type);
|
||||
case CONST_BOOL:
|
||||
case TYPE_BOOL:
|
||||
return LLVMConstInt(type, expr->const_expr.b ? 1 : 0, false);
|
||||
case CONST_STRING:
|
||||
case TYPE_STRING:
|
||||
{
|
||||
LLVMValueRef global_name = LLVMAddGlobal(context->module, type, "string");
|
||||
LLVMSetLinkage(global_name, LLVMInternalLinkage);
|
||||
@@ -564,8 +697,9 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
|
||||
0));
|
||||
return global_name;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
|
||||
|
||||
@@ -96,6 +96,7 @@ void gencontext_emit_implicit_return(GenContext *context)
|
||||
|
||||
void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
{
|
||||
DEBUG_LOG("Generating function %s.", decl->external_name);
|
||||
assert(decl->func.backend_value);
|
||||
|
||||
LLVMValueRef prev_function = context->function;
|
||||
@@ -143,7 +144,7 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
gencontext_emit_stmt(context, decl->func.body->compound_stmt.stmts[i]);
|
||||
}
|
||||
|
||||
if (!LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block)))
|
||||
if (context->current_block && !LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block)))
|
||||
{
|
||||
LLVMBasicBlockRef prev_block = LLVMGetPreviousBasicBlock(context->current_block);
|
||||
LLVMDeleteBasicBlock(context->current_block);
|
||||
@@ -151,9 +152,8 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
LLVMPositionBuilderAtEnd(context->builder, context->current_block);
|
||||
}
|
||||
// Insert a return (and defer) if needed.
|
||||
if (!LLVMGetBasicBlockTerminator(context->current_block))
|
||||
if (context->current_block && !LLVMGetBasicBlockTerminator(context->current_block))
|
||||
{
|
||||
assert(decl->func.function_signature.rtype->type->type_kind == TYPE_VOID);
|
||||
assert(decl->func.body->compound_stmt.defer_list.end == NULL);
|
||||
gencontext_emit_defer(context, decl->func.body->compound_stmt.defer_list.start, NULL);
|
||||
gencontext_emit_implicit_return(context);
|
||||
|
||||
@@ -134,16 +134,18 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast)
|
||||
if (!ret_value)
|
||||
{
|
||||
gencontext_emit_implicit_return(context);
|
||||
return;
|
||||
}
|
||||
if (context->return_out)
|
||||
{
|
||||
LLVMBuildStore(context->builder, ret_value, context->return_out);
|
||||
gencontext_emit_implicit_return(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMBuildRet(context->builder, ret_value);
|
||||
if (context->return_out)
|
||||
{
|
||||
LLVMBuildStore(context->builder, ret_value, context->return_out);
|
||||
gencontext_emit_implicit_return(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMBuildRet(context->builder, ret_value);
|
||||
}
|
||||
}
|
||||
context->current_block = NULL;
|
||||
LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret");
|
||||
|
||||
@@ -154,9 +154,6 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type)
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_IXX:
|
||||
case TYPE_UXX:
|
||||
case TYPE_FXX:
|
||||
case TYPE_META_TYPE:
|
||||
UNREACHABLE;
|
||||
case TYPE_TYPEDEF:
|
||||
@@ -172,6 +169,7 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type)
|
||||
case TYPE_VOID:
|
||||
return type->backend_type = LLVMVoidTypeInContext(context);
|
||||
case TYPE_F64:
|
||||
case TYPE_FXX:
|
||||
return type->backend_type = LLVMDoubleTypeInContext(context);
|
||||
case TYPE_F32:
|
||||
return type->backend_type = LLVMFloatTypeInContext(context);
|
||||
@@ -180,6 +178,7 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type)
|
||||
return type->backend_type = LLVMIntTypeInContext(context, 64U);
|
||||
case TYPE_U32:
|
||||
case TYPE_I32:
|
||||
case TYPE_IXX:
|
||||
return type->backend_type = LLVMIntTypeInContext(context, 32U);
|
||||
case TYPE_U16:
|
||||
case TYPE_I16:
|
||||
|
||||
265
src/compiler/number.c
Normal file
265
src/compiler/number.c
Normal file
@@ -0,0 +1,265 @@
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include "bigint.h"
|
||||
|
||||
#define CHECK_SI_KIND(_kind) assert(_kind >= TYPE_I8 && _kind <= TYPE_I64)
|
||||
#define CHECK_IXX_KIND(_kind) assert(_kind == TYPE_IXX)
|
||||
#define CHECK_UI_KIND(_kind) assert(_kind >= TYPE_U8 && _kind <= TYPE_U64)
|
||||
#define CHECK_INT_KIND(_kind) assert(_kind >= TYPE_I8 && _kind <= TYPE_U64)
|
||||
#define CHECK_CONVERSION(_kind) assert(i->kind != _kind && "Unnecessary conversion")
|
||||
#define TYPE_MATCH assert(left->kind == right->kind && left != res && right != res)
|
||||
|
||||
static uint64_t type_mask[TYPE_U64 + 1] = {
|
||||
[TYPE_U8] = 0xFF,
|
||||
[TYPE_I8] = 0xFF,
|
||||
[TYPE_U16] = 0xFFFF,
|
||||
[TYPE_I16] = 0xFFFF,
|
||||
[TYPE_U32] = 0xFFFFFFFFU,
|
||||
[TYPE_I32] = 0xFFFFFFFFU,
|
||||
[TYPE_U64] = 0xFFFFFFFFFFFFFFFFLLU,
|
||||
[TYPE_I64] = 0xFFFFFFFFFFFFFFFFLLU,
|
||||
};
|
||||
|
||||
static int type_bits[TYPE_U64 + 1] = {
|
||||
[TYPE_U8] = 8,
|
||||
[TYPE_I8] = 8,
|
||||
[TYPE_U16] = 16,
|
||||
[TYPE_I16] = 16,
|
||||
[TYPE_U32] = 32,
|
||||
[TYPE_I32] = 32,
|
||||
[TYPE_U64] = 64,
|
||||
[TYPE_I64] = 64,
|
||||
};
|
||||
|
||||
static int64_t int_type_max[TYPE_I64 + 1] = {
|
||||
[TYPE_I8] = 0x7F,
|
||||
[TYPE_I16] = 0x7FFF,
|
||||
[TYPE_I32] = 0x7FFFFFFFLL,
|
||||
[TYPE_I64] = 0x7FFFFFFFFFFFFFFFLL,
|
||||
};
|
||||
|
||||
static int64_t int_type_min[TYPE_I64 + 1] = {
|
||||
[TYPE_I8] = -0x80,
|
||||
[TYPE_I16] = -0x8000L,
|
||||
[TYPE_I32] = -0x80000000L,
|
||||
[TYPE_I64] = -0x8000000000000000LL,
|
||||
};
|
||||
|
||||
void expr_const_fprint(FILE *__restrict file, ExprConst *expr)
|
||||
{
|
||||
switch (expr->kind)
|
||||
{
|
||||
case TYPE_POINTER:
|
||||
fprintf(file, "nil");
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
fprintf(file, expr->b ? "true" : "false");
|
||||
break;
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
case TYPE_I32:
|
||||
case TYPE_I64:
|
||||
case TYPE_U8:
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
case TYPE_IXX:
|
||||
bigint_fprint(file, &expr->i, 10);
|
||||
break;
|
||||
case TYPE_F32:
|
||||
case TYPE_F64:
|
||||
case TYPE_FXX:
|
||||
fprintf(file, "%Lf", expr->f);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void expr_const_set_int(ExprConst *expr, uint64_t v, TypeKind kind)
|
||||
{
|
||||
if (type_kind_is_signed(kind))
|
||||
{
|
||||
bigint_init_signed(&expr->i, (int64_t)v);
|
||||
}
|
||||
else
|
||||
{
|
||||
bigint_init_unsigned(&expr->i, v);
|
||||
}
|
||||
expr->kind = kind;
|
||||
}
|
||||
|
||||
void expr_const_set_bool(ExprConst *expr, bool b)
|
||||
{
|
||||
expr->b = b;
|
||||
expr->kind = TYPE_BOOL;
|
||||
}
|
||||
|
||||
void expr_const_set_nil(ExprConst *expr)
|
||||
{
|
||||
expr->i.digit_count = 0;
|
||||
expr->i.digit = 0;
|
||||
expr->kind = TYPE_POINTER;
|
||||
}
|
||||
|
||||
static inline bool compare_bool(bool left, bool right, BinaryOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case BINARYOP_EQ:
|
||||
return left == right;
|
||||
case BINARYOP_NE:
|
||||
return left != right;
|
||||
case BINARYOP_GT:
|
||||
return left > right;
|
||||
case BINARYOP_LT:
|
||||
return left < right;
|
||||
case BINARYOP_GE:
|
||||
return left >= right;
|
||||
case BINARYOP_LE:
|
||||
return left <= right;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool compare_ints(const BigInt *left, const BigInt *right, BinaryOp op)
|
||||
{
|
||||
CmpRes res = bigint_cmp(left, right);
|
||||
switch (op)
|
||||
{
|
||||
case BINARYOP_GE:
|
||||
return res != CMP_LT;
|
||||
case BINARYOP_LE:
|
||||
return res != CMP_GT;
|
||||
case BINARYOP_NE:
|
||||
return res != CMP_EQ;
|
||||
case BINARYOP_GT:
|
||||
return res == CMP_GT;
|
||||
case BINARYOP_LT:
|
||||
return res == CMP_LT;
|
||||
case BINARYOP_EQ:
|
||||
return res == CMP_EQ;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool compare_fps(long double left, long double right, BinaryOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case BINARYOP_GE:
|
||||
return left >= right;
|
||||
case BINARYOP_LE:
|
||||
return left <= right;
|
||||
case BINARYOP_NE:
|
||||
return left != right;
|
||||
case BINARYOP_GT:
|
||||
return left > right;
|
||||
case BINARYOP_LT:
|
||||
return left < right;
|
||||
case BINARYOP_EQ:
|
||||
return left == right;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op)
|
||||
{
|
||||
switch (left->kind)
|
||||
{
|
||||
case TYPE_BOOL:
|
||||
return compare_bool(left->b, right->b, op);
|
||||
case ALL_INTS:
|
||||
return compare_ints(&left->i, &right->i, op);
|
||||
case ALL_FLOATS:
|
||||
return compare_fps(left->f, right->f, op);
|
||||
case TYPE_POINTER:
|
||||
return true;
|
||||
case TYPE_STRING:
|
||||
TODO
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
bool expr_const_int_overflowed(const ExprConst *expr)
|
||||
{
|
||||
switch (expr->kind)
|
||||
{
|
||||
case TYPE_I8:
|
||||
return !bigint_fits_in_bits(&expr->i, 8, true);
|
||||
case TYPE_I16:
|
||||
return !bigint_fits_in_bits(&expr->i, 16, true);
|
||||
case TYPE_I32:
|
||||
return !bigint_fits_in_bits(&expr->i, 32, true);
|
||||
case TYPE_I64:
|
||||
return !bigint_fits_in_bits(&expr->i, 64, true);
|
||||
case TYPE_U8:
|
||||
return !bigint_fits_in_bits(&expr->i, 8, false);
|
||||
case TYPE_U16:
|
||||
return !bigint_fits_in_bits(&expr->i, 16, false);
|
||||
case TYPE_U32:
|
||||
return !bigint_fits_in_bits(&expr->i, 32, false);
|
||||
case TYPE_U64:
|
||||
return !bigint_fits_in_bits(&expr->i, 64, false);
|
||||
case TYPE_IXX:
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
const char *expr_const_to_error_string(const ExprConst *expr)
|
||||
{
|
||||
char *buff = NULL;
|
||||
switch (expr->kind)
|
||||
{
|
||||
case TYPE_POINTER:
|
||||
return "nil";
|
||||
case TYPE_BOOL:
|
||||
return expr->b ? "true" : "false";
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
case TYPE_I32:
|
||||
case TYPE_I64:
|
||||
case TYPE_U8:
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
case TYPE_IXX:
|
||||
return bigint_to_error_string(&expr->i, 10);
|
||||
case TYPE_F32:
|
||||
case TYPE_F64:
|
||||
case TYPE_FXX:
|
||||
asprintf(&buff, "%Lf", expr->f);
|
||||
return buff;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void expr_const_set_float(ExprConst *expr, long double d, TypeKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case TYPE_F32:
|
||||
expr->f = (float)d;
|
||||
break;
|
||||
case TYPE_F64:
|
||||
expr->f = (double)d;
|
||||
break;
|
||||
default:
|
||||
expr->f = d;
|
||||
}
|
||||
expr->kind = kind;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include "parser_internal.h"
|
||||
#include "bigint.h"
|
||||
|
||||
|
||||
typedef Expr *(*ParseFn)(Context *context, Expr *);
|
||||
@@ -276,6 +277,7 @@ static Expr *parse_binary(Context *context, Expr *left_side)
|
||||
expr->binary_expr.operator = binaryop_from_token(operator_type);
|
||||
expr->binary_expr.left = left_side;
|
||||
expr->binary_expr.right = right_side;
|
||||
expr->span.end_loc = right_side->span.end_loc;
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -389,7 +391,6 @@ static Expr *parse_integer(Context *context, Expr *left)
|
||||
Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
const char *string = context->tok.start;
|
||||
const char *end = string + source_range_len(context->tok.span);
|
||||
uint64_t i = 0;
|
||||
if (string[0] == '\'')
|
||||
{
|
||||
union
|
||||
@@ -431,27 +432,39 @@ static Expr *parse_integer(Context *context, Expr *left)
|
||||
}
|
||||
bytes.b[pos++] = (unsigned)*string;
|
||||
}
|
||||
|
||||
switch (pos)
|
||||
{
|
||||
case 1:
|
||||
expr_int->const_expr.i = bytes.u8;
|
||||
expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U8);
|
||||
expr_int->type = type_byte;
|
||||
break;
|
||||
case 2:
|
||||
expr_int->const_expr.i = bytes.u16;
|
||||
expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U16);
|
||||
expr_int->type = type_ushort;
|
||||
break;
|
||||
case 4:
|
||||
expr_int->const_expr.i = bytes.u32;
|
||||
expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U32);
|
||||
expr_int->type = type_uint;
|
||||
break;
|
||||
case 8:
|
||||
expr_int->const_expr.i = bytes.u64;
|
||||
expr_const_set_int(&expr_int->const_expr, bytes.u8, TYPE_U64);
|
||||
expr_int->type = type_ulong;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
expr_int->const_expr.type = CONST_INT;
|
||||
expr_int->type = i > INT64_MAX ? type_compuint : type_compint;
|
||||
expr_int->resolve_status = RESOLVE_DONE;
|
||||
advance(context);
|
||||
return expr_int;
|
||||
}
|
||||
BigInt *i = &expr_int->const_expr.i;
|
||||
bigint_init_unsigned(i, 0);
|
||||
BigInt diff;
|
||||
bigint_init_unsigned(&diff, 0);
|
||||
BigInt ten;
|
||||
bigint_init_unsigned(&ten, 10);
|
||||
BigInt res;
|
||||
switch (source_range_len(context->tok.span) > 2 ? string[1] : '0')
|
||||
{
|
||||
case 'x':
|
||||
@@ -460,24 +473,20 @@ static Expr *parse_integer(Context *context, Expr *left)
|
||||
{
|
||||
char c = *(string++);
|
||||
if (c == '_') continue;
|
||||
if (i > (UINT64_MAX >> 4U))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
i <<= 4U;
|
||||
bigint_shl_int(&res, i, 4);
|
||||
if (c < 'A')
|
||||
{
|
||||
i += c - '0';
|
||||
bigint_init_unsigned(&diff, c - '0');
|
||||
}
|
||||
else if (c < 'a')
|
||||
{
|
||||
i += c - 'A' + 10;
|
||||
bigint_init_unsigned(&diff, c - 'A' + 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
i += c - 'a' + 10;
|
||||
bigint_init_unsigned(&diff, c - 'a' + 10);
|
||||
}
|
||||
bigint_add(i, &res, &diff);
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
@@ -486,13 +495,9 @@ static Expr *parse_integer(Context *context, Expr *left)
|
||||
{
|
||||
char c = *(string++);
|
||||
if (c == '_') continue;
|
||||
if (i > (UINT64_MAX >> 3U))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
i <<= (unsigned) 3;
|
||||
i += c - '0';
|
||||
bigint_shl_int(&res, i, 4);
|
||||
bigint_init_unsigned(&diff, c - '0');
|
||||
bigint_add(i, &res, &diff);
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
@@ -501,13 +506,9 @@ static Expr *parse_integer(Context *context, Expr *left)
|
||||
{
|
||||
char c = *(string++);
|
||||
if (c == '_') continue;
|
||||
if (i > (UINT64_MAX >> 1U))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
i <<= (unsigned) 1;
|
||||
i += c - '0';
|
||||
bigint_shl_int(&res, i, 1);
|
||||
bigint_init_unsigned(&diff, c - '0');
|
||||
bigint_add(i, &res, &diff);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -515,20 +516,14 @@ static Expr *parse_integer(Context *context, Expr *left)
|
||||
{
|
||||
char c = *(string++);
|
||||
if (c == '_') continue;
|
||||
if (i > (UINT64_MAX / 10))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
i *= 10;
|
||||
i += c - '0';
|
||||
bigint_mul(&res, i, &ten);
|
||||
bigint_init_unsigned(&diff, c - '0');
|
||||
bigint_add(i, &res, &diff);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
expr_int->const_expr.i = i;
|
||||
expr_int->const_expr.type = CONST_INT;
|
||||
expr_int->type = i > INT64_MAX ? type_compuint : type_compint;
|
||||
expr_int->const_expr.kind = TYPE_IXX;
|
||||
expr_int->type = type_compint;
|
||||
advance(context);
|
||||
return expr_int;
|
||||
}
|
||||
@@ -549,7 +544,7 @@ static Expr *parse_double(Context *context, Expr *left)
|
||||
advance(context);
|
||||
number->const_expr.f = fval;
|
||||
number->type = type_compfloat;
|
||||
number->const_expr.type = CONST_FLOAT;
|
||||
number->const_expr.kind = TYPE_FXX;
|
||||
return number;
|
||||
}
|
||||
|
||||
@@ -678,7 +673,7 @@ static Expr *parse_string_literal(Context *context, Expr *left)
|
||||
expr_string->const_expr.string.chars = str;
|
||||
expr_string->const_expr.string.len = len;
|
||||
expr_string->type = type_string;
|
||||
expr_string->const_expr.type = CONST_STRING;
|
||||
expr_string->const_expr.kind = TYPE_STRING;
|
||||
return expr_string;
|
||||
}
|
||||
|
||||
@@ -686,7 +681,7 @@ static Expr *parse_bool(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Had left hand side");
|
||||
Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
number->const_expr = (ExprConst) { .b = context->tok.type == TOKEN_TRUE, .type = CONST_BOOL };
|
||||
number->const_expr = (ExprConst) { .b = context->tok.type == TOKEN_TRUE, .kind = TYPE_BOOL };
|
||||
number->type = type_bool;
|
||||
number->resolve_status = RESOLVE_DONE;
|
||||
advance(context);
|
||||
@@ -697,7 +692,7 @@ static Expr *parse_nil(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Had left hand side");
|
||||
Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
number->const_expr.type = CONST_NIL;
|
||||
number->const_expr.kind = TYPE_POINTER;
|
||||
number->type = type_voidptr;
|
||||
advance(context);
|
||||
return number;
|
||||
@@ -734,7 +729,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
|
||||
//[TOKEN_SIZEOF] = { parse_sizeof, NULL, PREC_NONE },
|
||||
[TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL },
|
||||
[TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_MINUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_MINUS_MOD] = { parse_unary_expr, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_PLUS] = { NULL, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_PLUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_DIV] = { NULL, parse_binary, PREC_MULTIPLICATIVE },
|
||||
|
||||
@@ -436,7 +436,6 @@ static inline Ast *parse_return_stmt(Context *context)
|
||||
{
|
||||
advance_and_verify(context, TOKEN_RETURN);
|
||||
Ast *ast = AST_NEW_TOKEN(AST_RETURN_STMT, context->tok);
|
||||
ast->exit = EXIT_RETURN;
|
||||
ast->return_stmt.defer = NULL;
|
||||
if (try_consume(context, TOKEN_EOS))
|
||||
{
|
||||
|
||||
@@ -312,13 +312,15 @@ Path *parse_path_prefix(Context *context)
|
||||
*/
|
||||
static inline TypeInfo *parse_base_type(Context *context)
|
||||
{
|
||||
SourceRange range = context->tok.span;
|
||||
Path *path = parse_path_prefix(context);
|
||||
if (path)
|
||||
{
|
||||
TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER);
|
||||
TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER, range);
|
||||
type_info->unresolved.path = path;
|
||||
type_info->unresolved.name_loc = context->tok;
|
||||
if (!consume_type_name(context, "types")) return &poisoned_type_info;
|
||||
RANGE_EXTEND_PREV(type_info);
|
||||
return type_info;
|
||||
}
|
||||
|
||||
@@ -327,16 +329,17 @@ static inline TypeInfo *parse_base_type(Context *context)
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_TYPE_IDENT:
|
||||
type_info = type_info_new(TYPE_INFO_IDENTIFIER);
|
||||
type_info = type_info_new(TYPE_INFO_IDENTIFIER, context->tok.span);
|
||||
type_info->unresolved.name_loc = context->tok;
|
||||
break;
|
||||
case TOKEN_TYPE:
|
||||
type_info = type_info_new(TYPE_INFO_IDENTIFIER);
|
||||
type_info = type_info_new(TYPE_INFO_IDENTIFIER, context->tok.span);
|
||||
advance_and_verify(context, TOKEN_TYPE);
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_type_info);
|
||||
type_info->resolve_status = RESOLVE_NOT_DONE;
|
||||
type_info->unresolved_type_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_type_info);
|
||||
EXPECT_OR(TOKEN_RPAREN, &poisoned_type_info);
|
||||
RANGE_EXTEND_PREV(type_info);
|
||||
break;
|
||||
case TOKEN_VOID:
|
||||
type_found = type_void;
|
||||
@@ -408,14 +411,15 @@ static inline TypeInfo *parse_base_type(Context *context)
|
||||
SEMA_TOKEN_ERROR(context->tok, "A type name was expected here.");
|
||||
return &poisoned_type_info;
|
||||
}
|
||||
advance(context);
|
||||
if (type_found)
|
||||
{
|
||||
assert(!type_info);
|
||||
type_info = type_info_new(TYPE_INFO_IDENTIFIER);
|
||||
type_info = type_info_new(TYPE_INFO_IDENTIFIER, context->tok.span);
|
||||
type_info->resolve_status = RESOLVE_DONE;
|
||||
type_info->type = type_found;
|
||||
}
|
||||
advance(context);
|
||||
RANGE_EXTEND_PREV(type_info);
|
||||
return type_info;
|
||||
}
|
||||
|
||||
@@ -437,21 +441,24 @@ static inline TypeInfo *parse_array_type_index(Context *context, TypeInfo *type)
|
||||
if (try_consume(context, TOKEN_PLUS))
|
||||
{
|
||||
CONSUME_OR(TOKEN_RBRACKET, &poisoned_type_info);
|
||||
TypeInfo *incr_array = type_info_new(TYPE_INFO_INC_ARRAY);
|
||||
TypeInfo *incr_array = type_info_new(TYPE_INFO_INC_ARRAY, type->span);
|
||||
incr_array->array.base = type;
|
||||
RANGE_EXTEND_PREV(incr_array);
|
||||
return incr_array;
|
||||
}
|
||||
if (try_consume(context, TOKEN_RBRACKET))
|
||||
{
|
||||
TypeInfo *array = type_info_new(TYPE_INFO_ARRAY);
|
||||
TypeInfo *array = type_info_new(TYPE_INFO_ARRAY, type->span);
|
||||
array->array.base = type;
|
||||
array->array.len = NULL;
|
||||
RANGE_EXTEND_PREV(array);
|
||||
return array;
|
||||
}
|
||||
TypeInfo *array = type_info_new(TYPE_INFO_ARRAY);
|
||||
TypeInfo *array = type_info_new(TYPE_INFO_ARRAY, type->span);
|
||||
array->array.base = type;
|
||||
array->array.len = TRY_EXPR_OR(parse_expr(context), &poisoned_type_info);
|
||||
CONSUME_OR(TOKEN_RBRACKET, &poisoned_type_info);
|
||||
RANGE_EXTEND_PREV(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
@@ -478,10 +485,11 @@ TypeInfo *parse_type_expression(Context *context)
|
||||
case TOKEN_STAR:
|
||||
advance(context);
|
||||
{
|
||||
TypeInfo *ptr_type = type_info_new(TYPE_INFO_POINTER);
|
||||
TypeInfo *ptr_type = type_info_new(TYPE_INFO_POINTER, type_info->span);
|
||||
assert(type_info);
|
||||
ptr_type->pointer = type_info;
|
||||
type_info = ptr_type;
|
||||
RANGE_EXTEND_PREV(type_info);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -725,7 +733,7 @@ bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr)
|
||||
CONSUME_OR(TOKEN_RPAREN, false);
|
||||
if (inner_expr)
|
||||
{
|
||||
*type_ptr = type_info_new(TYPE_INFO_EXPRESSION);
|
||||
*type_ptr = type_info_new(TYPE_INFO_EXPRESSION, inner_expr->span);
|
||||
(**type_ptr).unresolved_type_expr = inner_expr;
|
||||
return true;
|
||||
}
|
||||
@@ -863,7 +871,7 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili
|
||||
{
|
||||
TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), false);
|
||||
Decl *param = decl_new_var(context->tok, type, VARDECL_PARAM, parent_visibility);
|
||||
|
||||
param->span = type->span;
|
||||
if (!try_consume(context, TOKEN_IDENT))
|
||||
{
|
||||
param->name = NULL;
|
||||
@@ -873,7 +881,12 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili
|
||||
|
||||
if (!name && !type_only)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "The function parameter must be named.");
|
||||
if (context->tok.type != TOKEN_COMMA && context->tok.type != TOKEN_RPAREN)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Unexpected end of the parameter list, did you forget an ')'?");
|
||||
return false;
|
||||
}
|
||||
SEMA_ERROR(type, "The function parameter must be named.");
|
||||
return false;
|
||||
}
|
||||
if (name && try_consume(context, TOKEN_EQ))
|
||||
@@ -882,6 +895,7 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili
|
||||
}
|
||||
|
||||
*parameters = VECADD(*parameters, param);
|
||||
RANGE_EXTEND_PREV(param);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1365,7 +1379,6 @@ static inline bool parse_enum_spec(Context *context, TypeInfo **type_ref, Decl**
|
||||
{
|
||||
*type_ref = TRY_TYPE_OR(parse_base_type(context), false);
|
||||
if (!try_consume(context, TOKEN_LPAREN)) return true;
|
||||
CONSUME_OR(TOKEN_LPAREN, false);
|
||||
while (!try_consume(context, TOKEN_RPAREN))
|
||||
{
|
||||
if (!parse_param_decl(context, parent_visibility, parameters_ref, false)) return false;
|
||||
@@ -1415,11 +1428,10 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili
|
||||
|
||||
CONSUME_OR(TOKEN_LBRACE, false);
|
||||
|
||||
decl->enums.type_info = type ? type : type_info_new_base(type_int);
|
||||
decl->enums.type_info = type ? type : type_info_new_base(type_int, decl->span);
|
||||
while (!try_consume(context, TOKEN_RBRACE))
|
||||
{
|
||||
Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, context->tok, decl->visibility);
|
||||
enum_const->enum_constant.parent = decl;
|
||||
VECEACH(decl->enums.values, i)
|
||||
{
|
||||
Decl *other_constant = decl->enums.values[i];
|
||||
@@ -1491,12 +1503,14 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit
|
||||
Decl *func = decl_new(DECL_FUNC, context->tok, visibility);
|
||||
func->func.function_signature.rtype = return_type;
|
||||
|
||||
SourceRange start = context->tok.span;
|
||||
Path *path = parse_path_prefix(context);
|
||||
if (path || context->tok.type == TOKEN_TYPE_IDENT)
|
||||
{
|
||||
// Special case, actually an extension
|
||||
TRY_EXPECT_OR(TOKEN_TYPE_IDENT, "A type was expected after '::'.", &poisoned_decl);
|
||||
TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER);
|
||||
// TODO span is incorrect
|
||||
TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER, start);
|
||||
type->unresolved.path = path;
|
||||
type->unresolved.name_loc = context->tok;
|
||||
func->func.type_parent = type;
|
||||
@@ -1963,15 +1977,17 @@ static Expr *parse_type_access(Context *context, TypeInfo *type)
|
||||
*/
|
||||
Expr *parse_type_identifier_with_path(Context *context, Path *path)
|
||||
{
|
||||
TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER);
|
||||
TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER, path ? path->span : context->tok.span );
|
||||
type->unresolved.path = path;
|
||||
type->unresolved.name_loc = context->tok;
|
||||
advance_and_verify(context, TOKEN_TYPE_IDENT);
|
||||
RANGE_EXTEND_PREV(type);
|
||||
if (context->tok.type == TOKEN_LBRACE)
|
||||
{
|
||||
Expr *expr = EXPR_NEW_TOKEN(EXPR_STRUCT_VALUE, context->tok);
|
||||
expr->struct_value_expr.type = type;
|
||||
expr->struct_value_expr.init_expr = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr);
|
||||
|
||||
return expr;
|
||||
}
|
||||
EXPECT_OR(TOKEN_DOT, &poisoned_expr);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "sema_internal.h"
|
||||
#include "bigint.h"
|
||||
|
||||
|
||||
static inline bool sema_analyse_error(Context *context __unused, Decl *decl)
|
||||
@@ -241,41 +242,81 @@ static inline bool sema_analyse_typedef(Context *context, Decl *decl)
|
||||
|
||||
static inline bool sema_analyse_enum(Context *context, Decl *decl)
|
||||
{
|
||||
// Resolve the type of the enum.
|
||||
if (!sema_resolve_type_info(context, decl->enums.type_info)) return false;
|
||||
uint64_t value = 0;
|
||||
|
||||
Type *type = decl->enums.type_info->type;
|
||||
Type *canonical = decl->enums.type_info->type;
|
||||
|
||||
// Require an integer type
|
||||
if (!type_is_integer(canonical))
|
||||
{
|
||||
SEMA_ERROR(decl->enums.type_info, "The enum type must be an integer type not '%s'.", type_to_error_string(type));
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_LOG("* Enum type resolved to %s.", type->name);
|
||||
bool success = true;
|
||||
VECEACH(decl->enums.values, i)
|
||||
unsigned enums = vec_size(decl->enums.values);
|
||||
BigInt value;
|
||||
BigInt add;
|
||||
bigint_init_unsigned(&add, 1);
|
||||
bigint_init_unsigned(&value, 0);
|
||||
|
||||
for (unsigned i = 0; i < enums; i++)
|
||||
{
|
||||
Decl *enum_value = decl->enums.values[i];
|
||||
enum_value->type = decl->type;
|
||||
DEBUG_LOG("* Checking enum constant %s.", enum_value->name);
|
||||
enum_value->enum_constant.ordinal = i;
|
||||
DEBUG_LOG("* Ordinal: %d", i);
|
||||
assert(enum_value->resolve_status == RESOLVE_NOT_DONE);
|
||||
assert(enum_value->decl_kind == DECL_ENUM_CONSTANT);
|
||||
|
||||
// Start evaluating the constant
|
||||
enum_value->resolve_status = RESOLVE_RUNNING;
|
||||
Expr *expr = enum_value->enum_constant.expr;
|
||||
|
||||
// Create a "fake" expression.
|
||||
// This will be evaluated later to catch the case
|
||||
if (!expr)
|
||||
{
|
||||
expr = expr_new(EXPR_CONST, INVALID_RANGE);
|
||||
expr = expr_new(EXPR_CONST, enum_value->name_span);
|
||||
expr->type = type;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
expr->const_expr.type = CONST_INT;
|
||||
expr->const_expr.i = value;
|
||||
expr->resolve_status = RESOLVE_NOT_DONE;
|
||||
bigint_init_bigint(&expr->const_expr.i, &value);
|
||||
expr->const_expr.kind = TYPE_IXX;
|
||||
expr->type = type_compint;
|
||||
enum_value->enum_constant.expr = expr;
|
||||
}
|
||||
|
||||
// We try to convert to the desired type.
|
||||
if (!sema_analyse_expr(context, type, expr))
|
||||
{
|
||||
success = false;
|
||||
enum_value->resolve_status = RESOLVE_DONE;
|
||||
decl_poison(enum_value);
|
||||
// Reset!
|
||||
bigint_init_unsigned(&value, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(type_is_integer(expr->type->canonical));
|
||||
|
||||
// Here we might have a non-constant value,
|
||||
if (expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(expr, "Expected a constant expression for enum");
|
||||
SEMA_ERROR(expr, "Expected a constant expression for enum.");
|
||||
decl_poison(enum_value);
|
||||
success = false;
|
||||
// Skip one value.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the value
|
||||
bigint_add(&value, &expr->const_expr.i, &add);
|
||||
DEBUG_LOG("* Value: %s", expr_const_to_error_string(&expr->const_expr));
|
||||
enum_value->resolve_status = RESOLVE_DONE;
|
||||
enum_value->type = decl->type;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -393,7 +434,7 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
{
|
||||
if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl);
|
||||
|
||||
DEBUG_LOG("Analyse %s", decl->name);
|
||||
DEBUG_LOG(">>> Analysing %s.", decl->name);
|
||||
if (decl->resolve_status == RESOLVE_RUNNING)
|
||||
{
|
||||
SEMA_ERROR(decl, "Recursive dependency on %s.", decl->name);
|
||||
@@ -454,5 +495,6 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
UNREACHABLE
|
||||
}
|
||||
decl->resolve_status = RESOLVE_DONE;
|
||||
DEBUG_LOG("<<< Analysis of %s successful.", decl->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "sema_internal.h"
|
||||
#include "bigint.h"
|
||||
|
||||
/*
|
||||
* TODOs
|
||||
@@ -14,8 +15,8 @@
|
||||
#define CONST_PROCESS(_op) \
|
||||
if (both_const(left, right)) { \
|
||||
switch (left->const_expr.type) { \
|
||||
case CONST_INT: expr->const_expr.i = left->const_expr.i _op right->const_expr.i; break; \
|
||||
case CONST_FLOAT: expr->const_expr.f = left->const_expr.f _op right->const_expr.f; break; \
|
||||
case CONST_INT: int_##_op(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i); break; \
|
||||
case CONST_FLOAT: float_##_op(&expr->const_expr.f, &left->const_expr.f, &right->const_expr.f); break; \
|
||||
default: UNREACHABLE } \
|
||||
expr->expr_kind = EXPR_CONST; \
|
||||
expr->const_expr.type = left->const_expr.type; \
|
||||
@@ -299,8 +300,7 @@ static inline bool sema_expr_analyse_error_constant(Context *context, Expr *expr
|
||||
assert(error_constant->resolve_status == RESOLVE_DONE);
|
||||
expr->type = decl->type;
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->const_expr.type = CONST_INT;
|
||||
expr->const_expr.i = decl->error_constant.value;
|
||||
expr_const_set_int(&expr->const_expr, decl->error_constant.value, TYPE_U32);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -440,6 +440,7 @@ static Decl *sema_analyse_init_identifier(Context *context, Decl *strukt, Expr *
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return expr->identifier_expr.decl;
|
||||
}
|
||||
|
||||
static Decl *sema_analyse_init_access(Context *context, Decl *strukt, Expr *access_expr)
|
||||
{
|
||||
assert(access_expr->resolve_status == RESOLVE_NOT_DONE);
|
||||
@@ -823,6 +824,10 @@ static bool binary_arithmetic_promotion(Expr *left, Expr *right, Type *left_type
|
||||
return max && type_is_number(max) && cast_implicit(left, max) && cast_implicit(right, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyse a - b
|
||||
* @return true if analysis succeeded
|
||||
*/
|
||||
static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
|
||||
{
|
||||
if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false;
|
||||
@@ -848,7 +853,23 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *
|
||||
|
||||
if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR;
|
||||
|
||||
CONST_PROCESS(-);
|
||||
if (both_const(left, right))
|
||||
{
|
||||
switch (left->const_expr.kind)
|
||||
{
|
||||
case ALL_INTS:
|
||||
bigint_sub(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
break;
|
||||
case ALL_FLOATS:
|
||||
// IMPROVE precision.
|
||||
expr->const_expr.f = left->const_expr.f - right->const_expr.f;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->const_expr.kind = left->const_expr.kind;
|
||||
}
|
||||
|
||||
expr->type = left->type;
|
||||
return true;
|
||||
@@ -885,7 +906,22 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr *
|
||||
|
||||
if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR;
|
||||
|
||||
CONST_PROCESS(+);
|
||||
if (both_const(left, right))
|
||||
{
|
||||
switch (left->const_expr.kind)
|
||||
{
|
||||
case ALL_INTS:
|
||||
bigint_add(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
break;
|
||||
case ALL_FLOATS:
|
||||
expr->const_expr.f = left->const_expr.f + right->const_expr.f;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->const_expr.kind = left->const_expr.kind;
|
||||
}
|
||||
|
||||
expr->type = left->type;
|
||||
return true;
|
||||
@@ -904,10 +940,40 @@ static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr
|
||||
|
||||
if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR;
|
||||
|
||||
CONST_PROCESS(*)
|
||||
|
||||
bool is_mod = expr->binary_expr.operator == BINARYOP_MULT_MOD;
|
||||
expr->type = left->type;
|
||||
return true;
|
||||
|
||||
// Might have changed
|
||||
left_type = left->type->canonical;
|
||||
|
||||
if (is_mod && !type_is_any_integer(left_type))
|
||||
{
|
||||
SEMA_ERROR(expr, "You can only use *% with integer types.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!both_const(left, right)) return true;
|
||||
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->const_expr.kind = left->const_expr.kind;
|
||||
|
||||
switch (left->const_expr.kind)
|
||||
{
|
||||
case ALL_SIGNED_INTS:
|
||||
// Do mod mult
|
||||
if (is_mod && left_type != type_compint)
|
||||
{
|
||||
bigint_mul_wrap(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i, is_mod, left_type->builtin.bitsize);
|
||||
return true;
|
||||
}
|
||||
bigint_mul(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
return true;
|
||||
case ALL_FLOATS:
|
||||
expr->const_expr.f = left->const_expr.f * right->const_expr.f;
|
||||
return true;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
ERR:
|
||||
SEMA_ERROR(expr, "Cannot multiply '%s' and '%s'", type_to_error_string(left_type), type_to_error_string(right_type));
|
||||
@@ -923,19 +989,21 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr *
|
||||
|
||||
if (!binary_arithmetic_promotion(left, right, left_type, right_type)) goto ERR;
|
||||
|
||||
expr->type = left->type;
|
||||
|
||||
// Check null
|
||||
if (right->expr_kind == EXPR_CONST)
|
||||
{
|
||||
switch (right->const_expr.type)
|
||||
switch (right->const_expr.kind)
|
||||
{
|
||||
case CONST_INT:
|
||||
if (right->const_expr.i == 0)
|
||||
case ALL_INTS:
|
||||
if (bigint_cmp_zero(&right->const_expr.i) == CMP_EQ)
|
||||
{
|
||||
SEMA_ERROR(right, "Division by zero not allowed.");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case CONST_FLOAT:
|
||||
case ALL_FLOATS:
|
||||
if (right->const_expr.f == 0)
|
||||
{
|
||||
SEMA_ERROR(right, "Division by zero not allowed.");
|
||||
@@ -947,10 +1015,21 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr *
|
||||
}
|
||||
}
|
||||
|
||||
CONST_PROCESS(/)
|
||||
|
||||
expr->type = left->type;
|
||||
return true;
|
||||
if (!both_const(left, right)) return true;
|
||||
|
||||
switch (left->const_expr.kind)
|
||||
{
|
||||
case ALL_INTS:
|
||||
bigint_div_floor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
return true;
|
||||
case ALL_FLOATS:
|
||||
expr->const_expr.f = left->const_expr.f / right->const_expr.f;
|
||||
return true;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
ERR:
|
||||
SEMA_ERROR(expr, "Cannot divide '%s' by '%s'", type_to_error_string(left_type), type_to_error_string(right_type));
|
||||
@@ -964,7 +1043,7 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr *
|
||||
|
||||
if (!type_is_integer(right->type->canonical) || !type_is_integer(left->type->canonical)) return sema_type_error_on_binop(expr);
|
||||
|
||||
if (right->expr_kind == EXPR_CONST && right->const_expr.i == 0)
|
||||
if (right->expr_kind == EXPR_CONST && bigint_cmp_zero(&right->const_expr.i) == CMP_EQ)
|
||||
{
|
||||
SEMA_ERROR(expr->binary_expr.right, "Cannot perform mod by zero.");
|
||||
return false;
|
||||
@@ -974,7 +1053,7 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr *
|
||||
{
|
||||
// TODO negative
|
||||
expr_replace(expr, left);
|
||||
expr->const_expr.i %= right->const_expr.i;
|
||||
bigint_mod(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -990,7 +1069,8 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr *
|
||||
static bool sema_expr_analyse_mod_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
|
||||
{
|
||||
|
||||
TODO }
|
||||
TODO
|
||||
}
|
||||
|
||||
|
||||
static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
|
||||
@@ -1010,13 +1090,13 @@ static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr *
|
||||
switch (expr->binary_expr.operator)
|
||||
{
|
||||
case TOKEN_AMP:
|
||||
expr->const_expr.i &= right->const_expr.i;
|
||||
bigint_and(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
break;
|
||||
case TOKEN_BIT_XOR:
|
||||
expr->const_expr.i ^= right->const_expr.i;
|
||||
bigint_xor(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
break;
|
||||
case TOKEN_BIT_OR:
|
||||
expr->const_expr.i |= right->const_expr.i;
|
||||
bigint_or(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
@@ -1048,7 +1128,8 @@ static bool sema_expr_analyse_shr(Context *context, Type *to, Expr *expr, Expr *
|
||||
|
||||
if (right->expr_kind == EXPR_CONST)
|
||||
{
|
||||
if (right->const_expr.i > left->type->canonical->builtin.bitsize)
|
||||
TODO
|
||||
//if (int_exceeds(&right->const_expr.i, left->type->canonical->builtin.bitsize))
|
||||
{
|
||||
SEMA_ERROR(right, "Rightshift exceeds bitsize of '%s'", type_to_error_string(left->type));
|
||||
return false;
|
||||
@@ -1056,7 +1137,7 @@ static bool sema_expr_analyse_shr(Context *context, Type *to, Expr *expr, Expr *
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
expr_replace(expr, left);
|
||||
expr->const_expr.i >>= right->const_expr.i;
|
||||
bigint_shr(&expr->const_expr.i, &left->const_expr.i, &right->const_expr.i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1083,7 +1164,8 @@ static bool sema_expr_analyse_shr_assign(Context *context, Type *to, Expr *expr,
|
||||
|
||||
if (right->expr_kind == EXPR_CONST)
|
||||
{
|
||||
if (right->const_expr.i > left->type->canonical->builtin.bitsize)
|
||||
TODO
|
||||
//if (int_exceeds(&right->const_expr.i, left->type->canonical->builtin.bitsize))
|
||||
{
|
||||
SEMA_ERROR(right, "Rightshift exceeds bitsize of '%s'", type_to_error_string(left->type));
|
||||
return false;
|
||||
@@ -1107,9 +1189,11 @@ static bool sema_expr_analyse_shl(Context *context, Type *to, Expr *expr, Expr *
|
||||
// Next, cast to runtime types, this might be needed for runtime constants, e.g. x << 2
|
||||
if (!cast_to_runtime(left) || !cast_to_runtime(right)) return false;
|
||||
|
||||
expr->type = left->type;
|
||||
if (right->expr_kind == EXPR_CONST)
|
||||
{
|
||||
if (right->const_expr.i > left->type->canonical->builtin.bitsize)
|
||||
TODO
|
||||
//if (int_int_exceeds(&right->const_expr.i, left->type->canonical->builtin.bitsize))
|
||||
{
|
||||
SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type));
|
||||
return false;
|
||||
@@ -1117,12 +1201,20 @@ static bool sema_expr_analyse_shl(Context *context, Type *to, Expr *expr, Expr *
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
expr_replace(expr, left);
|
||||
expr->const_expr.i <<= right->const_expr.i;
|
||||
if (left->const_expr.kind == TYPE_IXX)
|
||||
{
|
||||
bigint_shl(&expr->const_expr.i, &expr->const_expr.i, &right->const_expr.i);
|
||||
}
|
||||
else
|
||||
{
|
||||
int bit_count = left->type->canonical->builtin.bitsize;
|
||||
bool is_signed = !type_kind_is_unsigned(left->const_expr.kind);
|
||||
bigint_shl_trunc(&expr->const_expr.i, &expr->const_expr.i, &right->const_expr.i, bit_count, is_signed);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
expr->type = left->type;
|
||||
return true;
|
||||
|
||||
ERR:
|
||||
@@ -1139,6 +1231,8 @@ static bool sema_expr_analyse_shl_assign(Context *context, Type *to, Expr *expr,
|
||||
return false;
|
||||
}
|
||||
|
||||
expr->type = left->type;
|
||||
|
||||
// Check that right side is integer and cast to a runtime type if needed.
|
||||
if (!type_is_integer(right->type->canonical)) return sema_type_error_on_binop(expr);
|
||||
|
||||
@@ -1146,14 +1240,20 @@ static bool sema_expr_analyse_shl_assign(Context *context, Type *to, Expr *expr,
|
||||
|
||||
if (right->expr_kind == EXPR_CONST)
|
||||
{
|
||||
if (right->const_expr.i > left->type->canonical->builtin.bitsize)
|
||||
if (right->const_expr.i.is_negative)
|
||||
{
|
||||
SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type));
|
||||
SEMA_ERROR(right, "Leftshift constant was negative (%s). It must be zero or more.", expr_const_to_error_string(&right->const_expr));
|
||||
return false;
|
||||
}
|
||||
BigInt max;
|
||||
bigint_init_unsigned(&max, left->type->canonical->builtin.bitsize);
|
||||
if (bigint_cmp(&right->const_expr.i, &max) == CMP_GT)
|
||||
{
|
||||
// IMPROVE suppress in macro?
|
||||
SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s', it cannot be higher.", type_to_error_string(left->type));
|
||||
return expr_poison(right);
|
||||
}
|
||||
}
|
||||
|
||||
expr->type = left->type;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1190,69 +1290,76 @@ static bool sema_expr_analyse_and_assign(Context *context, Type *to, Expr *expr,
|
||||
static bool sema_expr_analyse_or_assign(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { TODO }
|
||||
|
||||
|
||||
static void cast_to_max_bit_size(Expr *left, Expr *right, Type *left_type, Type *right_type)
|
||||
{
|
||||
int bit_size_left = left_type->builtin.bitsize;
|
||||
int bit_size_right = right_type->builtin.bitsize;
|
||||
assert(bit_size_left && bit_size_right);
|
||||
if (bit_size_left == bit_size_right) return;
|
||||
if (bit_size_left < bit_size_right)
|
||||
{
|
||||
Type *to = left->type->type_kind < TYPE_U8
|
||||
? type_signed_int_by_bitsize(bit_size_right)
|
||||
: type_unsigned_int_by_bitsize(bit_size_right);
|
||||
bool success = cast_implicit(left, to);
|
||||
assert(success);
|
||||
return;
|
||||
}
|
||||
Type *to = right->type->type_kind < TYPE_U8
|
||||
? type_signed_int_by_bitsize(bit_size_right)
|
||||
: type_unsigned_int_by_bitsize(bit_size_right);
|
||||
bool success = cast_implicit(right, to);
|
||||
assert(success);
|
||||
}
|
||||
|
||||
static bool sema_expr_analyse_comp(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
|
||||
{
|
||||
if (!sema_expr_analyse_binary_sub_expr(context, left, right)) return false;
|
||||
Type *left_type = left->type->canonical;
|
||||
Type *right_type = right->type->canonical;
|
||||
Type *max = type_find_max_type(left_type, right_type);
|
||||
bool success = max && cast_implicit(left, max) && cast_implicit(right, max);
|
||||
if (!success)
|
||||
|
||||
// Handle the case of signed comparisons.
|
||||
if ((type_is_unsigned(left_type) && type_is_signed(right_type))
|
||||
|| (type_is_signed(left_type) && type_is_unsigned(right_type)))
|
||||
{
|
||||
SEMA_ERROR(expr, "Cannot implicitly convert types to evaluate '%s' %s '%s'", type_to_error_string(left_type), token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), type_to_error_string(right_type));
|
||||
return false;
|
||||
cast_to_max_bit_size(left, right, left_type, right_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Alternatively, find and promote to max
|
||||
Type *max = type_find_max_type(left_type, right_type);
|
||||
bool success = max && cast_implicit(left, max) && cast_implicit(right, max);
|
||||
if (!success)
|
||||
{
|
||||
SEMA_ERROR(expr, "Cannot implicitly convert types to evaluate '%s' %s '%s'", type_to_error_string(left_type), token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), type_to_error_string(right_type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (both_const(left, right))
|
||||
{
|
||||
#define COMP(_op_) \
|
||||
switch (left->const_expr.type) { \
|
||||
case CONST_FLOAT: expr->const_expr.b = left->const_expr.f _op_ right->const_expr.f; break; \
|
||||
case CONST_BOOL: expr->const_expr.b = left->const_expr.b _op_ right->const_expr.b; break; \
|
||||
case CONST_INT: expr->const_expr.b = left->const_expr.i _op_ right->const_expr.i; break; \
|
||||
default: UNREACHABLE } break;
|
||||
switch (expr->binary_expr.operator)
|
||||
switch (left->const_expr.kind)
|
||||
{
|
||||
case BINARYOP_GT:
|
||||
COMP(>)
|
||||
case BINARYOP_GE:
|
||||
COMP(>=)
|
||||
case BINARYOP_LT:
|
||||
COMP(<)
|
||||
case BINARYOP_LE:
|
||||
COMP(<=)
|
||||
case BINARYOP_EQ:
|
||||
// TODO elsewhere
|
||||
COMP(==)
|
||||
case BINARYOP_NE:
|
||||
// TODO elsewhere
|
||||
COMP(!=)
|
||||
case TYPE_BOOL:
|
||||
SEMA_ERROR(expr, "Cannot compare booleans, convert them into integers first.");
|
||||
return false;
|
||||
case ALL_FLOATS:
|
||||
case ALL_INTS:
|
||||
expr->const_expr.b = expr_const_compare(&left->const_expr, &right->const_expr, expr->binary_expr.operator);
|
||||
return true;
|
||||
case TYPE_STRING:
|
||||
SEMA_ERROR(expr, "Cannot compare strings.");
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
#undef COMP
|
||||
expr->const_expr.type = CONST_BOOL;
|
||||
expr->const_expr.kind = TYPE_BOOL;
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
}
|
||||
expr->type = type_bool;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define SEMA_ANALYSE_CMP(op) { \
|
||||
if (!cast_arithmetic(left, right, #op)) return false;\
|
||||
if (!cast_arithmetic(right, left, #op)) return false;\
|
||||
if (both_const(left, right)) { \
|
||||
switch (left->const_expr.type) { \
|
||||
case CONST_FLOAT: expr->const_expr.b = left->const_expr.f op right->const_expr.f; break; \
|
||||
case CONST_BOOL: expr->const_expr.b = left->const_expr.b op right->const_expr.b; break; \
|
||||
case CONST_INT: expr->const_expr.b = left->const_expr.i op right->const_expr.i; break; \
|
||||
default: UNREACHABLE }\
|
||||
expr->const_expr.type = CONST_BOOL;\
|
||||
expr->expr_kind = EXPR_CONST;\
|
||||
}\
|
||||
if (!cast_to_runtime(left) || !cast_to_runtime(right)) return false;\
|
||||
expr->type = type_bool;\
|
||||
return true; }
|
||||
|
||||
|
||||
static bool sema_expr_analyse_elvis(Context *context, Type *to, Expr *expr, Expr *left, Expr *right) { TODO }
|
||||
|
||||
@@ -1298,41 +1405,74 @@ static bool sema_expr_analyse_neg(Context *context, Type *to, Expr *expr, Expr *
|
||||
expr->type = inner->type;
|
||||
return true;
|
||||
}
|
||||
// TODO UXX CAP
|
||||
bool is_negmod = expr->unary_expr.operator == UNARYOP_NEGMOD;
|
||||
expr_replace(expr, inner);
|
||||
switch (expr->const_expr.type)
|
||||
switch (expr->const_expr.kind)
|
||||
{
|
||||
case CONST_INT:
|
||||
expr->const_expr.i = ~expr->const_expr.i;
|
||||
break;
|
||||
case CONST_FLOAT:
|
||||
expr->const_expr.f = -expr->const_expr.i;
|
||||
case ALL_INTS:
|
||||
if (is_negmod)
|
||||
{
|
||||
if (inner->expr_kind != TYPE_IXX)
|
||||
{
|
||||
SEMA_ERROR(expr, "Cannot use –% on compile time integers, you need to first cast it to an integer type e.g. -%cast(-128, char).");
|
||||
|
||||
// Continue parsing, pretending this is a -
|
||||
bigint_negate(&expr->const_expr.i, &inner->const_expr.i);
|
||||
return true;
|
||||
}
|
||||
bigint_negate_wrap(&expr->const_expr.i,
|
||||
&inner->const_expr.i,
|
||||
inner->type->canonical->builtin.bitsize);
|
||||
return true;
|
||||
}
|
||||
bigint_negate(&expr->const_expr.i, &inner->const_expr.i);
|
||||
if (expr_const_int_overflowed(&expr->const_expr))
|
||||
{
|
||||
SEMA_ERROR(expr, "Negating %s overflows '%s'.", expr_const_to_error_string(&expr->const_expr), type_to_error_string(expr->type));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case ALL_FLOATS:
|
||||
expr->const_expr.f = -expr->const_expr.f;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyse ~x and ~123
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Expr *inner)
|
||||
{
|
||||
Type *canonical = inner->type->canonical;
|
||||
if (!type_is_integer(canonical) && canonical != type_bool)
|
||||
if (!type_is_any_integer(canonical) && canonical != type_bool)
|
||||
{
|
||||
SEMA_ERROR(expr, "Cannot bit negate %s.", type_to_error_string(inner->type));
|
||||
SEMA_ERROR(expr, "Cannot bit negate '%s'.", type_to_error_string(inner->type));
|
||||
return false;
|
||||
}
|
||||
|
||||
// The simple case, non-const.
|
||||
if (inner->expr_kind != EXPR_CONST)
|
||||
{
|
||||
expr->type = inner->type;
|
||||
return true;
|
||||
}
|
||||
|
||||
expr_replace(expr, inner);
|
||||
// TODO UXX CAP
|
||||
switch (expr->const_expr.type)
|
||||
switch (expr->const_expr.kind)
|
||||
{
|
||||
case CONST_INT:
|
||||
expr->const_expr.i = ~expr->const_expr.i;
|
||||
case ALL_SIGNED_INTS:
|
||||
case ALL_UNSIGNED_INTS:
|
||||
bigint_negate_wrap(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize);
|
||||
break;
|
||||
case CONST_BOOL:
|
||||
case TYPE_IXX:
|
||||
bigint_negate(&expr->const_expr.i, &inner->const_expr.i);
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
expr->const_expr.b = !expr->const_expr.b;
|
||||
break;
|
||||
default:
|
||||
@@ -1340,30 +1480,30 @@ static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Ex
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr *inner)
|
||||
{
|
||||
expr->type = type_bool;
|
||||
if (inner->expr_kind == EXPR_CONST)
|
||||
{
|
||||
switch (expr->const_expr.type)
|
||||
switch (expr->const_expr.kind)
|
||||
{
|
||||
case CONST_NIL:
|
||||
expr->const_expr.b = true;
|
||||
case ALL_INTS:
|
||||
expr->const_expr.b = bigint_cmp_zero(&inner->const_expr.i) == CMP_EQ;
|
||||
break;
|
||||
case CONST_BOOL:
|
||||
case TYPE_BOOL:
|
||||
expr->const_expr.b = !inner->const_expr.b;
|
||||
break;
|
||||
case CONST_INT:
|
||||
expr->const_expr.b = inner->const_expr.i == 0;
|
||||
case ALL_FLOATS:
|
||||
expr->const_expr.b = inner->const_expr.f == 0.0;
|
||||
break;
|
||||
case CONST_FLOAT:
|
||||
expr->const_expr.b = inner->const_expr.f == 0;
|
||||
break;
|
||||
case CONST_STRING:
|
||||
case TYPE_STRING:
|
||||
expr->const_expr.b = !inner->const_expr.string.len;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
expr->const_expr.type = CONST_BOOL;
|
||||
expr->const_expr.kind = TYPE_BOOL;
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
return true;
|
||||
}
|
||||
@@ -1372,7 +1512,6 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr *
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_IXX:
|
||||
case TYPE_UXX:
|
||||
case TYPE_FXX:
|
||||
case TYPE_TYPEDEF:
|
||||
case TYPE_ERROR_UNION:
|
||||
@@ -1411,12 +1550,12 @@ static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *ex
|
||||
{
|
||||
if (!expr_is_ltype(inner))
|
||||
{
|
||||
SEMA_ERROR(inner, "Expression cannot be assigned to");
|
||||
SEMA_ERROR(inner, "Expression cannot be assigned to.");
|
||||
return false;
|
||||
}
|
||||
if (!type_is_number(inner->type->canonical) && inner->type->canonical->type_kind != TYPE_POINTER)
|
||||
{
|
||||
SEMA_ERROR(inner, "Expression must be a number or a pointer");
|
||||
SEMA_ERROR(inner, "Expression must be a number or a pointer.");
|
||||
return false;
|
||||
}
|
||||
expr->type = inner->type;
|
||||
@@ -1436,7 +1575,6 @@ static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *ex
|
||||
return sema_expr_analyse_assign(context, to, expr, left, right);
|
||||
case BINARYOP_MULT:
|
||||
case BINARYOP_MULT_MOD:
|
||||
// Todo treat mod differently
|
||||
return sema_expr_analyse_mult(context, to, expr, left, right);
|
||||
case BINARYOP_MULT_ASSIGN:
|
||||
case BINARYOP_MULT_MOD_ASSIGN:
|
||||
@@ -1518,6 +1656,7 @@ static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *exp
|
||||
case UNARYOP_ADDR:
|
||||
return sema_expr_analyse_addr(context, to, expr, inner);
|
||||
case UNARYOP_NEG:
|
||||
case UNARYOP_NEGMOD:
|
||||
return sema_expr_analyse_neg(context, to, expr, inner);
|
||||
case UNARYOP_BITNEG:
|
||||
return sema_expr_analyse_bit_not(context, to, expr, inner);
|
||||
@@ -1923,7 +2062,7 @@ static inline bool sema_expr_analyse_macro_call2(Context *context, Type *to, Exp
|
||||
|
||||
TODO
|
||||
return success;
|
||||
};
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_macro_expr(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "sema_internal.h"
|
||||
#include "bigint.h"
|
||||
|
||||
#pragma mark --- Context help functions
|
||||
|
||||
@@ -48,10 +49,7 @@ static inline void context_pop_defers_to(Context *context, DeferList *list)
|
||||
|
||||
static inline void context_add_exit(Context *context, ExitType exit)
|
||||
{
|
||||
if (context->current_scope->exit < exit)
|
||||
{
|
||||
context->current_scope->exit = exit;
|
||||
}
|
||||
if (!context->current_scope->exit) context->current_scope->exit = exit;
|
||||
}
|
||||
|
||||
void context_pop_scope(Context *context)
|
||||
@@ -61,7 +59,7 @@ void context_pop_scope(Context *context)
|
||||
ExitType exit_type = context->current_scope->exit;
|
||||
assert (context->current_scope->defers.end == context->current_scope->defers.start);
|
||||
context->current_scope--;
|
||||
if (context->current_scope->exit < exit_type)
|
||||
if (!context->current_scope->exit && exit_type)
|
||||
{
|
||||
context->current_scope->exit = exit_type;
|
||||
}
|
||||
@@ -125,7 +123,6 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement)
|
||||
}
|
||||
|
||||
UPDATE_EXIT(EXIT_RETURN);
|
||||
context->current_scope->exit = EXIT_RETURN;
|
||||
|
||||
Type *expected_rtype = context->rtype;
|
||||
Expr *return_expr = statement->return_stmt.expr;
|
||||
@@ -145,6 +142,11 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (expected_rtype == type_void)
|
||||
{
|
||||
SEMA_ERROR(statement, "You can't return a value from a void function, you need to add a return type.");
|
||||
return false;
|
||||
}
|
||||
if (!sema_analyse_expr(context, expected_rtype, return_expr)) return false;
|
||||
if (!expected_rtype)
|
||||
{
|
||||
@@ -171,14 +173,6 @@ static inline bool sema_analyse_var_decl(Context *context, Decl *decl)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline Ast *convert_expr_to_ast(Expr *expr)
|
||||
{
|
||||
Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span);
|
||||
ast->expr_stmt = expr;
|
||||
return ast;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline bool sema_analyse_decl_expr_list(Context *context, Ast *stmt)
|
||||
{
|
||||
@@ -305,11 +299,6 @@ static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_default_stmt(Context *context __unused, Ast *statement)
|
||||
{
|
||||
SEMA_ERROR(statement, "Unexpected 'default' outside of switch");
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_for_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
@@ -398,11 +387,17 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
ExitType prev_exit = context->current_scope->exit;
|
||||
success = success && sema_analyse_statement(context, statement->if_stmt.then_body);
|
||||
ExitType if_exit = context->current_scope->exit;
|
||||
ExitType else_exit = prev_exit;
|
||||
if (statement->if_stmt.else_body)
|
||||
{
|
||||
context->current_scope->exit = prev_exit;
|
||||
success = success && sema_analyse_statement(context, statement->if_stmt.else_body);
|
||||
else_exit = context->current_scope->exit;
|
||||
}
|
||||
context->current_scope->exit = else_exit < if_exit ? else_exit : if_exit;
|
||||
context_pop_defers_and_replace_ast(context, statement);
|
||||
context_pop_scope(context);
|
||||
return success;
|
||||
@@ -423,6 +418,7 @@ static inline bool sema_analyse_label(Context *context, Ast *statement)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
context->current_scope->exit = EXIT_NONE;
|
||||
vec_add(context->labels, statement);
|
||||
VECEACH(context->gotos, i)
|
||||
{
|
||||
@@ -456,6 +452,7 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement)
|
||||
SEMA_ERROR(statement, "'break' is not allowed here.");
|
||||
return false;
|
||||
}
|
||||
UPDATE_EXIT(EXIT_BREAK);
|
||||
DynamicScope *scope = context->current_scope;
|
||||
statement->break_stmt.defers.start = scope->defers.start;
|
||||
while (!(scope->flags_created & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise)
|
||||
@@ -473,6 +470,7 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement)
|
||||
SEMA_ERROR(statement, "'next' is not allowed here.");
|
||||
return false;
|
||||
}
|
||||
UPDATE_EXIT(EXIT_NEXT);
|
||||
DynamicScope *scope = context->current_scope;
|
||||
statement->next_stmt.defers.start = scope->defers.start;
|
||||
while (!(scope->flags_created & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise)
|
||||
@@ -483,11 +481,6 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sema_analyse_case_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
SEMA_ERROR(statement, "Unexpected 'case' outside of switch");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sema_analyse_continue_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
@@ -496,6 +489,7 @@ static bool sema_analyse_continue_stmt(Context *context, Ast *statement)
|
||||
SEMA_ERROR(statement, "'continue' is not allowed here.");
|
||||
return false;
|
||||
}
|
||||
UPDATE_EXIT(EXIT_CONTINUE);
|
||||
DynamicScope *scope = context->current_scope;
|
||||
statement->continue_stmt.defers.start = scope->defers.start;
|
||||
while (!(scope->flags_created & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise)
|
||||
@@ -551,24 +545,39 @@ static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement)
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_case_expr(Context *context, Ast *case_stmt)
|
||||
static bool sema_analyse_case_expr(Context *context, Type* to_type, Ast *case_stmt)
|
||||
{
|
||||
Expr *case_expr = case_stmt->case_stmt.expr;
|
||||
// TODO handle enums
|
||||
if (!sema_analyse_expr(context, NULL, case_expr)) return false;
|
||||
// TODO string expr
|
||||
if (!sema_analyse_expr(context, to_type, case_expr)) return false;
|
||||
if (case_expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(case_expr, "This must be a constant expression.");
|
||||
return false;
|
||||
}
|
||||
if (case_expr->const_expr.type != CONST_INT)
|
||||
|
||||
// As a special case, we handle bools, converting them to 0 / 1
|
||||
if (case_expr->const_expr.kind == TYPE_BOOL)
|
||||
{
|
||||
SEMA_ERROR(case_expr, "The 'case' value must be an integer constant.");
|
||||
return false;
|
||||
case_stmt->case_stmt.value_type = CASE_VALUE_UINT;
|
||||
case_stmt->case_stmt.val = case_expr->const_expr.b ? 1 : 0;
|
||||
return true;
|
||||
}
|
||||
assert(case_expr->const_expr.type == CONST_INT);
|
||||
|
||||
// TODO check for int
|
||||
/*
|
||||
if (case_expr->const_expr.kind < TYPE__!= CONST_INT && case_expr->const_expr.type != CONST_BOOL)
|
||||
{
|
||||
SEMA_ERROR(case_expr, "The 'case' value must be a boolean or integer constant.");
|
||||
return false;
|
||||
}*/
|
||||
|
||||
case_stmt->case_stmt.value_type = type_is_signed(case_expr->type->canonical) ? CASE_VALUE_INT : CASE_VALUE_UINT;
|
||||
uint64_t val = case_expr->const_expr.i;
|
||||
assert(case_expr->type->canonical->type_kind != TYPE_IXX);
|
||||
// TODO this is incorrect
|
||||
TODO
|
||||
uint64_t val = (uint64_t)bigint_as_signed(&case_expr->const_expr.i);
|
||||
case_stmt->case_stmt.val = val;
|
||||
return true;
|
||||
}
|
||||
@@ -586,12 +595,6 @@ static inline bool sema_analyse_compound_statement_no_scope(Context *context, As
|
||||
}
|
||||
}
|
||||
context_pop_defers_to(context, &compound_statement->compound_stmt.defer_list);
|
||||
|
||||
/*
|
||||
if (parent->exit < compound_statement->exit)
|
||||
{
|
||||
parent->exit = compound_statement->exit;
|
||||
}*/
|
||||
return all_ok;
|
||||
}
|
||||
|
||||
@@ -610,6 +613,7 @@ static inline Type *ast_cond_type(Ast *ast)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
context_push_scope(context);
|
||||
@@ -617,20 +621,27 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
bool success = sema_analyse_cond(context, cond, false);
|
||||
|
||||
Type *switch_type = ast_cond_type(cond)->canonical;
|
||||
if (!type_is_integer(switch_type))
|
||||
if (switch_type == type_bool || !type_is_integer(switch_type))
|
||||
{
|
||||
SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type));
|
||||
return false;
|
||||
}
|
||||
Ast *default_case = NULL;
|
||||
assert(context->current_scope->defers.start == context->current_scope->defers.end);
|
||||
VECEACH(statement->switch_stmt.cases, i)
|
||||
|
||||
// TODO enum, exhaustive cases.
|
||||
ExitType prev_exit = context->current_scope->exit;
|
||||
bool exhaustive = false;
|
||||
ExitType lowest_exit = EXIT_NONE;
|
||||
unsigned cases = vec_size(statement->switch_stmt.cases);
|
||||
for (unsigned i = 0; i < cases; i++)
|
||||
{
|
||||
context->current_scope->exit = prev_exit;
|
||||
Ast *stmt = statement->switch_stmt.cases[i];
|
||||
switch (stmt->ast_kind)
|
||||
{
|
||||
case AST_CASE_STMT:
|
||||
if (!sema_analyse_case_expr(context, stmt))
|
||||
if (!sema_analyse_case_expr(context, switch_type, stmt))
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
@@ -647,6 +658,7 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
}
|
||||
break;
|
||||
case AST_DEFAULT_STMT:
|
||||
exhaustive = true;
|
||||
if (default_case)
|
||||
{
|
||||
SEMA_ERROR(stmt, "'default' may only appear once in a single 'switch', please remove one.");
|
||||
@@ -658,15 +670,41 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT);
|
||||
if (i == cases - 1)
|
||||
{
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK);
|
||||
}
|
||||
else
|
||||
{
|
||||
context_push_scope_with_flags(context, SCOPE_NEXT | SCOPE_BREAK);
|
||||
}
|
||||
success = success && sema_analyse_compound_statement_no_scope(context, stmt->case_stmt.body);
|
||||
ExitType case_exit = context->current_scope->exit;
|
||||
if (case_exit != lowest_exit)
|
||||
{
|
||||
switch (case_exit)
|
||||
{
|
||||
case EXIT_NONE:
|
||||
case EXIT_BREAK:
|
||||
lowest_exit = EXIT_BREAK;
|
||||
break;
|
||||
case EXIT_NEXT:
|
||||
// We ignore this completely
|
||||
break;
|
||||
default:
|
||||
if (!lowest_exit || lowest_exit > case_exit) lowest_exit = case_exit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
context_pop_scope(context);
|
||||
}
|
||||
context_pop_defers_and_replace_ast(context, statement);
|
||||
if (lowest_exit <= EXIT_BREAK) lowest_exit = prev_exit;
|
||||
context->current_scope->exit = exhaustive ? lowest_exit : EXIT_NONE;
|
||||
context_pop_scope(context);
|
||||
if (!success) return false;
|
||||
// Is this a typeless switch value?
|
||||
if (switch_type->type_kind == TYPE_UXX || switch_type->type_kind == TYPE_IXX)
|
||||
if (switch_type->type_kind == TYPE_IXX)
|
||||
{
|
||||
|
||||
TODO
|
||||
@@ -701,6 +739,7 @@ static bool sema_analyse_try_stmt(Context *context, Ast *statement)
|
||||
static bool sema_analyse_throw_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
Expr *throw_value = statement->throw_stmt.throw_value;
|
||||
UPDATE_EXIT(EXIT_THROW);
|
||||
if (!sema_analyse_expr(context, NULL, throw_value)) return false;
|
||||
Type *type = throw_value->type->canonical;
|
||||
if (type->type_kind != TYPE_ERROR)
|
||||
@@ -713,7 +752,7 @@ static bool sema_analyse_throw_stmt(Context *context, Ast *statement)
|
||||
SEMA_ERROR(statement, "This 'throw' is not handled, please add a 'throws %s' clause to the function signature or use try-catch.", type->name);
|
||||
return false;
|
||||
}
|
||||
VECADD(context->errors, type->decl);
|
||||
vec_add(context->errors, type->decl);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -749,7 +788,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement
|
||||
case AST_BREAK_STMT:
|
||||
return sema_analyse_break_stmt(context, statement);
|
||||
case AST_CASE_STMT:
|
||||
return sema_analyse_case_stmt(context, statement);
|
||||
SEMA_ERROR(statement, "Unexpected 'case' outside of switch");
|
||||
return false;
|
||||
case AST_CATCH_STMT:
|
||||
return sema_analyse_catch_stmt(context, statement);
|
||||
case AST_COMPOUND_STMT:
|
||||
@@ -761,7 +801,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement
|
||||
case AST_DECLARE_STMT:
|
||||
return sema_analyse_declare_stmt(context, statement);
|
||||
case AST_DEFAULT_STMT:
|
||||
return sema_analyse_default_stmt(context, statement);
|
||||
SEMA_ERROR(statement, "Unexpected 'default' outside of switch");
|
||||
return false;
|
||||
case AST_DEFER_STMT:
|
||||
return sema_analyse_defer_stmt(context, statement);
|
||||
case AST_DO_STMT:
|
||||
@@ -855,15 +896,18 @@ bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
func->func.annotations = CALLOCS(*func->func.annotations);
|
||||
context_push_scope(context);
|
||||
Decl **params = func->func.function_signature.params;
|
||||
assert(context->current_scope == &context->scopes[1]);
|
||||
VECEACH(params, i)
|
||||
{
|
||||
if (!sema_add_local(context, params[i])) return false;
|
||||
}
|
||||
if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false;
|
||||
if (context->current_scope->exit != EXIT_RETURN)
|
||||
assert(context->current_scope == &context->scopes[1]);
|
||||
if (context->current_scope->exit != EXIT_RETURN && context->current_scope->exit != EXIT_THROW && context->current_scope->exit != EXIT_GOTO)
|
||||
{
|
||||
if (func->func.function_signature.rtype->type->canonical != type_void)
|
||||
{
|
||||
// IMPROVE better pointer to end.
|
||||
SEMA_ERROR(func, "Missing return statement at the end of the function.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "sema_internal.h"
|
||||
#include "compiler_internal.h"
|
||||
#include "bigint.h"
|
||||
|
||||
|
||||
static inline bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info)
|
||||
@@ -23,6 +25,7 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type)
|
||||
{
|
||||
return type_info_poison(type);
|
||||
}
|
||||
uint64_t len = 0;
|
||||
if (type->array.len)
|
||||
{
|
||||
if (!sema_analyse_expr(context, type_usize, type->array.len)) return type_info_poison(type);
|
||||
@@ -31,9 +34,10 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type)
|
||||
SEMA_ERROR(type->array.len, "Expected a constant value as array size.");
|
||||
return type_info_poison(type);
|
||||
}
|
||||
len = bigint_as_unsigned(&type->array.len->const_expr.i);
|
||||
}
|
||||
assert(!type->array.len || type->array.len->expr_kind == EXPR_CONST);
|
||||
type->type = type_get_array(type->array.base->type, type->array.len ? type->array.len->const_expr.i : 0);
|
||||
type->type = type_get_array(type->array.base->type, len);
|
||||
type->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ const char *type_to_error_string(Type *type)
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
case TYPE_UXX:
|
||||
case TYPE_F32:
|
||||
case TYPE_F64:
|
||||
case TYPE_FXX:
|
||||
@@ -197,7 +196,7 @@ size_t type_size(Type *canonical)
|
||||
case TYPE_F64:
|
||||
return canonical->builtin.bytesize;
|
||||
case TYPE_IXX:
|
||||
case TYPE_UXX:
|
||||
return 4;
|
||||
case TYPE_FXX:
|
||||
return 8;
|
||||
case TYPE_FUNC:
|
||||
@@ -409,8 +408,7 @@ type_create(#_name, &_shortname, &type_ ## _name, _type, _bits, target->align_mi
|
||||
create_type_cache(type_void);
|
||||
type_void->type_cache[0] = &t_voidstar;
|
||||
t_voidstar.pointer = type_void;
|
||||
type_create("compint", &t_ixx, &type_compint, TYPE_IXX, 64, 0, 0);
|
||||
type_create("compuint", &t_uxx, &type_compuint, TYPE_UXX, 64, 0, 0);
|
||||
type_create("compint", &t_ixx, &type_compint, TYPE_IXX, 32, 0, 0);
|
||||
type_create("compfloat", &t_fxx, &type_compfloat, TYPE_FXX, 64, 0, 0);
|
||||
|
||||
type_create_alias("usize", &t_usz, &type_usize, type_unsigned_int_by_bitsize(target->width_pointer));
|
||||
@@ -471,46 +469,39 @@ bool type_may_have_method_functions(Type *type)
|
||||
typedef enum
|
||||
{
|
||||
L,
|
||||
LS,
|
||||
R,
|
||||
RS,
|
||||
FL,
|
||||
X,
|
||||
} MaxType;
|
||||
|
||||
Type *type_find_max_num_type(Type *num_type, Type *other_num)
|
||||
{
|
||||
if (other_num->type_kind < TYPE_BOOL || other_num->type_kind > TYPE_FXX) return NULL;
|
||||
assert(num_type->type_kind >= TYPE_BOOL && num_type->type_kind <= TYPE_FXX);
|
||||
static MaxType max_conv[TYPE_FXX - TYPE_BOOL + 1][TYPE_FXX - TYPE_BOOL + 1] = {
|
||||
//Bool I8 I16 I32 I64 IXX U8 U16 U32 U64 UXX F32 F64 FXX
|
||||
{ L, R, R, R, R, L, R, R, R, R, L, R, R, FL }, // Bool
|
||||
{ L, L, R, R, R, L, L, RS, RS, RS, L, R, R, FL }, // I8
|
||||
{ L, L, L, R, R, L, L, L, RS, RS, L, R, R, FL }, // I16
|
||||
{ L, L, L, L, R, L, L, L, L, RS, L, R, R, FL }, // I32
|
||||
{ L, L, L, L, L, L, L, L, L, L, L, R, R, FL }, // I64
|
||||
{ R, R, R, R, R, L, RS, RS, RS, RS, L, R, R, R }, // IXX
|
||||
{ L, R, R, R, R, LS, L, R, R, R, L, R, R, FL }, // U8
|
||||
{ L, LS, R, R, R, LS, L, L, R, R, L, R, R, FL }, // U16
|
||||
{ L, LS, LS, R, R, L, L, L, L, R, L, R, R, FL }, // U32
|
||||
{ L, LS, LS, LS, R, L, L, L, L, L, L, R, R, FL }, // U64
|
||||
{ R, R, R, R, R, R, R, R, R, R, L, R, R, R }, // UXX
|
||||
{ L, L, L, L, L, L, L, L, L, L, L, L, R, L }, // F32
|
||||
{ L, L, L, L, L, L, L, L, L, L, L, L, L, L }, // F32
|
||||
{ FL, FL, FL, FL, FL, FL, FL, FL, FL, FL, L, R, R, L }, // FXX
|
||||
if (other_num->type_kind < TYPE_I8 || other_num->type_kind > TYPE_FXX) return NULL;
|
||||
assert(num_type->type_kind >= TYPE_I8 && num_type->type_kind <= TYPE_FXX);
|
||||
static MaxType max_conv[TYPE_FXX - TYPE_I8 + 1][TYPE_FXX - TYPE_BOOL + 1] = {
|
||||
// I8 I16 I32 I64 U8 U16 U32 U64 IXX F32 F64 FXX
|
||||
{ L, R, R, R, X, X, X, X, L, R, R, FL }, // I8
|
||||
{ L, L, R, R, L, X, X, X, L, R, R, FL }, // I16
|
||||
{ L, L, L, R, L, L, X, X, L, R, R, FL }, // I32
|
||||
{ L, L, L, L, L, L, L, X, L, R, R, FL }, // I64
|
||||
{ X, R, R, R, L, R, R, R, L, R, R, FL }, // U8
|
||||
{ X, X, R, R, L, L, R, R, L, R, R, FL }, // U16
|
||||
{ X, X, X, R, L, L, L, R, L, R, R, FL }, // U32
|
||||
{ X, X, X, X, L, L, L, L, L, R, R, FL }, // U64
|
||||
{ R, R, R, R, R, R, R, R, L, R, R, R }, // IXX
|
||||
{ L, L, L, L, L, L, L, L, L, L, R, L }, // F32
|
||||
{ L, L, L, L, L, L, L, L, L, L, L, L }, // F64
|
||||
{ FL, FL, FL, FL, FL, FL, FL, FL, FL, R, R, L }, // FXX
|
||||
};
|
||||
MaxType conversion = max_conv[num_type->type_kind - TYPE_BOOL][other_num->type_kind - TYPE_BOOL];
|
||||
MaxType conversion = max_conv[num_type->type_kind - TYPE_I8][other_num->type_kind - TYPE_I8];
|
||||
switch (conversion)
|
||||
{
|
||||
case X:
|
||||
return NULL;
|
||||
case L:
|
||||
assert (num_type->type_kind != TYPE_FXX);
|
||||
return num_type;
|
||||
case R:
|
||||
assert (other_num->type_kind != TYPE_FXX);
|
||||
return other_num;
|
||||
case LS:
|
||||
return type_signed_int_by_bitsize(num_type->builtin.bytesize * 8);
|
||||
case RS:
|
||||
return type_signed_int_by_bitsize(other_num->builtin.bytesize * 8);
|
||||
case FL:
|
||||
return type_double;
|
||||
default:
|
||||
@@ -619,8 +610,8 @@ Type *type_find_max_type(Type *type, Type *other)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_VOID:
|
||||
return NULL;
|
||||
case TYPE_BOOL:
|
||||
return NULL;
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
case TYPE_I32:
|
||||
@@ -630,7 +621,6 @@ Type *type_find_max_type(Type *type, Type *other)
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
case TYPE_UXX:
|
||||
case TYPE_F32:
|
||||
case TYPE_F64:
|
||||
case TYPE_FXX:
|
||||
|
||||
Reference in New Issue
Block a user