More work on typechecking and type conversions
This commit is contained in:
@@ -12,6 +12,11 @@ unary_test :: ()
|
||||
// var := -true
|
||||
// var := +true
|
||||
|
||||
binary_test :: ()
|
||||
int_val :: 1000
|
||||
add :: int_val + 10 + 2.242
|
||||
|
||||
|
||||
|
||||
basic_type_assignment :: ()
|
||||
custom_data: Custom_Data
|
||||
|
||||
191
typecheck.cpp
191
typecheck.cpp
@@ -1,7 +1,8 @@
|
||||
#define CASE(kind,type) case AST_##kind: { Ast_##type *node = (Ast_##type *)ast;
|
||||
#define BREAK() } break
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Evaluating constant expressions
|
||||
//-----------------------------------------------------------------------------
|
||||
#define CASE_CONVERT(pos, int_val, kind_val, new_val, TYPE, min, max) \
|
||||
case TYPE:{ \
|
||||
if(int_val > max) parsing_error(pos, "Overflow when converting from %s constant to %s, value out of range: %d, max is: %d", type_names[kind_val], type_names[TYPE], int_val, max); \
|
||||
@@ -10,13 +11,14 @@
|
||||
}break;
|
||||
|
||||
function Value
|
||||
from_untyped_to_typed(Token *pos, Value a, Ast_Resolved_Type *new_type){
|
||||
convert_untyped(Token *pos, Value a, Ast_Resolved_Type *new_type){
|
||||
assert(is_untyped(a.type));
|
||||
assert(new_type);
|
||||
|
||||
if(is_int(a.type) && is_int(new_type)){
|
||||
assert(a.type == untyped_int);
|
||||
switch(new_type->kind){
|
||||
case TYPE_UNTYPED_INT: break;
|
||||
CASE_CONVERT(pos, a.int_val, a.type->kind, a.s64_val, TYPE_INT, TYPE_INT_MIN, TYPE_INT_MAX)
|
||||
CASE_CONVERT(pos, a.int_val, a.type->kind, a.u64_val, TYPE_UINT, TYPE_UINT_MIN, TYPE_INT_MAX)
|
||||
CASE_CONVERT(pos, a.int_val, a.type->kind, a.s8_val , TYPE_S8 , S8MIN, S8MAX)
|
||||
@@ -53,15 +55,112 @@ from_untyped_to_typed(Token *pos, Value a, Ast_Resolved_Type *new_type){
|
||||
return a;
|
||||
}
|
||||
|
||||
function S64
|
||||
value_get_int(Value value){
|
||||
assert(is_float(value.type) || is_int(value.type));
|
||||
S64 result = 0;
|
||||
switch(value.type->kind){
|
||||
case TYPE_UNTYPED_FLOAT: result = (S64)value.f64_val; break;
|
||||
case TYPE_F64: result = (S64)value.f64_val; break;
|
||||
case TYPE_F32: result = (S64)value.f32_val; break;
|
||||
case TYPE_UNTYPED_INT: result = value.int_val; break;
|
||||
case TYPE_INT: result = value.s64_val; break;
|
||||
case TYPE_S64:result = value.s64_val; break;
|
||||
case TYPE_S32:result = value.s32_val; break;
|
||||
case TYPE_S16:result = value.s16_val; break;
|
||||
case TYPE_S8:result = value.s8_val; break;
|
||||
case TYPE_U64:result = value.u64_val; break;// @todo Need big int
|
||||
case TYPE_U32:result = value.u32_val; break;
|
||||
case TYPE_U16:result = value.u16_val; break;
|
||||
case TYPE_U8:result = value.u8_val; break;
|
||||
case TYPE_UINT:result = value.u64_val; break;
|
||||
default: invalid_codepath;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function F64
|
||||
value_get_float(Value value){
|
||||
assert(is_float(value.type) || is_int(value.type));
|
||||
F64 result = value.f64_val;
|
||||
if(is_int(value.type))
|
||||
result = (F64)value_get_int(value);
|
||||
else if(value.type == type_f32)
|
||||
result = value.f32_val;
|
||||
return result;
|
||||
}
|
||||
|
||||
#include <math.h>
|
||||
function Value
|
||||
eval_binary(Token *pos, Token_Kind op, Value a, Value b){
|
||||
if(!(is_numeric(a.type) && is_numeric(b.type))) parsing_error(pos, "Constant application of binary %s on values of type %s is not allowed", token_kind_string(op).str, docname(a.type));
|
||||
|
||||
S64 left_int = value_get_int(a);
|
||||
S64 right_int = value_get_int(b);
|
||||
F64 left_float = value_get_float(a);
|
||||
F64 right_float = value_get_float(b);
|
||||
|
||||
switch(op){
|
||||
case TK_Add: {
|
||||
left_int = left_int + right_int;
|
||||
left_float = left_float + right_float;
|
||||
} break;
|
||||
case TK_Sub: {
|
||||
left_int = left_int - right_int;
|
||||
left_float = left_float - right_float;
|
||||
} break;
|
||||
case TK_Mul: {
|
||||
left_int = left_int * right_int;
|
||||
left_float = left_float * right_float;
|
||||
} break;
|
||||
case TK_Div: {
|
||||
left_int = left_int / right_int;
|
||||
left_float = left_float / right_float;
|
||||
} break;
|
||||
case TK_Mod: {
|
||||
left_int = left_int % right_int;
|
||||
left_float = fmod(left_float, right_float);
|
||||
} break;
|
||||
invalid_default_case;
|
||||
}
|
||||
|
||||
Ast_Resolved_Type *final_type = 0;
|
||||
Value before_conversion;
|
||||
if(is_int(a.type) && is_int(b.type)) {
|
||||
before_conversion.type = untyped_int;
|
||||
before_conversion.int_val = left_int;
|
||||
final_type = untyped_int;
|
||||
}
|
||||
else{
|
||||
before_conversion.type = untyped_float;
|
||||
before_conversion.f64_val = left_float;
|
||||
final_type = untyped_float;
|
||||
}
|
||||
|
||||
if(is_typed(a.type) && is_typed(b.type)){
|
||||
if(a.type != b.type) parsing_error(pos, "Type mismatch in binary operation %s - left: %s right: %s", token_kind_string(op).str, docname(a.type), docname(b.type));
|
||||
else final_type = a.type;
|
||||
}
|
||||
|
||||
else if(is_typed(a.type) || is_typed(b.type)){
|
||||
not_implemented;
|
||||
}
|
||||
|
||||
Value result = convert_untyped(pos, before_conversion, final_type);
|
||||
return result;
|
||||
}
|
||||
|
||||
function Value
|
||||
eval_unary(Token *pos, Token_Kind op, Value v){
|
||||
switch(op){
|
||||
case TK_Sub:{
|
||||
switch(v.type->kind){
|
||||
// @todo: Bounds checking for conversion from negative to positive
|
||||
// it's, positive is 2147483647 negative is -2147483648
|
||||
case TYPE_UNTYPED_INT: v.int_val = -v.int_val;break;
|
||||
case TYPE_UNTYPED_FLOAT: v.f64_val = -v.f64_val;break;
|
||||
case TYPE_INT: v.int_val = -v.int_val;break;
|
||||
case TYPE_S64: v.s64_val = -v.s64_val;break; // @todo Check bounds
|
||||
case TYPE_INT: v.s64_val = -v.int_val;break;
|
||||
case TYPE_S64: v.s64_val = -v.s64_val;break;
|
||||
case TYPE_S32: v.s32_val = -v.s32_val;break;
|
||||
case TYPE_S16: v.s16_val = -v.s16_val;break;
|
||||
case TYPE_S8: v.s8_val = -v.s8_val; break;
|
||||
@@ -79,11 +178,11 @@ eval_unary(Token *pos, Token_Kind op, Value v){
|
||||
switch(v.type->kind){
|
||||
case TYPE_UNTYPED_INT: v.int_val = +v.int_val;break;
|
||||
case TYPE_UNTYPED_FLOAT: v.f64_val = +v.f64_val;break;
|
||||
case TYPE_INT: v.int_val = +v.int_val;break;
|
||||
case TYPE_S64: v.int_val = +v.int_val;break; // @todo Check bounds
|
||||
case TYPE_S32: v.int_val = +v.int_val;break;
|
||||
case TYPE_S16: v.int_val = +v.int_val;break;
|
||||
case TYPE_S8: v.int_val = +v.int_val;break;
|
||||
case TYPE_INT: v.s64_val = +v.int_val;break;
|
||||
case TYPE_S64: v.s64_val = +v.int_val;break;
|
||||
case TYPE_S32: v.s32_val = +v.int_val;break;
|
||||
case TYPE_S16: v.s16_val = +v.int_val;break;
|
||||
case TYPE_S8: v.s8_val = +v.int_val;break;
|
||||
case TYPE_F32: v.f32_val = +v.f32_val;break;
|
||||
case TYPE_F64: v.f64_val = +v.f64_val;break;
|
||||
case TYPE_U64: v.u64_val = +v.u64_val;break;
|
||||
@@ -100,6 +199,21 @@ eval_unary(Token *pos, Token_Kind op, Value v){
|
||||
return v;
|
||||
}
|
||||
|
||||
#define rewrite_into_const(ast,T,s) _rewrite_into_const(ast,sizeof(T),s)
|
||||
function void
|
||||
_rewrite_into_const(Ast *node, U64 ast_size, Value value){
|
||||
auto ast = (Ast_Atom *)node;
|
||||
assert(ast_size >= sizeof(Ast_Atom));
|
||||
ast->flags = set_flag(ast->flags, AST_ATOM);
|
||||
ast->kind = AST_VALUE;
|
||||
ast->value = value;
|
||||
}
|
||||
|
||||
function void
|
||||
_rewrite_into_const(Ast *node, U64 ast_size, Sym *sym){
|
||||
_rewrite_into_const(node, ast_size, sym->value);
|
||||
}
|
||||
|
||||
function Ast_Resolved_Type *
|
||||
resolve_typespec(Ast_Expr *ast, B32 ast_can_be_null){
|
||||
if(ast_can_be_null && ast == 0) return 0;
|
||||
@@ -207,21 +321,6 @@ require_const_int(Ast_Expr *expr, B32 ast_can_be_null){
|
||||
return op;
|
||||
}
|
||||
|
||||
#define rewrite_into_const(ast,T,s) _rewrite_into_const(ast,sizeof(T),s)
|
||||
function void
|
||||
_rewrite_into_const(Ast *node, U64 ast_size, Value value){
|
||||
auto ast = (Ast_Atom *)node;
|
||||
assert(ast_size >= sizeof(Ast_Atom));
|
||||
ast->flags = set_flag(ast->flags, AST_ATOM);
|
||||
ast->kind = AST_VALUE;
|
||||
ast->value = value;
|
||||
}
|
||||
|
||||
function void
|
||||
_rewrite_into_const(Ast *node, U64 ast_size, Sym *sym){
|
||||
_rewrite_into_const(node, ast_size, sym->value);
|
||||
}
|
||||
|
||||
|
||||
function Operand
|
||||
resolve_lambda(Ast_Lambda *lambda, Sym *sym = 0){
|
||||
@@ -275,7 +374,7 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res
|
||||
|
||||
switch(ast->kind){
|
||||
CASE(VALUE, Atom){
|
||||
return operand_rvalue(node->value);
|
||||
return operand_const_rvalue(node->value);
|
||||
BREAK();
|
||||
}
|
||||
CASE(IDENT, Atom){
|
||||
@@ -480,16 +579,18 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res
|
||||
if(!is_numeric(op.type)) parsing_error(node->pos, "Unary [-] cant be applied to value of type %s", docname(op.type));
|
||||
if(op.is_const){
|
||||
rewrite_into_const(node, Ast_Unary, eval_unary(node->pos, node->op, op.value));
|
||||
return operand_const_rvalue(op.value);
|
||||
}
|
||||
return op;
|
||||
return operand_rvalue(op.value.type);
|
||||
}break;
|
||||
case TK_Add:{
|
||||
Operand op = resolve_expr(node->expr);
|
||||
if(!is_numeric(op.type)) parsing_error(node->pos, "Unary [+] cant be applied to value of type %s", docname(op.type));
|
||||
if(op.is_const){
|
||||
rewrite_into_const(node, Ast_Unary, eval_unary(node->pos, node->op, op.value));
|
||||
return operand_const_rvalue(op.value);
|
||||
}
|
||||
return op;
|
||||
return operand_rvalue(op.type);
|
||||
}break;
|
||||
invalid_default_case; return {};
|
||||
}
|
||||
@@ -604,8 +705,8 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res
|
||||
else if(token_is_compare(node->op)){
|
||||
Operand left = resolve_expr(node->left);
|
||||
Operand right = resolve_expr(node->right);
|
||||
result = resolve_operand_pair(node->pos, left, right);
|
||||
if(result.is_const){
|
||||
|
||||
if(left.is_const && right.is_const){
|
||||
if(result.type == type_int){
|
||||
switch(node->op){
|
||||
case TK_GreaterThen : result.bool_val = left.int_val > right.int_val; break;
|
||||
@@ -619,6 +720,12 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res
|
||||
}
|
||||
else parsing_error(node->pos, "Arithmetic on type [%s] is not supported", type_names[result.type->kind]);
|
||||
}
|
||||
else if(left.type != right.type) {
|
||||
parsing_error(node->pos, "Type mismatch in binary operation %s - left: %s right: %s", token_kind_string(node->op).str, docname(left.type), docname(right.type));
|
||||
}
|
||||
else{
|
||||
result = operand_rvalue(left.type);
|
||||
}
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -627,18 +734,16 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res
|
||||
else{
|
||||
Operand left = resolve_expr(node->left);
|
||||
Operand right = resolve_expr(node->right);
|
||||
result = resolve_operand_pair(node->pos, left, right);
|
||||
if(result.is_const){
|
||||
if(result.type == type_int){
|
||||
switch(node->op){
|
||||
case TK_Add: result.int_val = left.int_val + right.int_val; break;
|
||||
case TK_Sub: result.int_val = left.int_val - right.int_val; break;
|
||||
case TK_Mul: result.int_val = left.int_val * right.int_val; break;
|
||||
case TK_Div: result.int_val = left.int_val / right.int_val; break;
|
||||
invalid_default_case;
|
||||
if(left.is_const && right.is_const){
|
||||
Value value = eval_binary(node->pos, node->op, left.value, right.value);
|
||||
rewrite_into_const(node, Ast_Binary, value);
|
||||
result = operand_const_rvalue(value);
|
||||
}
|
||||
else if(left.type != right.type){
|
||||
parsing_error(node->pos, "Type mismatch in binary operation %s - left: %s right: %s", token_kind_string(node->op).str, docname(left.type), docname(right.type));
|
||||
}
|
||||
else parsing_error(node->pos, "Arithmetic on type [%s] is not supported", type_names[result.type->kind]);
|
||||
else{
|
||||
result = operand_rvalue(left.type);
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -700,13 +805,13 @@ resolve_binding(Ast *ast, Sym *sym){
|
||||
CASE(VAR, Var){
|
||||
Ast_Resolved_Type *type = resolve_typespec(node->typespec, AST_CAN_BE_NULL);
|
||||
Operand expr = resolve_expr(node->expr, type);
|
||||
assert(expr.type != 0 || type != 0);
|
||||
|
||||
if(!type) expr.type = if_untyped_get_default_conversion(expr.type);
|
||||
else if(!expr.type) expr.type = type;
|
||||
else if(type == expr.type);
|
||||
else if(is_untyped(expr.type)){
|
||||
expr.value = from_untyped_to_typed(node->pos, expr.value, type);
|
||||
}
|
||||
else if(is_untyped(expr.type)) expr.value = convert_untyped(node->pos, expr.value, type);
|
||||
else invalid_codepath;
|
||||
|
||||
type_complete(expr.type);
|
||||
return expr;
|
||||
|
||||
@@ -233,7 +233,7 @@ operand_lambda(Ast_Resolved_Type *type){
|
||||
}
|
||||
|
||||
function Operand
|
||||
operand_rvalue(Value value){
|
||||
operand_const_rvalue(Value value){
|
||||
Operand result = {};
|
||||
result.is_const = true;
|
||||
result.value = value;
|
||||
|
||||
Reference in New Issue
Block a user