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:
Christoffer Lerno
2020-03-20 17:55:24 +01:00
parent f4b4bab947
commit b1aa64cdcc
25 changed files with 1679 additions and 672 deletions

View File

@@ -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:

View File

@@ -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)

View File

@@ -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);

View File

@@ -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:

View File

@@ -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; })

View File

@@ -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,

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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");

View File

@@ -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
View 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;
}

View File

@@ -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 },

View File

@@ -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))
{

View File

@@ -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);

View File

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

View File

@@ -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)
{

View File

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

View File

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

View File

@@ -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: