number literal rework

master
Andrew Kelley 2016-01-21 03:02:25 -07:00
parent 5e212db29c
commit 32e2196257
11 changed files with 773 additions and 617 deletions

View File

@ -25,6 +25,7 @@ include_directories(
)
set(ZIG_SOURCES
"${CMAKE_SOURCE_DIR}/src/bignum.cpp"
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
"${CMAKE_SOURCE_DIR}/src/parser.cpp"
"${CMAKE_SOURCE_DIR}/src/analyze.cpp"

View File

@ -13,6 +13,7 @@
#include "zig_llvm.hpp"
#include "hash_map.hpp"
#include "errmsg.hpp"
#include "bignum.hpp"
struct AstNode;
struct ImportTableEntry;
@ -49,15 +50,6 @@ enum CastOp {
CastOpPointerReinterpret,
};
struct Cast {
CastOp op;
// if op is CastOpArrayToString, this will be a pointer to
// the string struct on the stack
LLVMValueRef ptr;
TypeTableEntry *after_type;
AstNode *source_node;
};
struct ConstEnumValue {
uint64_t tag;
ConstExprValue *payload;
@ -68,9 +60,7 @@ struct ConstExprValue {
bool depends_on_compile_var;
union {
uint64_t x_uint;
int64_t x_int;
double x_float;
BigNum x_bignum;
bool x_bool;
FnTableEntry *x_fn;
TypeTableEntry *x_type;
@ -79,8 +69,19 @@ struct ConstExprValue {
} data;
};
struct Cast {
CastOp op;
// if op is CastOpArrayToString, this will be a pointer to
// the string struct on the stack
LLVMValueRef ptr;
TypeTableEntry *after_type;
AstNode *source_node;
ConstExprValue const_val;
};
struct Expr {
TypeTableEntry *type_entry;
TypeTableEntry *resolved_type;
// the context in which this expression is evaluated.
// for blocks, this points to the containing scope, not the block's own scope for its children.
BlockContext *block_context;
@ -92,10 +93,6 @@ struct Expr {
ConstExprValue const_val;
};
struct NumLitCodeGen {
TypeTableEntry *resolved_type;
};
struct StructValExprCodeGen {
TypeTableEntry *type_entry;
LLVMValueRef ptr;
@ -315,7 +312,6 @@ struct AstNodeFnCallExpr {
// populated by semantic analyzer:
BuiltinFnEntry *builtin_fn;
Expr resolved_expr;
NumLitCodeGen resolved_num_lit;
Cast cast;
FnTableEntry *fn_entry;
};
@ -550,26 +546,15 @@ struct AstNodeCharLiteral {
};
enum NumLit {
NumLitF32,
NumLitF64,
NumLitF128,
NumLitU8,
NumLitU16,
NumLitU32,
NumLitU64,
NumLitI8,
NumLitI16,
NumLitI32,
NumLitI64,
NumLitCount
NumLitFloat,
NumLitUInt,
};
struct AstNodeNumberLiteral {
NumLit kind;
// overflow is true if when parsing the number, we discovered it would not
// fit without losing data in a uint64_t, int64_t, or double
// fit without losing data in a uint64_t or double
bool overflow;
union {
@ -578,7 +563,6 @@ struct AstNodeNumberLiteral {
} data;
// populated by semantic analyzer
NumLitCodeGen codegen;
Expr resolved_expr;
};
@ -586,7 +570,6 @@ struct AstNodeErrorLiteral {
Buf symbol;
// populated by semantic analyzer
NumLitCodeGen codegen;
Expr resolved_expr;
};
@ -758,10 +741,6 @@ struct TypeTableEntryStruct {
bool reported_infinite_err;
};
struct TypeTableEntryNumLit {
NumLit kind;
};
struct TypeTableEntryMaybe {
TypeTableEntry *child_type;
};
@ -808,7 +787,8 @@ enum TypeTableEntryId {
TypeTableEntryIdPointer,
TypeTableEntryIdArray,
TypeTableEntryIdStruct,
TypeTableEntryIdNumberLiteral,
TypeTableEntryIdNumLitFloat,
TypeTableEntryIdNumLitInt,
TypeTableEntryIdMaybe,
TypeTableEntryIdError,
TypeTableEntryIdEnum,
@ -830,7 +810,6 @@ struct TypeTableEntry {
TypeTableEntryInt integral;
TypeTableEntryArray array;
TypeTableEntryStruct structure;
TypeTableEntryNumLit num_lit;
TypeTableEntryMaybe maybe;
TypeTableEntryError error;
TypeTableEntryEnum enumeration;
@ -951,10 +930,10 @@ struct CodeGen {
TypeTableEntry *entry_unreachable;
TypeTableEntry *entry_type;
TypeTableEntry *entry_invalid;
TypeTableEntry *entry_num_lit_int;
TypeTableEntry *entry_num_lit_float;
} builtin_types;
TypeTableEntry *num_lit_types[NumLitCount];
LLVMTargetDataRef target_data_ref;
unsigned pointer_size_bytes;
bool is_static;

View File

@ -103,7 +103,8 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdNumberLiteral:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdFn:
case TypeTableEntryIdError:
@ -121,24 +122,20 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
return entry;
}
static NumLit get_number_literal_kind_unsigned(uint64_t x) {
static int bits_needed_for_unsigned(uint64_t x) {
if (x <= UINT8_MAX) {
return NumLitU8;
return 8;
} else if (x <= UINT16_MAX) {
return NumLitU16;
return 16;
} else if (x <= UINT32_MAX) {
return NumLitU32;
return 32;
} else {
return NumLitU64;
return 64;
}
}
static TypeTableEntry *get_number_literal_type_unsigned(CodeGen *g, uint64_t x) {
return g->num_lit_types[get_number_literal_kind_unsigned(x)];
}
static TypeTableEntry *get_int_type_unsigned(CodeGen *g, uint64_t x) {
return get_int_type(g, false, num_lit_bit_count(get_number_literal_kind_unsigned(x)));
static TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
return get_int_type(g, false, bits_needed_for_unsigned(x));
}
TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
@ -660,10 +657,9 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
if (!enum_type->data.enumeration.is_invalid) {
enum_type->data.enumeration.gen_field_count = gen_field_index;
uint64_t tag_size_in_bits = num_lit_bit_count(get_number_literal_kind_unsigned(field_count));
enum_type->align_in_bits = tag_size_in_bits;
enum_type->size_in_bits = tag_size_in_bits + biggest_union_member_size_in_bits;
TypeTableEntry *tag_type_entry = get_int_type_unsigned(g, field_count);
TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
enum_type->align_in_bits = tag_type_entry->size_in_bits;
enum_type->size_in_bits = tag_type_entry->size_in_bits + biggest_union_member_size_in_bits;
enum_type->data.enumeration.tag_type = tag_type_entry;
if (biggest_union_member) {
@ -1048,136 +1044,105 @@ static TypeTableEntry *get_return_type(BlockContext *context) {
return unwrapped_node_type(return_type_node);
}
static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) {
NumLit num_lit = literal_type->data.num_lit.kind;
uint64_t lit_size_in_bits = num_lit_bit_count(num_lit);
switch (other_type->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdNumberLiteral:
zig_unreachable();
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdEnum:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdFn:
case TypeTableEntryIdError:
return false;
case TypeTableEntryIdInt:
if (is_num_lit_unsigned(num_lit)) {
return lit_size_in_bits <= other_type->size_in_bits;
} else {
return false;
}
case TypeTableEntryIdFloat:
if (is_num_lit_float(num_lit)) {
return lit_size_in_bits <= other_type->size_in_bits;
} else if (other_type->size_in_bits == 32) {
return lit_size_in_bits < 24;
} else if (other_type->size_in_bits == 64) {
return lit_size_in_bits < 53;
} else {
return false;
}
case TypeTableEntryIdMaybe:
return false;
}
zig_unreachable();
}
static TypeTableEntry *resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type)
{
NumLitCodeGen *num_lit_codegen = get_resolved_num_lit(literal_node);
if (non_literal_type && num_lit_fits_in_other_type(g, literal_type, non_literal_type)) {
assert(!num_lit_codegen->resolved_type);
num_lit_codegen->resolved_type = non_literal_type;
return non_literal_type;
} else {
return nullptr;
}
}
static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2,
TypeTableEntry *type1, TypeTableEntry *type2)
{
if (type1->id == TypeTableEntryIdNumberLiteral &&
type2->id == TypeTableEntryIdNumberLiteral)
static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTableEntry *other_type) {
Expr *expr = get_resolved_expr(literal_node);
ConstExprValue *const_val = &expr->const_val;
assert(const_val->ok);
if (other_type->id == TypeTableEntryIdFloat) {
expr->resolved_type = other_type;
return true;
} else if (other_type->id == TypeTableEntryIdInt &&
const_val->data.x_bignum.kind == BigNumKindInt)
{
NumLitCodeGen *codegen_num_lit_1 = get_resolved_num_lit(node1);
NumLitCodeGen *codegen_num_lit_2 = get_resolved_num_lit(node2);
assert(!codegen_num_lit_1->resolved_type);
assert(!codegen_num_lit_2->resolved_type);
if (is_num_lit_float(type1->data.num_lit.kind) &&
is_num_lit_float(type2->data.num_lit.kind))
if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type->size_in_bits,
other_type->data.integral.is_signed))
{
codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64;
codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64;
return g->builtin_types.entry_f64;
} else if (is_num_lit_unsigned(type1->data.num_lit.kind) &&
is_num_lit_unsigned(type2->data.num_lit.kind))
{
codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64;
codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64;
return g->builtin_types.entry_u64;
} else {
return nullptr;
expr->resolved_type = other_type;
return true;
}
} else if (type1->id == TypeTableEntryIdNumberLiteral) {
return resolve_rhs_number_literal(g, node2, type2, node1, type1);
} else {
assert(type2->id == TypeTableEntryIdNumberLiteral);
return resolve_rhs_number_literal(g, node1, type1, node2, type2);
} else if (other_type->id == TypeTableEntryIdNumLitFloat ||
other_type->id == TypeTableEntryIdNumLitInt)
{
return true;
}
add_node_error(g, literal_node,
buf_sprintf("value %s cannot be represented in type '%s'",
buf_ptr(bignum_to_buf(&const_val->data.x_bignum)),
buf_ptr(&other_type->name)));
return false;
}
static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *node,
TypeTableEntry *type1, TypeTableEntry *type2, AstNode *node1, AstNode *node2)
static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *parent_source_node,
AstNode **child_nodes, TypeTableEntry **child_types, int child_count)
{
if (type1->id == TypeTableEntryIdInvalid ||
type2->id == TypeTableEntryIdInvalid)
{
return type1;
} else if (type1->id == TypeTableEntryIdUnreachable) {
return type2;
} else if (type2->id == TypeTableEntryIdUnreachable) {
return type1;
} else if (type1->id == TypeTableEntryIdInt &&
type2->id == TypeTableEntryIdInt &&
type1->data.integral.is_signed == type2->data.integral.is_signed)
{
return (type1->size_in_bits > type2->size_in_bits) ? type1 : type2;
} else if (type1->id == TypeTableEntryIdFloat &&
type2->id == TypeTableEntryIdFloat)
{
return (type1->size_in_bits > type2->size_in_bits) ? type1 : type2;
} else if (type1->id == TypeTableEntryIdArray &&
type2->id == TypeTableEntryIdArray &&
type1 == type2)
{
return type1;
} else if (type1->id == TypeTableEntryIdNumberLiteral ||
type2->id == TypeTableEntryIdNumberLiteral)
{
TypeTableEntry *resolved_type = resolve_number_literals(g, node1, node2, type1, type2);
if (resolved_type)
return resolved_type;
} else if (type1 == type2) {
return type1;
TypeTableEntry *prev_type = child_types[0];
AstNode *prev_node = child_nodes[0];
if (prev_type->id == TypeTableEntryIdInvalid) {
return prev_type;
}
for (int i = 1; i < child_count; i += 1) {
TypeTableEntry *cur_type = child_types[i];
AstNode *cur_node = child_nodes[i];
if (cur_type->id == TypeTableEntryIdInvalid) {
return cur_type;
} else if (prev_type->id == TypeTableEntryIdUnreachable) {
prev_type = cur_type;
prev_node = cur_node;
} else if (cur_type->id == TypeTableEntryIdUnreachable) {
continue;
} else if (prev_type->id == TypeTableEntryIdInt &&
cur_type->id == TypeTableEntryIdInt &&
prev_type->data.integral.is_signed == cur_type->data.integral.is_signed)
{
if (cur_type->size_in_bits > prev_type->size_in_bits) {
prev_type = cur_type;
prev_node = cur_node;
}
} else if (prev_type->id == TypeTableEntryIdFloat &&
cur_type->id == TypeTableEntryIdFloat)
{
if (cur_type->size_in_bits > prev_type->size_in_bits) {
prev_type = cur_type;
prev_node = cur_node;
}
} else if (prev_type->id == TypeTableEntryIdNumLitFloat &&
cur_type->id == TypeTableEntryIdNumLitFloat)
{
continue;
} else if (prev_type->id == TypeTableEntryIdNumLitInt &&
cur_type->id == TypeTableEntryIdNumLitInt)
{
continue;
} else if (prev_type->id == TypeTableEntryIdNumLitInt ||
prev_type->id == TypeTableEntryIdNumLitFloat)
{
if (num_lit_fits_in_other_type(g, prev_node, cur_type)) {
prev_type = cur_type;
prev_node = cur_node;
continue;
} else {
return g->builtin_types.entry_invalid;
}
} else if (cur_type->id == TypeTableEntryIdNumLitInt ||
cur_type->id == TypeTableEntryIdNumLitFloat)
{
if (num_lit_fits_in_other_type(g, cur_node, prev_type)) {
continue;
} else {
return g->builtin_types.entry_invalid;
}
} else if (prev_type == cur_type) {
continue;
} else {
add_node_error(g, parent_source_node,
buf_sprintf("incompatible types: '%s' and '%s'",
buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
add_node_error(g, node,
buf_sprintf("incompatible types: '%s' and '%s'",
buf_ptr(&type1->name), buf_ptr(&type2->name)));
return g->builtin_types.entry_invalid;
return g->builtin_types.entry_invalid;
}
}
return prev_type;
}
static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *context, AstNode *node,
@ -1192,16 +1157,6 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
if (actual_type->id == TypeTableEntryIdUnreachable)
return actual_type; // sorry toots; gotta run. good luck with that expected type.
if (actual_type->id == TypeTableEntryIdNumberLiteral &&
num_lit_fits_in_other_type(g, actual_type, expected_type))
{
NumLitCodeGen *num_lit_code_gen = get_resolved_num_lit(node);
assert(!num_lit_code_gen->resolved_type ||
num_lit_code_gen->resolved_type == expected_type);
num_lit_code_gen->resolved_type = expected_type;
return expected_type;
}
if (expected_type->id == TypeTableEntryIdMaybe &&
actual_type->id == TypeTableEntryIdMaybe)
{
@ -1283,6 +1238,16 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
return expected_type;
}
if ((actual_type->id == TypeTableEntryIdNumLitFloat ||
actual_type->id == TypeTableEntryIdNumLitInt))
{
if (num_lit_fits_in_other_type(g, node, expected_type)) {
return expected_type;
} else {
return g->builtin_types.entry_invalid;
}
}
add_node_error(g, first_executing_node(node),
buf_sprintf("expected type '%s', got '%s'",
buf_ptr(&expected_type->name),
@ -1292,23 +1257,23 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
}
static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, BlockContext *block_context,
AstNode *parent_node,
AstNode *child1, AstNode *child2,
TypeTableEntry *type1, TypeTableEntry *type2)
AstNode *parent_source_node,
AstNode **child_nodes, TypeTableEntry **child_types, int child_count)
{
assert(type1);
assert(type2);
assert(child_count > 0);
TypeTableEntry *parent_type = determine_peer_type_compatibility(g, parent_node, type1, type2, child1, child2);
TypeTableEntry *expected_type = determine_peer_type_compatibility(g, parent_source_node,
child_nodes, child_types, child_count);
if (parent_type->id == TypeTableEntryIdInvalid) {
return parent_type;
if (expected_type->id == TypeTableEntryIdInvalid) {
return expected_type;
}
resolve_type_compatibility(g, block_context, child1, parent_type, type1);
resolve_type_compatibility(g, block_context, child2, parent_type, type2);
for (int i = 0; i < child_count; i += 1) {
resolve_type_compatibility(g, block_context, child_nodes[i], expected_type, child_types[i]);
}
return parent_type;
return expected_type;
}
BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
@ -1732,12 +1697,58 @@ static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, As
{
Expr *expr = get_resolved_expr(node);
expr->const_val.ok = true;
expr->const_val.data.x_uint = x;
TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, x);
TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type);
return resolved_type ? resolved_type : num_lit_type;
bignum_init_unsigned(&expr->const_val.data.x_bignum, x);
if (expected_type) {
if (expected_type->id == TypeTableEntryIdMaybe) {
return g->builtin_types.entry_num_lit_int;
} else {
num_lit_fits_in_other_type(g, node, expected_type);
return expected_type;
}
} else {
return g->builtin_types.entry_num_lit_int;
}
}
static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNode *node,
TypeTableEntry *expected_type, double x)
{
Expr *expr = get_resolved_expr(node);
expr->const_val.ok = true;
bignum_init_float(&expr->const_val.data.x_bignum, x);
if (expected_type) {
num_lit_fits_in_other_type(g, node, expected_type);
return expected_type;
} else {
return g->builtin_types.entry_num_lit_float;
}
}
static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *node,
bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), AstNode *op1, AstNode *op2,
TypeTableEntry *resolved_type)
{
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
const_val->ok = true;
if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
add_node_error(g, node,
buf_sprintf("value cannot be represented in any integer type"));
} else {
num_lit_fits_in_other_type(g, node, resolved_type);
}
return resolved_type;
}
static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
@ -1918,60 +1929,6 @@ static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) {
}
}
static bool eval_bool_bin_op_signed(int64_t a, BinOpType bin_op, int64_t b) {
if (bin_op == BinOpTypeCmpEq) {
return a == b;
} else if (bin_op == BinOpTypeCmpNotEq) {
return a != b;
} else if (bin_op == BinOpTypeCmpLessThan) {
return a < b;
} else if (bin_op == BinOpTypeCmpGreaterThan) {
return a > b;
} else if (bin_op == BinOpTypeCmpLessOrEq) {
return a <= b;
} else if (bin_op == BinOpTypeCmpGreaterOrEq) {
return a >= b;
} else {
zig_unreachable();
}
}
static bool eval_bool_bin_op_unsigned(uint64_t a, BinOpType bin_op, uint64_t b) {
if (bin_op == BinOpTypeCmpEq) {
return a == b;
} else if (bin_op == BinOpTypeCmpNotEq) {
return a != b;
} else if (bin_op == BinOpTypeCmpLessThan) {
return a < b;
} else if (bin_op == BinOpTypeCmpGreaterThan) {
return a > b;
} else if (bin_op == BinOpTypeCmpLessOrEq) {
return a <= b;
} else if (bin_op == BinOpTypeCmpGreaterOrEq) {
return a >= b;
} else {
zig_unreachable();
}
}
static bool eval_bool_bin_op_float(double a, BinOpType bin_op, double b) {
if (bin_op == BinOpTypeCmpEq) {
return a == b;
} else if (bin_op == BinOpTypeCmpNotEq) {
return a != b;
} else if (bin_op == BinOpTypeCmpLessThan) {
return a < b;
} else if (bin_op == BinOpTypeCmpGreaterThan) {
return a > b;
} else if (bin_op == BinOpTypeCmpLessOrEq) {
return a <= b;
} else if (bin_op == BinOpTypeCmpGreaterOrEq) {
return a >= b;
} else {
zig_unreachable();
}
}
static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
AstNode *node)
{
@ -1983,8 +1940,11 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, op1);
TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, op2);
AstNode *op_nodes[] = {op1, op2};
TypeTableEntry *op_types[] = {op1_type, op2_type};
TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node,
op1, op2, op1_type, op2_type);
op_nodes, op_types, 2);
if (resolved_type->id == TypeTableEntryIdInvalid) {
return g->builtin_types.entry_invalid;
@ -1997,20 +1957,30 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
}
bool answer;
if (resolved_type->id == TypeTableEntryIdInt) {
if (op1_type->data.integral.is_signed &&
op2_type->data.integral.is_signed)
{
answer = eval_bool_bin_op_signed(op1_val->data.x_int, bin_op_type, op2_val->data.x_int);
} else if (!op1_type->data.integral.is_signed &&
!op2_type->data.integral.is_signed)
{
answer = eval_bool_bin_op_unsigned(op1_val->data.x_uint, bin_op_type, op2_val->data.x_uint);
if (resolved_type->id == TypeTableEntryIdNumLitFloat ||
resolved_type->id == TypeTableEntryIdNumLitInt ||
resolved_type->id == TypeTableEntryIdFloat ||
resolved_type->id == TypeTableEntryIdInt)
{
bool (*bignum_cmp)(BigNum *, BigNum *);
if (bin_op_type == BinOpTypeCmpEq) {
bignum_cmp = bignum_cmp_eq;
} else if (bin_op_type == BinOpTypeCmpNotEq) {
bignum_cmp = bignum_cmp_neq;
} else if (bin_op_type == BinOpTypeCmpLessThan) {
bignum_cmp = bignum_cmp_lt;
} else if (bin_op_type == BinOpTypeCmpGreaterThan) {
bignum_cmp = bignum_cmp_gt;
} else if (bin_op_type == BinOpTypeCmpLessOrEq) {
bignum_cmp = bignum_cmp_lte;
} else if (bin_op_type == BinOpTypeCmpGreaterOrEq) {
bignum_cmp = bignum_cmp_gte;
} else {
zig_unreachable();
}
} else if (resolved_type->id == TypeTableEntryIdFloat) {
answer = eval_bool_bin_op_float(op1_val->data.x_float, bin_op_type, op2_val->data.x_float);
answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum);
} else if (resolved_type->id == TypeTableEntryIdEnum) {
ConstEnumValue *enum1 = &op1_val->data.x_enum;
ConstEnumValue *enum2 = &op2_val->data.x_enum;
@ -2124,7 +2094,45 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
TypeTableEntry *lhs_type = analyze_expression(g, import, context, expected_type, op1);
TypeTableEntry *rhs_type = analyze_expression(g, import, context, expected_type, op2);
return resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type);
AstNode *op_nodes[] = {op1, op2};
TypeTableEntry *op_types[] = {lhs_type, rhs_type};
TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node,
op_nodes, op_types, 2);
if (resolved_type->id == TypeTableEntryIdInvalid) {
return resolved_type;
}
ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
if (!op1_val->ok || !op2_val->ok) {
return resolved_type;
}
if (bin_op_type == BinOpTypeAdd) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_add, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeSub) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_sub, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeMult) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_mul, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeDiv) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_div, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeMod) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_mod, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeBinOr) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_or, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeBinAnd) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_and, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeBinXor) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_xor, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeBitShiftLeft) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_shl, op1, op2, resolved_type);
} else if (bin_op_type == BinOpTypeBitShiftRight) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_shr, op1, op2, resolved_type);
} else {
zig_unreachable();
}
}
case BinOpTypeUnwrapMaybe:
{
@ -2197,6 +2205,8 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
AstNodeVariableDeclaration *variable_declaration,
bool expr_is_maybe)
{
bool is_const = variable_declaration->is_const;
TypeTableEntry *explicit_type = nullptr;
if (variable_declaration->type != nullptr) {
explicit_type = analyze_type_expr(g, import, context, variable_declaration->type);
@ -2223,19 +2233,19 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
add_node_error(g, source_node,
buf_sprintf("variable initialization is unreachable"));
implicit_type = g->builtin_types.entry_invalid;
} else if (implicit_type->id == TypeTableEntryIdNumberLiteral) {
add_node_error(g, source_node,
buf_sprintf("unable to infer variable type"));
implicit_type = g->builtin_types.entry_invalid;
} else if (implicit_type->id == TypeTableEntryIdMetaType &&
!variable_declaration->is_const)
} else if (!is_const &&
(implicit_type->id == TypeTableEntryIdNumLitFloat ||
implicit_type->id == TypeTableEntryIdNumLitInt))
{
add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
implicit_type = g->builtin_types.entry_invalid;
} else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
implicit_type = g->builtin_types.entry_invalid;
}
}
if (implicit_type == nullptr && variable_declaration->is_const) {
if (implicit_type == nullptr && is_const) {
add_node_error(g, source_node, buf_sprintf("const variable missing initialization"));
implicit_type = g->builtin_types.entry_invalid;
}
@ -2244,7 +2254,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
assert(type != nullptr); // should have been caught by the parser
VariableTableEntry *var = add_local_var(g, source_node, context,
&variable_declaration->symbol, type, variable_declaration->is_const);
&variable_declaration->symbol, type, is_const);
bool is_pub = (variable_declaration->visib_mod != VisibModPrivate);
@ -2300,28 +2310,15 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry
return g->builtin_types.entry_invalid;
}
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
const_val->ok = true;
if (is_num_lit_unsigned(node->data.number_literal.kind)) {
const_val->data.x_uint = node->data.number_literal.data.x_uint;
} else if (is_num_lit_float(node->data.number_literal.kind)) {
const_val->data.x_float = node->data.number_literal.data.x_float;
if (node->data.number_literal.kind == NumLitUInt) {
return resolve_expr_const_val_as_unsigned_num_lit(g, node,
expected_type, node->data.number_literal.data.x_uint);
} else if (node->data.number_literal.kind == NumLitFloat) {
return resolve_expr_const_val_as_float_num_lit(g, node,
expected_type, node->data.number_literal.data.x_float);
} else {
zig_unreachable();
}
TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind];
if (expected_type) {
NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
assert(!codegen_num_lit->resolved_type);
TypeTableEntry *after_implicit_cast_resolved_type =
resolve_type_compatibility(g, block_context, node, expected_type, num_lit_type);
assert(codegen_num_lit->resolved_type ||
after_implicit_cast_resolved_type->id == TypeTableEntryIdInvalid);
return after_implicit_cast_resolved_type;
} else {
return num_lit_type;
}
}
static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
@ -2353,8 +2350,15 @@ static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import,
ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
if (const_val->ok) {
return resolve_expr_const_val_as_type(g, node,
get_array_type(g, child_type, const_val->data.x_uint));
if (const_val->data.x_bignum.is_negative) {
add_node_error(g, size_node,
buf_sprintf("array size %s is negative",
buf_ptr(bignum_to_buf(&const_val->data.x_bignum))));
return g->builtin_types.entry_invalid;
} else {
return resolve_expr_const_val_as_type(g, node,
get_array_type(g, child_type, const_val->data.x_bignum.data.x_uint));
}
} else {
return resolve_expr_const_val_as_type(g, node,
get_unknown_size_array_type(g, child_type, node->data.array_type.is_const));
@ -2493,9 +2497,9 @@ static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import
if (expected_type) {
return (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
} else {
return resolve_peer_type_compatibility(g, context, parent_node,
then_block, else_node,
then_type, else_type);
AstNode *op_nodes[] = {then_block, else_node};
TypeTableEntry *op_types[] = {then_type, else_type};
return resolve_peer_type_compatibility(g, context, parent_node, op_nodes, op_types, 2);
}
}
@ -2535,10 +2539,60 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
if (type_entry->id == TypeTableEntryIdInvalid) {
return g->builtin_types.entry_invalid;
} else if (type_entry->id == TypeTableEntryIdInt) {
// TODO const expr eval for min/max int
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
const_val->ok = true;
if (is_max) {
if (type_entry->data.integral.is_signed) {
int64_t val;
if (type_entry->size_in_bits == 64) {
val = INT64_MAX;
} else if (type_entry->size_in_bits == 32) {
val = INT32_MAX;
} else if (type_entry->size_in_bits == 16) {
val = INT16_MAX;
} else if (type_entry->size_in_bits == 8) {
val = INT8_MAX;
} else {
zig_unreachable();
}
bignum_init_signed(&const_val->data.x_bignum, val);
} else {
uint64_t val;
if (type_entry->size_in_bits == 64) {
val = UINT64_MAX;
} else if (type_entry->size_in_bits == 32) {
val = UINT32_MAX;
} else if (type_entry->size_in_bits == 16) {
val = UINT16_MAX;
} else if (type_entry->size_in_bits == 8) {
val = UINT8_MAX;
} else {
zig_unreachable();
}
bignum_init_unsigned(&const_val->data.x_bignum, val);
}
} else {
if (type_entry->data.integral.is_signed) {
int64_t val;
if (type_entry->size_in_bits == 64) {
val = INT64_MIN;
} else if (type_entry->size_in_bits == 32) {
val = INT32_MIN;
} else if (type_entry->size_in_bits == 16) {
val = INT16_MIN;
} else if (type_entry->size_in_bits == 8) {
val = INT8_MIN;
} else {
zig_unreachable();
}
bignum_init_signed(&const_val->data.x_bignum, val);
} else {
bignum_init_unsigned(&const_val->data.x_bignum, 0);
}
}
return type_entry;
} else if (type_entry->id == TypeTableEntryIdFloat) {
// TODO const expr eval for min/max float
zig_panic("TODO analyze_min_max_value float");
return type_entry;
} else if (type_entry->id == TypeTableEntryIdBool) {
return resolve_expr_const_val_as_bool(g, node, is_max);
@ -2559,10 +2613,9 @@ static void eval_const_expr_implicit_cast(CodeGen *g, ImportTableEntry *import,
case CastOpPointerReinterpret:
{
ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
if (other_val != const_val) {
*const_val = *other_val;
}
ConstExprValue *const_val = &cast->const_val;
assert(const_val != other_val);
*const_val = *other_val;
break;
}
case CastOpToUnknownSizeArray:
@ -2571,14 +2624,11 @@ static void eval_const_expr_implicit_cast(CodeGen *g, ImportTableEntry *import,
case CastOpMaybeWrap:
{
ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
ConstExprValue *const_val = &cast->const_val;
if (!other_val->ok) {
break;
} else if (const_val == other_val) {
ConstExprValue *new_val = allocate<ConstExprValue>(1);
memcpy(new_val, other_val, sizeof(ConstExprValue));
other_val = new_val;
}
assert(const_val != other_val);
const_val->data.x_maybe = other_val;
const_val->ok = true;
@ -2635,12 +2685,10 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
context->cast_expr_alloca_list.append(cast);
eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
return wanted_type;
} else if (actual_type->id == TypeTableEntryIdNumberLiteral &&
num_lit_fits_in_other_type(g, actual_type, wanted_type))
} else if (actual_type->id == TypeTableEntryIdNumLitFloat ||
actual_type->id == TypeTableEntryIdNumLitInt)
{
NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(expr_node);
assert(!codegen_num_lit->resolved_type);
codegen_num_lit->resolved_type = wanted_type;
num_lit_fits_in_other_type(g, expr_node, wanted_type);
cast->op = CastOpNothing;
eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
return wanted_type;
@ -2998,7 +3046,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
return g->builtin_types.entry_bool;
}
bool answer = target_const_val->data.x_bool;
bool answer = !target_const_val->data.x_bool;
return resolve_expr_const_val_as_bool(g, node, answer);
}
case PrefixOpBinNot:
@ -3008,8 +3056,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
if (expr_type->id == TypeTableEntryIdInvalid) {
return expr_type;
} else if (expr_type->id == TypeTableEntryIdInt ||
(expr_type->id == TypeTableEntryIdNumberLiteral &&
!is_num_lit_float(expr_type->data.num_lit.kind)))
expr_type->id == TypeTableEntryIdNumLitInt)
{
return expr_type;
} else {
@ -3017,6 +3064,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
buf_ptr(&expr_type->name)));
return g->builtin_types.entry_invalid;
}
// TODO const expr eval
}
case PrefixOpNegation:
{
@ -3030,13 +3078,16 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
return expr_type;
} else if (expr_type->id == TypeTableEntryIdFloat) {
return expr_type;
} else if (expr_type->id == TypeTableEntryIdNumberLiteral) {
} else if (expr_type->id == TypeTableEntryIdNumLitInt) {
return expr_type;
} else if (expr_type->id == TypeTableEntryIdNumLitFloat) {
return expr_type;
} else {
add_node_error(g, node, buf_sprintf("invalid negation type: '%s'",
buf_ptr(&expr_type->name)));
return g->builtin_types.entry_invalid;
}
// TODO const expr eval
}
case PrefixOpAddressOf:
case PrefixOpConstAddressOf:
@ -4129,59 +4180,6 @@ Expr *get_resolved_expr(AstNode *node) {
zig_unreachable();
}
NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
switch (node->type) {
case NodeTypeNumberLiteral:
return &node->data.number_literal.codegen;
case NodeTypeErrorLiteral:
return &node->data.error_literal.codegen;
case NodeTypeFnCallExpr:
return &node->data.fn_call_expr.resolved_num_lit;
case NodeTypeReturnExpr:
case NodeTypeBinOpExpr:
case NodeTypePrefixOpExpr:
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeFieldAccessExpr:
case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeAsmExpr:
case NodeTypeContainerInitExpr:
case NodeTypeRoot:
case NodeTypeRootExportDecl:
case NodeTypeFnProto:
case NodeTypeFnDef:
case NodeTypeFnDecl:
case NodeTypeParamDecl:
case NodeTypeBlock:
case NodeTypeExternBlock:
case NodeTypeDirective:
case NodeTypeVariableDeclaration:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeSymbol:
case NodeTypeUse:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeStructDecl:
case NodeTypeStructField:
case NodeTypeStructValueField:
case NodeTypeArrayType:
case NodeTypeErrorValueDecl:
zig_unreachable();
}
zig_unreachable();
}
TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
switch (node->type) {
case NodeTypeVariableDeclaration:

View File

@ -18,7 +18,6 @@ VariableTableEntry *find_variable(BlockContext *context, Buf *name);
TypeTableEntry *find_container(BlockContext *context, Buf *name);
BlockContext *new_block_context(AstNode *node, BlockContext *parent);
Expr *get_resolved_expr(AstNode *node);
NumLitCodeGen *get_resolved_num_lit(AstNode *node);
TopLevelDecl *get_resolved_top_level_decl(AstNode *node);
bool is_node_void_expr(AstNode *node);
TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits);

305
src/bignum.cpp Normal file
View File

@ -0,0 +1,305 @@
/*
* Copyright (c) 2016 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "bignum.hpp"
#include <assert.h>
#include <math.h>
static void bignum_normalize(BigNum *bn) {
assert(bn->kind == BigNumKindInt);
if (bn->data.x_uint == 0) {
bn->is_negative = false;
}
}
void bignum_init_float(BigNum *dest, double x) {
dest->kind = BigNumKindFloat;
dest->is_negative = false;
dest->data.x_float = x;
}
void bignum_init_unsigned(BigNum *dest, uint64_t x) {
dest->kind = BigNumKindInt;
dest->is_negative = false;
dest->data.x_uint = x;
}
void bignum_init_signed(BigNum *dest, int64_t x) {
dest->kind = BigNumKindInt;
if (x < 0) {
dest->is_negative = true;
dest->data.x_uint = ((uint64_t)(-(x + 1))) + 1;
} else {
dest->is_negative = false;
dest->data.x_uint = x;
}
}
bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed) {
assert(bn->kind == BigNumKindInt);
if (is_signed) {
if (bn->data.x_uint <= ((uint64_t)(INT8_MAX)) + 1) {
return bit_count >= 8;
} else if (bn->data.x_uint <= ((uint64_t)(INT16_MAX)) + 1) {
return bit_count >= 16;
} else if (bn->data.x_uint <= ((uint64_t)(INT32_MAX)) + 1) {
return bit_count >= 32;
} else {
return bit_count >= 64;
}
} else {
if (bn->is_negative) {
return bn->data.x_uint == 0;
} else {
if (bn->data.x_uint <= UINT8_MAX) {
return bit_count >= 8;
} else if (bn->data.x_uint <= UINT16_MAX) {
return bit_count >= 16;
} else if (bn->data.x_uint <= UINT32_MAX) {
return bit_count >= 32;
} else {
return bit_count >= 64;
}
}
}
}
uint64_t bignum_to_twos_complement(BigNum *bn) {
assert(bn->kind == BigNumKindInt);
if (bn->is_negative) {
int64_t x = bn->data.x_uint;
return -x;
} else {
return bn->data.x_uint;
}
}
// returns true if overflow happened
bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == op2->kind);
dest->kind = op1->kind;
if (dest->kind == BigNumKindFloat) {
dest->data.x_float = op1->data.x_float + op2->data.x_float;
return false;
}
if (op1->is_negative == op2->is_negative) {
return __builtin_uaddll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint);
} else if (!op1->is_negative && op2->is_negative) {
if (__builtin_usubll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) {
dest->data.x_uint = (UINT64_MAX - dest->data.x_uint) + 1;
dest->is_negative = true;
bignum_normalize(dest);
return false;
} else {
bignum_normalize(dest);
return false;
}
} else {
return bignum_add(dest, op2, op1);
}
}
void bignum_negate(BigNum *dest, BigNum *op) {
dest->kind = op->kind;
if (dest->kind == BigNumKindFloat) {
dest->data.x_float = -dest->data.x_float;
} else {
dest->data.x_uint = op->data.x_uint;
dest->is_negative = !op->is_negative;
bignum_normalize(dest);
}
}
bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2) {
BigNum op2_negated;
bignum_negate(&op2_negated, op2);
return bignum_add(dest, op1, &op2_negated);
}
bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == op2->kind);
dest->kind = op1->kind;
if (dest->kind == BigNumKindFloat) {
dest->data.x_float = op1->data.x_float * op2->data.x_float;
bignum_normalize(dest);
return false;
}
if (__builtin_umulll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) {
return true;
}
dest->is_negative = op1->is_negative != op2->is_negative;
bignum_normalize(dest);
return false;
}
bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == op2->kind);
dest->kind = op1->kind;
if (dest->kind == BigNumKindFloat) {
dest->data.x_float = op1->data.x_float / op2->data.x_float;
} else {
dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;
dest->is_negative = op1->is_negative != op2->is_negative;
bignum_normalize(dest);
}
return false;
}
bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == op2->kind);
dest->kind = op1->kind;
if (dest->kind == BigNumKindFloat) {
dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
} else {
if (op1->is_negative || op2->is_negative) {
zig_panic("TODO handle mod with negative numbers");
}
dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
bignum_normalize(dest);
}
return false;
}
bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == BigNumKindInt);
assert(op2->kind == BigNumKindInt);
assert(!op1->is_negative);
assert(!op2->is_negative);
dest->kind = BigNumKindInt;
dest->data.x_uint = op1->data.x_uint | op2->data.x_uint;
return false;
}
bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == BigNumKindInt);
assert(op2->kind == BigNumKindInt);
assert(!op1->is_negative);
assert(!op2->is_negative);
dest->kind = BigNumKindInt;
dest->data.x_uint = op1->data.x_uint & op2->data.x_uint;
return false;
}
bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == BigNumKindInt);
assert(op2->kind == BigNumKindInt);
assert(!op1->is_negative);
assert(!op2->is_negative);
dest->kind = BigNumKindInt;
dest->data.x_uint = op1->data.x_uint ^ op2->data.x_uint;
return false;
}
bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == BigNumKindInt);
assert(op2->kind == BigNumKindInt);
assert(!op1->is_negative);
assert(!op2->is_negative);
dest->kind = BigNumKindInt;
dest->data.x_uint = op1->data.x_uint << op2->data.x_uint;
return false;
}
bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2) {
assert(op1->kind == BigNumKindInt);
assert(op2->kind == BigNumKindInt);
assert(!op1->is_negative);
assert(!op2->is_negative);
dest->kind = BigNumKindInt;
dest->data.x_uint = op1->data.x_uint >> op2->data.x_uint;
return false;
}
Buf *bignum_to_buf(BigNum *bn) {
if (bn->kind == BigNumKindFloat) {
return buf_sprintf("%f", bn->data.x_float);
} else {
const char *neg = bn->is_negative ? "-" : "";
return buf_sprintf("%s%llu", neg, bn->data.x_uint);
}
}
bool bignum_cmp_eq(BigNum *op1, BigNum *op2) {
assert(op1->kind == op2->kind);
if (op1->kind == BigNumKindFloat) {
return op1->data.x_float == op2->data.x_float;
} else {
return op1->data.x_uint == op2->data.x_uint &&
(op1->is_negative == op2->is_negative || op1->data.x_uint == 0);
}
}
bool bignum_cmp_neq(BigNum *op1, BigNum *op2) {
return !bignum_cmp_eq(op1, op2);
}
bool bignum_cmp_lt(BigNum *op1, BigNum *op2) {
return !bignum_cmp_gte(op1, op2);
}
bool bignum_cmp_gt(BigNum *op1, BigNum *op2) {
return !bignum_cmp_lte(op1, op2);
}
bool bignum_cmp_lte(BigNum *op1, BigNum *op2) {
assert(op1->kind == op2->kind);
if (op1->kind == BigNumKindFloat) {
return (op1->data.x_float <= op2->data.x_float);
}
// assume normalized is_negative
if (!op1->is_negative && !op2->is_negative) {
return op1->data.x_uint <= op2->data.x_uint;
} else if (op1->is_negative && op2->is_negative) {
return op1->data.x_uint >= op2->data.x_uint;
} else if (op1->is_negative && !op2->is_negative) {
return true;
} else {
return false;
}
}
bool bignum_cmp_gte(BigNum *op1, BigNum *op2) {
assert(op1->kind == op2->kind);
if (op1->kind == BigNumKindFloat) {
return (op1->data.x_float >= op2->data.x_float);
}
// assume normalized is_negative
if (!op1->is_negative && !op2->is_negative) {
return op1->data.x_uint >= op2->data.x_uint;
} else if (op1->is_negative && op2->is_negative) {
return op1->data.x_uint <= op2->data.x_uint;
} else if (op1->is_negative && !op2->is_negative) {
return false;
} else {
return true;
}
}

56
src/bignum.hpp Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2016 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "buffer.hpp"
#include <stdint.h>
enum BigNumKind {
BigNumKindInt,
BigNumKindFloat,
};
struct BigNum {
BigNumKind kind;
bool is_negative;
union {
unsigned long long x_uint;
double x_float;
} data;
};
void bignum_init_float(BigNum *dest, double x);
void bignum_init_unsigned(BigNum *dest, uint64_t x);
void bignum_init_signed(BigNum *dest, int64_t x);
bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed);
uint64_t bignum_to_twos_complement(BigNum *bn);
// returns true if overflow happened
bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2);
bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2);
void bignum_negate(BigNum *dest, BigNum *op);
// returns the result of the comparison
bool bignum_cmp_eq(BigNum *op1, BigNum *op2);
bool bignum_cmp_neq(BigNum *op1, BigNum *op2);
bool bignum_cmp_lt(BigNum *op1, BigNum *op2);
bool bignum_cmp_gt(BigNum *op1, BigNum *op2);
bool bignum_cmp_lte(BigNum *op1, BigNum *op2);
bool bignum_cmp_gte(BigNum *op1, BigNum *op2);
Buf *bignum_to_buf(BigNum *bn);

View File

@ -118,6 +118,9 @@ static TypeTableEntry *get_expr_type(AstNode *node) {
if (expr->implicit_cast.after_type) {
return expr->implicit_cast.after_type;
}
if (expr->resolved_type) {
return expr->resolved_type;
}
return expr->type_entry;
}
@ -131,29 +134,35 @@ static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_no
}
}
static LLVMValueRef gen_number_literal_raw(CodeGen *g, AstNode *source_node,
NumLitCodeGen *codegen_num_lit, AstNodeNumberLiteral *num_lit_node)
{
TypeTableEntry *type_entry = codegen_num_lit->resolved_type;
static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *expr_node) {
Expr *expr = get_resolved_expr(expr_node);
TypeTableEntry *type_entry = expr->resolved_type;
if (!type_entry) {
type_entry = expr->type_entry;
}
assert(type_entry);
// override the expression type for number literals
get_resolved_expr(source_node)->type_entry = type_entry;
ConstExprValue *const_val = &expr->const_val;
assert(const_val->ok);
if (type_entry->id == TypeTableEntryIdInt) {
// here the union has int64_t and uint64_t and we purposefully read
// the uint64_t value in either case, because we want the twos
// complement representation
assert(const_val->data.x_bignum.kind == BigNumKindInt);
return LLVMConstInt(type_entry->type_ref,
num_lit_node->data.x_uint,
type_entry->data.integral.is_signed);
bignum_to_twos_complement(&const_val->data.x_bignum),
type_entry->data.integral.is_signed);
} else if (type_entry->id == TypeTableEntryIdFloat) {
return LLVMConstReal(type_entry->type_ref,
num_lit_node->data.x_float);
if (const_val->data.x_bignum.kind == BigNumKindFloat) {
return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float);
} else {
int64_t x = const_val->data.x_bignum.data.x_uint;
if (const_val->data.x_bignum.is_negative) {
x = -x;
}
return LLVMConstReal(type_entry->type_ref, x);
}
} else {
zig_panic("bad number literal type");
zig_unreachable();
}
}
@ -265,73 +274,10 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
return nullptr;
}
case BuiltinFnIdSizeof:
{
assert(node->data.fn_call_expr.params.length == 1);
AstNode *type_node = node->data.fn_call_expr.params.at(0);
TypeTableEntry *type_entry = get_type_for_type_node(type_node);
NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
AstNodeNumberLiteral num_lit_node;
num_lit_node.kind = NumLitU64; // this field isn't even read
num_lit_node.overflow = false;
num_lit_node.data.x_uint = type_entry->size_in_bits / 8;
return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node);
}
case BuiltinFnIdMinValue:
{
assert(node->data.fn_call_expr.params.length == 1);
AstNode *type_node = node->data.fn_call_expr.params.at(0);
TypeTableEntry *type_entry = get_type_for_type_node(type_node);
if (type_entry->id == TypeTableEntryIdInt) {
if (type_entry->data.integral.is_signed) {
return LLVMConstInt(type_entry->type_ref, 1ULL << (type_entry->size_in_bits - 1), false);
} else {
return LLVMConstNull(type_entry->type_ref);
}
} else if (type_entry->id == TypeTableEntryIdFloat) {
zig_panic("TODO codegen min_value float");
} else {
zig_unreachable();
}
}
case BuiltinFnIdMaxValue:
{
assert(node->data.fn_call_expr.params.length == 1);
AstNode *type_node = node->data.fn_call_expr.params.at(0);
TypeTableEntry *type_entry = get_type_for_type_node(type_node);
if (type_entry->id == TypeTableEntryIdInt) {
if (type_entry->data.integral.is_signed) {
return LLVMConstInt(type_entry->type_ref, (1ULL << (type_entry->size_in_bits - 1)) - 1, false);
} else {
return LLVMConstAllOnes(type_entry->type_ref);
}
} else if (type_entry->id == TypeTableEntryIdFloat) {
zig_panic("TODO codegen max_value float");
} else {
zig_unreachable();
}
}
case BuiltinFnIdMemberCount:
{
assert(node->data.fn_call_expr.params.length == 1);
AstNode *type_node = node->data.fn_call_expr.params.at(0);
TypeTableEntry *type_entry = get_type_for_type_node(type_node);
if (type_entry->id == TypeTableEntryIdEnum) {
NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
AstNodeNumberLiteral num_lit_node;
num_lit_node.kind = NumLitU64; // field ignored
num_lit_node.overflow = false;
num_lit_node.data.x_uint = type_entry->data.enumeration.field_count;
return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node);
} else {
zig_unreachable();
}
}
return gen_number_literal(g, node);
}
zig_unreachable();
}
@ -1407,11 +1353,22 @@ static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
assert(node->data.if_bool_expr.condition);
assert(node->data.if_bool_expr.then_block);
LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
ConstExprValue *const_val = &get_resolved_expr(node->data.if_bool_expr.condition)->const_val;
if (const_val->ok) {
if (const_val->data.x_bool) {
return gen_expr(g, node->data.if_bool_expr.then_block);
} else if (node->data.if_bool_expr.else_node) {
return gen_expr(g, node->data.if_bool_expr.else_node);
} else {
return nullptr;
}
} else {
LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
return gen_if_bool_expr_raw(g, node, cond_value,
node->data.if_bool_expr.then_block,
node->data.if_bool_expr.else_node);
return gen_if_bool_expr_raw(g, node, cond_value,
node->data.if_bool_expr.then_block,
node->data.if_bool_expr.else_node);
}
}
static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
@ -1932,15 +1889,6 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
get_resolved_expr(node)->block_context, false, &init_val);
}
static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeNumberLiteral);
NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
assert(codegen_num_lit);
return gen_number_literal_raw(g, node, codegen_num_lit, &node->data.number_literal);
}
static LLVMValueRef gen_error_literal(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeErrorLiteral);
@ -2383,20 +2331,6 @@ static void add_int_overflow_fns(CodeGen *g, TypeTableEntry *type_entry) {
type_entry->data.integral.mul_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
}
static const NumLit num_lit_kinds[] = {
NumLitF32,
NumLitF64,
NumLitF128,
NumLitU8,
NumLitU16,
NumLitU32,
NumLitU64,
NumLitI8,
NumLitI16,
NumLitI32,
NumLitI64,
};
static const int int_sizes_in_bits[] = {
8,
16,
@ -2411,18 +2345,15 @@ static void define_builtin_types(CodeGen *g) {
buf_init_from_str(&entry->name, "(invalid)");
g->builtin_types.entry_invalid = entry;
}
assert(NumLitCount == array_length(num_lit_kinds));
for (int i = 0; i < NumLitCount; i += 1) {
NumLit num_lit_kind = num_lit_kinds[i];
// This type should just create a constant with whatever actual number
// type is expected at the time.
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumberLiteral);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "(%s literal)", num_lit_str(num_lit_kind));
entry->data.num_lit.kind = num_lit_kind;
entry->size_in_bits = num_lit_bit_count(num_lit_kind);
g->num_lit_types[i] = entry;
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat);
buf_init_from_str(&entry->name, "(float literal)");
g->builtin_types.entry_num_lit_float = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt);
buf_init_from_str(&entry->name, "(integer literal)");
g->builtin_types.entry_num_lit_int = entry;
}
for (int i = 0; i < array_length(int_sizes_in_bits); i += 1) {

View File

@ -301,13 +301,12 @@ void ast_print(AstNode *node, int indent) {
break;
case NodeTypeNumberLiteral:
{
NumLit num_lit = node->data.number_literal.kind;
NumLit kind = node->data.number_literal.kind;
const char *name = node_type_str(node->type);
const char *kind_str = num_lit_str(num_lit);
if (is_num_lit_unsigned(num_lit)) {
fprintf(stderr, "%s %s %" PRIu64 "\n", name, kind_str, node->data.number_literal.data.x_uint);
if (kind == NumLitUInt) {
fprintf(stderr, "%s uint %" PRIu64 "\n", name, node->data.number_literal.data.x_uint);
} else {
fprintf(stderr, "%s %s %f\n", name, kind_str, node->data.number_literal.data.x_float);
fprintf(stderr, "%s float %f\n", name, node->data.number_literal.data.x_float);
}
break;
}
@ -808,16 +807,7 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
if (num_lit->overflow) return;
num_lit->data.x_uint = whole_number;
if (whole_number <= UINT8_MAX) {
num_lit->kind = NumLitU8;
} else if (whole_number <= UINT16_MAX) {
num_lit->kind = NumLitU16;
} else if (whole_number <= UINT32_MAX) {
num_lit->kind = NumLitU32;
} else {
num_lit->kind = NumLitU64;
}
num_lit->kind = NumLitUInt;
} else {
// float
@ -834,7 +824,7 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
}
assert(str_end == buf_ptr(pc->buf) + token->end_pos);
num_lit->data.x_float = x;
num_lit->kind = NumLitF64;
num_lit->kind = NumLitFloat;
return;
}
@ -954,8 +944,7 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
double x = *(double *)&double_bits;
num_lit->data.x_float = x;
// TODO: see if we can store it in f32
num_lit->kind = NumLitF64;
num_lit->kind = NumLitFloat;
}
}
@ -3053,99 +3042,3 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner,
pc.root = ast_parse_root(&pc, &token_index);
return pc.root;
}
const char *num_lit_str(NumLit num_lit) {
switch (num_lit) {
case NumLitF32:
return "f32";
case NumLitF64:
return "f64";
case NumLitF128:
return "f128";
case NumLitU8:
return "u8";
case NumLitU16:
return "u16";
case NumLitU32:
return "u32";
case NumLitU64:
return "u64";
case NumLitI8:
return "i8";
case NumLitI16:
return "i16";
case NumLitI32:
return "i32";
case NumLitI64:
return "i64";
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}
bool is_num_lit_unsigned(NumLit num_lit) {
switch (num_lit) {
case NumLitF32:
case NumLitF64:
case NumLitF128:
case NumLitI8:
case NumLitI16:
case NumLitI32:
case NumLitI64:
return false;
case NumLitU8:
case NumLitU16:
case NumLitU32:
case NumLitU64:
return true;
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}
bool is_num_lit_float(NumLit num_lit) {
switch (num_lit) {
case NumLitF32:
case NumLitF64:
case NumLitF128:
return true;
case NumLitU8:
case NumLitU16:
case NumLitU32:
case NumLitU64:
case NumLitI8:
case NumLitI16:
case NumLitI32:
case NumLitI64:
return false;
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}
uint64_t num_lit_bit_count(NumLit num_lit) {
switch (num_lit) {
case NumLitU8:
case NumLitI8:
return 8;
case NumLitU16:
case NumLitI16:
return 16;
case NumLitU32:
case NumLitI32:
case NumLitF32:
return 32;
case NumLitU64:
case NumLitI64:
case NumLitF64:
return 64;
case NumLitF128:
return 128;
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}

View File

@ -24,10 +24,4 @@ const char *node_type_str(NodeType node_type);
void ast_print(AstNode *node, int indent);
const char *num_lit_str(NumLit num_lit);
bool is_num_lit_unsigned(NumLit num_lit);
bool is_num_lit_float(NumLit num_lit);
uint64_t num_lit_bit_count(NumLit num_lit);
#endif

View File

@ -34,7 +34,7 @@ pub %.BadPerm;
pub %.PipeFail;
*/
const buffer_size: u16 = 4 * 1024;
//const buffer_size: u16 = 4 * 1024;
const max_u64_base10_digits: isize = 20;
/*

View File

@ -1323,7 +1323,7 @@ fn f() i32 => {
fn f() => {
if (0) {}
}
)SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'");
)SOURCE", 1, ".tmp_source.zig:3:9: error: value 0 cannot be represented in type 'bool'");
add_compile_fail_case("assign unreachable", R"SOURCE(
fn f() => {