From 32e2196257b2650e41b683d16bf00ba77ccfbb13 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jan 2016 03:02:25 -0700 Subject: [PATCH] number literal rework --- CMakeLists.txt | 1 + src/all_types.hpp | 61 ++--- src/analyze.cpp | 666 ++++++++++++++++++++++----------------------- src/analyze.hpp | 1 - src/bignum.cpp | 305 +++++++++++++++++++++ src/bignum.hpp | 56 ++++ src/codegen.cpp | 169 ++++-------- src/parser.cpp | 121 +------- src/parser.hpp | 6 - std/std.zig | 2 +- test/run_tests.cpp | 2 +- 11 files changed, 773 insertions(+), 617 deletions(-) create mode 100644 src/bignum.cpp create mode 100644 src/bignum.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a1a4eaace8..55b2868455 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" diff --git a/src/all_types.hpp b/src/all_types.hpp index 89619cd112..4c0dab7724 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -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; diff --git a/src/analyze.cpp b/src/analyze.cpp index 810d6006fb..9a9de850a2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -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(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: diff --git a/src/analyze.hpp b/src/analyze.hpp index bc75ed0beb..e9358e07bd 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -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); diff --git a/src/bignum.cpp b/src/bignum.cpp new file mode 100644 index 0000000000..4ec61308bb --- /dev/null +++ b/src/bignum.cpp @@ -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 +#include + +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; + } +} diff --git a/src/bignum.hpp b/src/bignum.hpp new file mode 100644 index 0000000000..e814c90697 --- /dev/null +++ b/src/bignum.hpp @@ -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 + +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); diff --git a/src/codegen.cpp b/src/codegen.cpp index 25ca9bf54a..be4661c57f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -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) { diff --git a/src/parser.cpp b/src/parser.cpp index aa60665389..7f9d39ea96 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -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 *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(); -} diff --git a/src/parser.hpp b/src/parser.hpp index c8209fae4d..230ff5c693 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -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 diff --git a/std/std.zig b/std/std.zig index 2d8962ec3c..6eae224f3f 100644 --- a/std/std.zig +++ b/std/std.zig @@ -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; /* diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 339c9ed1d1..1e7da90682 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -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() => {