From a177e305342c25e83d6b65e24561c27e3c2e4c91 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Apr 2016 15:32:08 -0700 Subject: [PATCH 1/9] compile-time function evaluation of pure functions --- CMakeLists.txt | 1 + src/all_types.hpp | 36 +++ src/analyze.cpp | 320 ++++++++++-------------- src/eval.cpp | 607 ++++++++++++++++++++++++++++++++++++++++++++++ src/eval.hpp | 24 ++ 5 files changed, 793 insertions(+), 195 deletions(-) create mode 100644 src/eval.cpp create mode 100644 src/eval.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f3cba1c28..cf0f41dd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ 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/eval.cpp" "${CMAKE_SOURCE_DIR}/src/analyze.cpp" "${CMAKE_SOURCE_DIR}/src/codegen.cpp" "${CMAKE_SOURCE_DIR}/src/buffer.cpp" diff --git a/src/all_types.hpp b/src/all_types.hpp index e71330be4..45691fcb2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -65,6 +65,7 @@ struct ConstExprValue { bool ok; // true if constant expression evalution worked bool depends_on_compile_var; bool undef; + bool deep_const; union { BigNum x_bignum; @@ -1005,6 +1006,13 @@ struct ImportTableEntry { ZigList use_decls; }; +enum FnAnalState { + FnAnalStateReady, + FnAnalStateProbing, + FnAnalStateComplete, + FnAnalStateSkipped, +}; + struct FnTableEntry { LLVMValueRef fn_value; AstNode *proto_node; @@ -1019,7 +1027,9 @@ struct FnTableEntry { bool internal_linkage; bool is_extern; bool is_test; + bool is_pure; BlockContext *parent_block_context; + FnAnalState anal_state; ZigList cast_alloca_list; ZigList struct_val_expr_alloca_list; @@ -1027,6 +1037,32 @@ struct FnTableEntry { ZigList goto_list; }; +struct EvalVar { + Buf *name; + ConstExprValue value; +}; + +struct EvalScope { + BlockContext *block_context; + ZigList vars; +}; + +struct EvalFnRoot { + CodeGen *codegen; + FnTableEntry *fn; + AstNode *call_node; + int branch_quota; + int branches_used; + AstNode *exceeded_quota_node; +}; + +struct EvalFn { + EvalFnRoot *root; + FnTableEntry *fn; + ConstExprValue *return_expr; + ZigList scope_stack; +}; + enum BuiltinFnId { BuiltinFnIdInvalid, BuiltinFnIdMemcpy, diff --git a/src/analyze.cpp b/src/analyze.cpp index 62b560f0c..375076109 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -13,6 +13,7 @@ #include "parseh.hpp" #include "config.h" #include "ast_render.hpp" +#include "eval.hpp" static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); @@ -1374,6 +1375,7 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN fn_table_entry->proto_node = proto_node; fn_table_entry->fn_def_node = fn_def_node; fn_table_entry->is_extern = is_extern; + fn_table_entry->is_pure = !is_extern; get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_'); @@ -2443,6 +2445,12 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, return return_type; } +static void mark_impure_fn(BlockContext *context) { + if (context->fn_entry) { + context->fn_entry->is_pure = false; + } +} + static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { @@ -2478,6 +2486,7 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; + expr->const_val.deep_const = true; return g->builtin_types.entry_void; } @@ -2485,6 +2494,7 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_type = type; + expr->const_val.deep_const = true; return g->builtin_types.entry_type; } @@ -2499,6 +2509,7 @@ static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, F Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_fn = fn; + expr->const_val.deep_const = true; return fn->type_entry; } @@ -2508,6 +2519,7 @@ static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_type = type_entry; + expr->const_val.deep_const = true; return type_entry; } @@ -2515,6 +2527,7 @@ static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_err.err = err; + expr->const_val.deep_const = true; return g->builtin_types.entry_pure_error; } @@ -2525,6 +2538,7 @@ static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, expr->const_val.ok = true; expr->const_val.depends_on_compile_var = depends_on_compile_var; expr->const_val.data.x_bool = value; + expr->const_val.deep_const = true; return g->builtin_types.entry_bool; } @@ -2532,6 +2546,7 @@ static TypeTableEntry *resolve_expr_const_val_as_null(CodeGen *g, AstNode *node, Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_maybe = nullptr; + expr->const_val.deep_const = true; return type; } @@ -2542,12 +2557,14 @@ static TypeTableEntry *resolve_expr_const_val_as_non_null(CodeGen *g, AstNode *n Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_maybe = other_val; + expr->const_val.deep_const = other_val->deep_const; return type; } static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNode *node, Buf *str) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; + expr->const_val.deep_const = true; int len_with_null = buf_len(str) + 1; expr->const_val.data.x_ptr.ptr = allocate(len_with_null); @@ -2557,12 +2574,14 @@ static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNod for (int i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &all_chars[i]; this_char->ok = true; + this_char->deep_const = true; bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]); expr->const_val.data.x_ptr.ptr[i] = this_char; } ConstExprValue *null_char = &all_chars[len_with_null - 1]; null_char->ok = true; + null_char->deep_const = true; bignum_init_unsigned(&null_char->data.x_bignum, 0); expr->const_val.data.x_ptr.ptr[len_with_null - 1] = null_char; @@ -2572,12 +2591,14 @@ static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNod static TypeTableEntry *resolve_expr_const_val_as_string_lit(CodeGen *g, AstNode *node, Buf *str) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; + expr->const_val.deep_const = true; expr->const_val.data.x_array.fields = allocate(buf_len(str)); ConstExprValue *all_chars = allocate(buf_len(str)); for (int i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &all_chars[i]; this_char->ok = true; + this_char->deep_const = true; bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]); expr->const_val.data.x_array.fields[i] = this_char; } @@ -2590,6 +2611,7 @@ 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.deep_const = true; bignum_init_unsigned(&expr->const_val.data.x_bignum, x); @@ -2601,6 +2623,7 @@ static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNo { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; + expr->const_val.deep_const = true; bignum_init_float(&expr->const_val.data.x_bignum, x); @@ -2616,6 +2639,7 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode * ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val; const_val->ok = true; + const_val->deep_const = true; if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { add_node_error(g, node, @@ -2684,6 +2708,19 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod } } +static bool var_is_pure(VariableTableEntry *var, TypeTableEntry *var_type, BlockContext *context) { + if (var->block_context->fn_entry == context->fn_entry) { + // variable was declared in the current function, so it's OK. + return true; + } + if (!var->is_const) { + return false; + } + + ConstExprValue *const_val = &get_resolved_expr(var->val_node)->const_val; + return const_val->deep_const; +} + static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only) { @@ -2700,7 +2737,11 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, VariableTableEntry *var = find_variable(g, context, variable_name); if (var) { - return analyze_var_ref(g, node, var); + TypeTableEntry *var_type = analyze_var_ref(g, node, var); + if (!var_is_pure(var, var_type, context)) { + mark_impure_fn(context); + } + return var_type; } AstNode *decl_node = find_decl(context, variable_name); @@ -2840,71 +2881,6 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc return expected_rhs_type; } -static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) { - if (bin_op == BinOpTypeBoolOr) { - return a || b; - } else if (bin_op == BinOpTypeBoolAnd) { - return a && b; - } else { - zig_unreachable(); - } -} - -static bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry) { - switch (type_entry->id) { - case TypeTableEntryIdEnum: - { - ConstEnumValue *enum1 = &a->data.x_enum; - ConstEnumValue *enum2 = &b->data.x_enum; - if (enum1->tag == enum2->tag) { - TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum1->tag]; - if (type_has_bits(enum_field->type_entry)) { - zig_panic("TODO const expr analyze enum special value for equality"); - } else { - return true; - } - } - return false; - } - case TypeTableEntryIdMetaType: - return a->data.x_type == b->data.x_type; - case TypeTableEntryIdVoid: - return true; - case TypeTableEntryIdPureError: - return a->data.x_err.err == b->data.x_err.err; - case TypeTableEntryIdFn: - return a->data.x_fn == b->data.x_fn; - case TypeTableEntryIdBool: - return a->data.x_bool == b->data.x_bool; - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: - return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum); - case TypeTableEntryIdPointer: - zig_panic("TODO"); - case TypeTableEntryIdArray: - zig_panic("TODO"); - case TypeTableEntryIdStruct: - zig_panic("TODO"); - case TypeTableEntryIdUndefLit: - zig_panic("TODO"); - case TypeTableEntryIdMaybe: - zig_panic("TODO"); - case TypeTableEntryIdErrorUnion: - zig_panic("TODO"); - case TypeTableEntryIdTypeDecl: - zig_panic("TODO"); - case TypeTableEntryIdNamespace: - zig_panic("TODO"); - case TypeTableEntryIdGenericFn: - case TypeTableEntryIdInvalid: - case TypeTableEntryIdUnreachable: - zig_unreachable(); - } - zig_unreachable(); -} - static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { @@ -2944,39 +2920,11 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im return g->builtin_types.entry_bool; } - bool answer; - if (type_can_gt_lt_cmp) { - 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(); - } - answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum); - } else { - bool are_equal = const_values_equal(op1_val, op2_val, resolved_type); - if (bin_op_type == BinOpTypeCmpEq) { - answer = are_equal; - } else if (bin_op_type == BinOpTypeCmpNotEq) { - answer = !are_equal; - } else { - zig_unreachable(); - } - } + ConstExprValue *out_val = &get_resolved_expr(node)->const_val; + eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val); + return g->builtin_types.entry_bool; - bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; - return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var); } static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -3002,9 +2950,9 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i return g->builtin_types.entry_bool; } - bool answer = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op_type, op2_val->data.x_bool); - bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; - return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var); + ConstExprValue *out_val = &get_resolved_expr(node)->const_val; + eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val); + return g->builtin_types.entry_bool; } static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -3041,7 +2989,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, } analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); - return g->builtin_types.entry_void; + return resolve_expr_const_val_as_void(g, node); } case BinOpTypeBoolOr: case BinOpTypeBoolAnd: @@ -3720,6 +3668,10 @@ static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import } ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val; + if (cond_val->undef) { + add_node_error(g, first_executing_node(*cond), buf_sprintf("branch on undefined value")); + return cond_type; + } if (cond_val->ok && !cond_val->depends_on_compile_var) { const char *str_val = cond_val->data.x_bool ? "true" : "false"; add_node_error(g, first_executing_node(*cond), @@ -3849,92 +3801,18 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor } } -static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *expr_node) { - assert(node->type == NodeTypeFnCallExpr); - ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; - ConstExprValue *const_val = &get_resolved_expr(node)->const_val; - if (!other_val->ok) { - return; - } - const_val->depends_on_compile_var = other_val->depends_on_compile_var; - const_val->undef = other_val->undef; - - assert(other_val != const_val); - switch (node->data.fn_call_expr.cast_op) { - case CastOpNoCast: - zig_unreachable(); - case CastOpNoop: - case CastOpWidenOrShorten: - case CastOpPointerReinterpret: - *const_val = *other_val; - break; - case CastOpPtrToInt: - case CastOpIntToPtr: - // can't do it - break; - case CastOpToUnknownSizeArray: - { - TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry; - assert(other_type->id == TypeTableEntryIdArray); - - ConstExprValue *all_fields = allocate(2); - ConstExprValue *ptr_field = &all_fields[0]; - ConstExprValue *len_field = &all_fields[1]; - - const_val->data.x_struct.fields = allocate(2); - const_val->data.x_struct.fields[0] = ptr_field; - const_val->data.x_struct.fields[1] = len_field; - - ptr_field->ok = true; - ptr_field->data.x_ptr.ptr = other_val->data.x_array.fields; - ptr_field->data.x_ptr.len = other_type->data.array.len; - - len_field->ok = true; - bignum_init_unsigned(&len_field->data.x_bignum, other_type->data.array.len); - - const_val->ok = true; - break; - } - case CastOpMaybeWrap: - const_val->data.x_maybe = other_val; - const_val->ok = true; - break; - case CastOpErrorWrap: - const_val->data.x_err.err = nullptr; - const_val->data.x_err.payload = other_val; - const_val->ok = true; - break; - case CastOpPureErrorWrap: - const_val->data.x_err.err = other_val->data.x_err.err; - const_val->ok = true; - break; - case CastOpErrToInt: - { - uint64_t value = other_val->data.x_err.err ? other_val->data.x_err.err->value : 0; - bignum_init_unsigned(&const_val->data.x_bignum, value); - const_val->ok = true; - break; - } - case CastOpIntToFloat: - bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum); - const_val->ok = true; - break; - case CastOpFloatToInt: - bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum); - const_val->ok = true; - break; - case CastOpBoolToInt: - bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0); - const_val->ok = true; - break; - } -} - static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode *node, AstNode *expr_node, TypeTableEntry *wanted_type, CastOp op, bool need_alloca) { node->data.fn_call_expr.cast_op = op; - eval_const_expr_implicit_cast(g, node, expr_node); + + ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; + TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry; + ConstExprValue *const_val = &get_resolved_expr(node)->const_val; + if (other_val->ok) { + eval_const_expr_implicit_cast(node->data.fn_call_expr.cast_op, other_val, other_type, const_val); + } + if (need_alloca) { if (context->fn_entry) { context->fn_entry->cast_alloca_list.append(node); @@ -4360,6 +4238,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry } case BuiltinFnIdMemcpy: { + mark_impure_fn(context); + AstNode *dest_node = node->data.fn_call_expr.params.at(0); AstNode *src_node = node->data.fn_call_expr.params.at(1); AstNode *len_node = node->data.fn_call_expr.params.at(2); @@ -4398,6 +4278,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry } case BuiltinFnIdMemset: { + mark_impure_fn(context); + AstNode *dest_node = node->data.fn_call_expr.params.at(0); AstNode *char_node = node->data.fn_call_expr.params.at(1); AstNode *len_node = node->data.fn_call_expr.params.at(2); @@ -4630,13 +4512,15 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry case BuiltinFnIdErrName: return analyze_err_name(g, import, context, node); case BuiltinFnIdBreakpoint: + mark_impure_fn(context); return g->builtin_types.entry_void; } zig_unreachable(); } static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, TypeTableEntry *struct_type) + TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, + AstNode *struct_node) { assert(node->type == NodeTypeFnCallExpr); @@ -4648,7 +4532,7 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, int src_param_count = fn_type->data.fn.fn_type_id.param_count; int actual_param_count = node->data.fn_call_expr.params.length; - if (struct_type) { + if (struct_node) { actual_param_count += 1; } @@ -4662,17 +4546,31 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, buf_sprintf("expected %d arguments, got %d", src_param_count, actual_param_count)); } + bool all_args_const_expr = true; + + if (struct_node) { + ConstExprValue *struct_const_val = &get_resolved_expr(struct_node)->const_val; + if (!struct_const_val->ok) { + all_args_const_expr = false; + } + } + // analyze each parameter. in the case of a method, we already analyzed the // first parameter in order to figure out which struct we were calling a method on. for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { - AstNode *child = node->data.fn_call_expr.params.at(i); + AstNode **child = &node->data.fn_call_expr.params.at(i); // determine the expected type for each parameter TypeTableEntry *expected_param_type = nullptr; - int fn_proto_i = i + (struct_type ? 1 : 0); + int fn_proto_i = i + (struct_node ? 1 : 0); if (fn_proto_i < src_param_count) { expected_param_type = fn_type->data.fn.fn_type_id.param_info[fn_proto_i].type; } - analyze_expression(g, import, context, expected_param_type, child); + analyze_expression(g, import, context, expected_param_type, *child); + + ConstExprValue *const_arg_val = &get_resolved_expr(*child)->const_val; + if (!const_arg_val->ok) { + all_args_const_expr = false; + } } TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; @@ -4681,6 +4579,30 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, return return_type; } + FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; + if (fn_table_entry && fn_table_entry->is_pure && all_args_const_expr) { + if (fn_table_entry->anal_state == FnAnalStateReady) { + analyze_fn_body(g, fn_table_entry); + } else if (fn_table_entry->anal_state == FnAnalStateProbing) { + mark_impure_fn(context); + } + if (fn_table_entry->is_pure) { + if (fn_table_entry->anal_state == FnAnalStateComplete) { + ConstExprValue *result_val = &get_resolved_expr(node)->const_val; + if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) { + // function evaluation generated an error + return g->builtin_types.entry_invalid; + } + return return_type; + } else if (fn_table_entry->anal_state == FnAnalStateSkipped) { + return g->builtin_types.entry_invalid; + } + } else { + // calling an impure fn is impure + mark_impure_fn(context); + } + } + if (handle_is_ptr(return_type)) { context->fn_entry->cast_alloca_list.append(node); } @@ -4689,13 +4611,13 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, } static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, TypeTableEntry *struct_type) + TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, AstNode *struct_node) { assert(node->type == NodeTypeFnCallExpr); node->data.fn_call_expr.fn_entry = fn_table_entry; - return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_type); + return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_node); } static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, @@ -4861,17 +4783,17 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import return analyze_cast_expr(g, import, context, node); } } else if (invoke_type_entry->id == TypeTableEntryIdFn) { - TypeTableEntry *bare_struct_type; + AstNode *struct_node; if (fn_ref_expr->type == NodeTypeFieldAccessExpr && fn_ref_expr->data.field_access_expr.is_member_fn) { - bare_struct_type = fn_ref_expr->data.field_access_expr.bare_struct_type; + struct_node = fn_ref_expr; } else { - bare_struct_type = nullptr; + struct_node = nullptr; } return analyze_fn_call_raw(g, import, context, expected_type, node, - const_val->data.x_fn, bare_struct_type); + const_val->data.x_fn, struct_node); } else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) { return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type); } else { @@ -5421,6 +5343,8 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { + mark_impure_fn(context); + node->data.asm_expr.return_count = 0; TypeTableEntry *return_type = g->builtin_types.entry_void; for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { @@ -5641,8 +5565,10 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { if (fn_proto_node->data.fn_proto.skip) { // we detected an error with this function definition which prevents us // from further analyzing it. + fn_table_entry->anal_state = FnAnalStateSkipped; return; } + fn_table_entry->anal_state = FnAnalStateProbing; BlockContext *context = node->data.fn_def.block_context; @@ -5697,6 +5623,8 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { buf_ptr(&label->decl_node->data.label.name))); } } + + fn_table_entry->anal_state = FnAnalStateComplete; } static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, @@ -6005,7 +5933,9 @@ void semantic_analyze(CodeGen *g) { for (int i = 0; i < g->fn_defs.length; i += 1) { FnTableEntry *fn_entry = g->fn_defs.at(i); - analyze_fn_body(g, fn_entry); + if (fn_entry->anal_state == FnAnalStateReady) { + analyze_fn_body(g, fn_entry); + } } } diff --git a/src/eval.cpp b/src/eval.cpp new file mode 100644 index 000000000..08a858ab2 --- /dev/null +++ b/src/eval.cpp @@ -0,0 +1,607 @@ +#include "eval.hpp" +#include "analyze.hpp" + +static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val); + +bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry) { + switch (type_entry->id) { + case TypeTableEntryIdEnum: + { + ConstEnumValue *enum1 = &a->data.x_enum; + ConstEnumValue *enum2 = &b->data.x_enum; + if (enum1->tag == enum2->tag) { + TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum1->tag]; + if (type_has_bits(enum_field->type_entry)) { + zig_panic("TODO const expr analyze enum special value for equality"); + } else { + return true; + } + } + return false; + } + case TypeTableEntryIdMetaType: + return a->data.x_type == b->data.x_type; + case TypeTableEntryIdVoid: + return true; + case TypeTableEntryIdPureError: + return a->data.x_err.err == b->data.x_err.err; + case TypeTableEntryIdFn: + return a->data.x_fn == b->data.x_fn; + case TypeTableEntryIdBool: + return a->data.x_bool == b->data.x_bool; + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum); + case TypeTableEntryIdPointer: + zig_panic("TODO"); + case TypeTableEntryIdArray: + zig_panic("TODO"); + case TypeTableEntryIdStruct: + zig_panic("TODO"); + case TypeTableEntryIdUndefLit: + zig_panic("TODO"); + case TypeTableEntryIdMaybe: + zig_panic("TODO"); + case TypeTableEntryIdErrorUnion: + zig_panic("TODO"); + case TypeTableEntryIdTypeDecl: + zig_panic("TODO"); + case TypeTableEntryIdNamespace: + zig_panic("TODO"); + case TypeTableEntryIdGenericFn: + case TypeTableEntryIdInvalid: + case TypeTableEntryIdUnreachable: + zig_unreachable(); + } + zig_unreachable(); +} + + +static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out); + +static bool eval_block(EvalFn *ef, AstNode *node, ConstExprValue *out) { + assert(node->type == NodeTypeBlock); + + EvalScope *my_scope = allocate(1); + my_scope->block_context = node->block_context; + ef->scope_stack.append(my_scope); + + for (int i = 0; i < node->data.block.statements.length; i += 1) { + AstNode *child = node->data.block.statements.at(i); + if (eval_expr(ef, child, out)) return true; + } + + ef->scope_stack.pop(); + + return false; +} + +static bool eval_return(EvalFn *ef, AstNode *node, ConstExprValue *out) { + assert(node->type == NodeTypeReturnExpr); + + eval_expr(ef, node->data.return_expr.expr, ef->return_expr); + return true; +} + +static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) { + if (bin_op == BinOpTypeBoolOr) { + return a || b; + } else if (bin_op == BinOpTypeBoolAnd) { + return a && b; + } else { + zig_unreachable(); + } +} + +static void eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, + ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *)) +{ + bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); + assert(!overflow); + out_val->ok = true; + out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; +} + +void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, + BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val) +{ + assert(op1_val->ok); + assert(op2_val->ok); + assert(op1_type == op2_type); + + switch (bin_op) { + case BinOpTypeAssign: + case BinOpTypeAssignTimes: + case BinOpTypeAssignDiv: + case BinOpTypeAssignMod: + case BinOpTypeAssignPlus: + case BinOpTypeAssignMinus: + case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftRight: + case BinOpTypeAssignBitAnd: + case BinOpTypeAssignBitXor: + case BinOpTypeAssignBitOr: + case BinOpTypeAssignBoolAnd: + case BinOpTypeAssignBoolOr: + out_val->ok = true; + return; + case BinOpTypeBoolOr: + case BinOpTypeBoolAnd: + assert(op1_type->id == TypeTableEntryIdBool); + assert(op2_type->id == TypeTableEntryIdBool); + out_val->data.x_bool = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op, op2_val->data.x_bool); + out_val->ok = true; + out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; + return; + case BinOpTypeCmpEq: + case BinOpTypeCmpNotEq: + case BinOpTypeCmpLessThan: + case BinOpTypeCmpGreaterThan: + case BinOpTypeCmpLessOrEq: + case BinOpTypeCmpGreaterOrEq: + { + bool type_can_gt_lt_cmp = (op1_type->id == TypeTableEntryIdNumLitFloat || + op1_type->id == TypeTableEntryIdNumLitInt || + op1_type->id == TypeTableEntryIdFloat || + op1_type->id == TypeTableEntryIdInt); + bool answer; + if (type_can_gt_lt_cmp) { + bool (*bignum_cmp)(BigNum *, BigNum *); + if (bin_op == BinOpTypeCmpEq) { + bignum_cmp = bignum_cmp_eq; + } else if (bin_op == BinOpTypeCmpNotEq) { + bignum_cmp = bignum_cmp_neq; + } else if (bin_op == BinOpTypeCmpLessThan) { + bignum_cmp = bignum_cmp_lt; + } else if (bin_op == BinOpTypeCmpGreaterThan) { + bignum_cmp = bignum_cmp_gt; + } else if (bin_op == BinOpTypeCmpLessOrEq) { + bignum_cmp = bignum_cmp_lte; + } else if (bin_op == BinOpTypeCmpGreaterOrEq) { + bignum_cmp = bignum_cmp_gte; + } else { + zig_unreachable(); + } + + answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum); + } else { + bool are_equal = const_values_equal(op1_val, op2_val, op1_type); + if (bin_op == BinOpTypeCmpEq) { + answer = are_equal; + } else if (bin_op == BinOpTypeCmpNotEq) { + answer = !are_equal; + } else { + zig_unreachable(); + } + } + + out_val->depends_on_compile_var = + op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; + out_val->data.x_bool = answer; + out_val->ok = true; + return; + } + case BinOpTypeAdd: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add); + case BinOpTypeBinOr: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_or); + case BinOpTypeBinXor: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_xor); + case BinOpTypeBinAnd: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_and); + case BinOpTypeBitShiftLeft: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl); + case BinOpTypeBitShiftRight: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shr); + case BinOpTypeSub: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub); + case BinOpTypeMult: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul); + case BinOpTypeDiv: + { + bool is_int = false; + bool is_float = false; + if (op1_type->id == TypeTableEntryIdInt || + op1_type->id == TypeTableEntryIdNumLitInt) + { + is_int = true; + } else if (op1_type->id == TypeTableEntryIdFloat || + op1_type->id == TypeTableEntryIdNumLitFloat) + { + is_float = true; + } + if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || + (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) + { + zig_panic("TODO handle errors in eval"); + } else { + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_div); + } + } + case BinOpTypeMod: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod); + case BinOpTypeUnwrapMaybe: + zig_panic("TODO"); + case BinOpTypeStrCat: + zig_panic("TODO"); + case BinOpTypeInvalid: + zig_unreachable(); + } + zig_unreachable(); +} + +static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeBinOpExpr); + + AstNode *op1 = node->data.bin_op_expr.op1; + AstNode *op2 = node->data.bin_op_expr.op2; + + TypeTableEntry *op1_type = get_resolved_expr(op1)->type_entry; + TypeTableEntry *op2_type = get_resolved_expr(op2)->type_entry; + + ConstExprValue op1_val = {0}; + if (eval_expr(ef, op1, &op1_val)) return true; + + ConstExprValue op2_val = {0}; + if (eval_expr(ef, op2, &op2_val)) return true; + + BinOpType bin_op = node->data.bin_op_expr.bin_op; + + eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val); + + return false; +} + +static EvalVar *find_var(EvalFn *ef, Buf *name) { + int scope_index = ef->scope_stack.length - 1; + while (scope_index >= 0) { + EvalScope *scope = ef->scope_stack.at(scope_index); + for (int var_i = 0; var_i < scope->vars.length; var_i += 1) { + EvalVar *var = &scope->vars.at(var_i); + if (buf_eql_buf(var->name, name)) { + return var; + } + } + scope_index -= 1; + } + + return nullptr; +} + +static bool eval_symbol_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeSymbol); + + Buf *name = &node->data.symbol_expr.symbol; + EvalVar *var = find_var(ef, name); + + *out_val = var->value; + + return false; +} + +static TypeTableEntry *resolve_expr_type(AstNode *node) { + Expr *expr = get_resolved_expr(node); + TypeTableEntry *type_entry = expr->type_entry; + assert(type_entry->id == TypeTableEntryIdMetaType); + ConstExprValue *const_val = &expr->const_val; + assert(const_val->ok); + return const_val->data.x_type; +} + +static bool eval_container_init_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeContainerInitExpr); + + AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; + ContainerInitKind kind = container_init_expr->kind; + TypeTableEntry *container_type = resolve_expr_type(container_init_expr->type); + out_val->ok = true; + + if (container_type->id == TypeTableEntryIdStruct && + !container_type->data.structure.is_unknown_size_array && + kind == ContainerInitKindStruct) + { + int expr_field_count = container_init_expr->entries.length; + int actual_field_count = container_type->data.structure.src_field_count; + assert(expr_field_count == actual_field_count); + + out_val->data.x_struct.fields = allocate(actual_field_count); + + for (int i = 0; i < expr_field_count; i += 1) { + AstNode *val_field_node = container_init_expr->entries.at(i); + assert(val_field_node->type == NodeTypeStructValueField); + + TypeStructField *type_field = val_field_node->data.struct_val_field.type_struct_field; + int field_index = type_field->src_index; + + ConstExprValue src_field_val = {0}; + if (eval_expr(ef, val_field_node->data.struct_val_field.expr, &src_field_val)) return true; + + ConstExprValue *dest_field_val = allocate(1); + *dest_field_val = src_field_val; + + out_val->data.x_struct.fields[field_index] = dest_field_val; + out_val->depends_on_compile_var = out_val->depends_on_compile_var || + src_field_val.depends_on_compile_var; + } + } else if (container_type->id == TypeTableEntryIdVoid) { + return false; + } else { + zig_panic("TODO"); + } + + + return false; +} + +static bool eval_if_bool_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeIfBoolExpr); + + ConstExprValue cond_val = {0}; + if (eval_expr(ef, node->data.if_bool_expr.condition, &cond_val)) return true; + + AstNode *exec_node = cond_val.data.x_bool ? + node->data.if_bool_expr.then_block : node->data.if_bool_expr.else_node; + + if (exec_node) { + if (eval_expr(ef, exec_node, out_val)) return true; + } + out_val->ok = true; + return false; +} + +void eval_const_expr_implicit_cast(CastOp cast_op, + ConstExprValue *other_val, TypeTableEntry *other_type, + ConstExprValue *const_val) +{ + const_val->depends_on_compile_var = other_val->depends_on_compile_var; + const_val->undef = other_val->undef; + + assert(other_val != const_val); + switch (cast_op) { + case CastOpNoCast: + zig_unreachable(); + case CastOpNoop: + case CastOpWidenOrShorten: + case CastOpPointerReinterpret: + *const_val = *other_val; + break; + case CastOpPtrToInt: + case CastOpIntToPtr: + // can't do it + break; + case CastOpToUnknownSizeArray: + { + assert(other_type->id == TypeTableEntryIdArray); + + ConstExprValue *all_fields = allocate(2); + ConstExprValue *ptr_field = &all_fields[0]; + ConstExprValue *len_field = &all_fields[1]; + + const_val->data.x_struct.fields = allocate(2); + const_val->data.x_struct.fields[0] = ptr_field; + const_val->data.x_struct.fields[1] = len_field; + + ptr_field->ok = true; + ptr_field->data.x_ptr.ptr = other_val->data.x_array.fields; + ptr_field->data.x_ptr.len = other_type->data.array.len; + + len_field->ok = true; + bignum_init_unsigned(&len_field->data.x_bignum, other_type->data.array.len); + + const_val->ok = true; + break; + } + case CastOpMaybeWrap: + const_val->data.x_maybe = other_val; + const_val->ok = true; + break; + case CastOpErrorWrap: + const_val->data.x_err.err = nullptr; + const_val->data.x_err.payload = other_val; + const_val->ok = true; + break; + case CastOpPureErrorWrap: + const_val->data.x_err.err = other_val->data.x_err.err; + const_val->ok = true; + break; + case CastOpErrToInt: + { + uint64_t value = other_val->data.x_err.err ? other_val->data.x_err.err->value : 0; + bignum_init_unsigned(&const_val->data.x_bignum, value); + const_val->ok = true; + break; + } + case CastOpIntToFloat: + bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum); + const_val->ok = true; + break; + case CastOpFloatToInt: + bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum); + const_val->ok = true; + break; + case CastOpBoolToInt: + bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0); + const_val->ok = true; + break; + } +} + +static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeFnCallExpr); + + CastOp cast_op = node->data.fn_call_expr.cast_op; + if (node->data.fn_call_expr.is_builtin) { + zig_panic("TODO"); + } else if (cast_op != CastOpNoCast) { + AstNode *expr_node = node->data.fn_call_expr.params.at(0); + Expr *expr = get_resolved_expr(expr_node); + eval_const_expr_implicit_cast(cast_op, &expr->const_val, expr->type_entry, out_val); + return false; + } + + AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; + if (node->data.fn_call_expr.enum_type) { + zig_panic("TODO"); + } + + FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; + + if (fn_ref_expr->type == NodeTypeFieldAccessExpr && + fn_ref_expr->data.field_access_expr.is_member_fn) + { + zig_panic("TODO"); + } + + if (!fn_table_entry) { + zig_panic("TODO"); + } + + int param_count = node->data.fn_call_expr.params.length; + ConstExprValue *args = allocate(param_count); + for (int i = 0; i < param_count; i += 1) { + AstNode *param_expr_node = node->data.fn_call_expr.params.at(i); + ConstExprValue *param_val = &args[i]; + if (eval_expr(ef, param_expr_node, param_val)) return true; + } + + ef->root->branches_used += 1; + + eval_fn_args(ef->root, fn_table_entry, args, out_val); + return false; +} + +static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { + if (ef->root->branches_used > ef->root->branch_quota) { + ef->root->exceeded_quota_node = node; + return true; + } + switch (node->type) { + case NodeTypeBlock: + return eval_block(ef, node, out); + case NodeTypeReturnExpr: + return eval_return(ef, node, out); + case NodeTypeBinOpExpr: + return eval_bin_op_expr(ef, node, out); + case NodeTypeSymbol: + return eval_symbol_expr(ef, node, out); + case NodeTypeContainerInitExpr: + return eval_container_init_expr(ef, node, out); + case NodeTypeIfBoolExpr: + return eval_if_bool_expr(ef, node, out); + case NodeTypeFnCallExpr: + return eval_fn_call_expr(ef, node, out); + case NodeTypeRoot: + case NodeTypeFnProto: + case NodeTypeFnDef: + case NodeTypeFnDecl: + case NodeTypeParamDecl: + case NodeTypeDirective: + case NodeTypeDefer: + case NodeTypeVariableDeclaration: + case NodeTypeTypeDecl: + case NodeTypeErrorValueDecl: + case NodeTypeUnwrapErrorExpr: + case NodeTypeNumberLiteral: + case NodeTypeStringLiteral: + case NodeTypeCharLiteral: + case NodeTypePrefixOpExpr: + case NodeTypeArrayAccessExpr: + case NodeTypeSliceExpr: + case NodeTypeFieldAccessExpr: + case NodeTypeUse: + case NodeTypeBoolLiteral: + case NodeTypeNullLiteral: + case NodeTypeUndefinedLiteral: + case NodeTypeIfVarExpr: + case NodeTypeWhileExpr: + case NodeTypeForExpr: + case NodeTypeSwitchExpr: + case NodeTypeSwitchProng: + case NodeTypeSwitchRange: + case NodeTypeLabel: + case NodeTypeGoto: + case NodeTypeBreak: + case NodeTypeContinue: + case NodeTypeAsmExpr: + case NodeTypeStructDecl: + case NodeTypeStructField: + case NodeTypeStructValueField: + case NodeTypeArrayType: + case NodeTypeErrorType: + case NodeTypeTypeLiteral: + zig_unreachable(); + } +} + +static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val) { + EvalFn ef = {0}; + ef.root = efr; + ef.fn = fn; + ef.return_expr = out_val; + + EvalScope *root_scope = allocate(1); + root_scope->block_context = fn->fn_def_node->data.fn_def.body->block_context; + ef.scope_stack.append(root_scope); + + int param_count = fn->type_entry->data.fn.fn_type_id.param_count; + for (int i = 0; i < param_count; i += 1) { + AstNode *decl_param_node = fn->proto_node->data.fn_proto.params.at(i); + assert(decl_param_node->type == NodeTypeParamDecl); + + ConstExprValue *src_const_val = &args[i]; + assert(src_const_val->ok); + + root_scope->vars.add_one(); + EvalVar *eval_var = &root_scope->vars.last(); + eval_var->name = &decl_param_node->data.param_decl.name; + eval_var->value = *src_const_val; + } + + return eval_expr(&ef, fn->fn_def_node->data.fn_def.body, out_val); + +} + +bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_val, + int branch_quota, AstNode *struct_node) +{ + assert(node->type == NodeTypeFnCallExpr); + + EvalFnRoot efr = {0}; + efr.codegen = g; + efr.fn = fn; + efr.call_node = node; + efr.branch_quota = branch_quota; + + int call_param_count = node->data.fn_call_expr.params.length; + int type_param_count = fn->type_entry->data.fn.fn_type_id.param_count; + ConstExprValue *args = allocate(type_param_count); + int next_arg_index = 0; + if (struct_node) { + ConstExprValue *struct_val = &get_resolved_expr(struct_node)->const_val; + assert(struct_val->ok); + args[next_arg_index] = *struct_val; + next_arg_index += 1; + } + for (int call_index = 0; call_index < call_param_count; call_index += 1) { + AstNode *call_param_node = node->data.fn_call_expr.params.at(call_index); + ConstExprValue *src_const_val = &get_resolved_expr(call_param_node)->const_val; + assert(src_const_val->ok); + args[next_arg_index] = *src_const_val; + next_arg_index += 1; + } + eval_fn_args(&efr, fn, args, out_val); + + if (efr.exceeded_quota_node) { + ErrorMsg *msg = add_node_error(g, fn->fn_def_node, + buf_sprintf("function evaluation exceeded %d branches", efr.branch_quota)); + + add_error_note(g, msg, efr.call_node, buf_sprintf("called from here")); + add_error_note(g, msg, efr.exceeded_quota_node, buf_sprintf("quota exceeded here")); + return true; + } + + return false; +} + diff --git a/src/eval.hpp b/src/eval.hpp new file mode 100644 index 000000000..4a8d29450 --- /dev/null +++ b/src/eval.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_EVAL_HPP +#define ZIG_EVAL_HPP + +#include "all_types.hpp" + +bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_val, int branch_quota, + AstNode *struct_node); + +bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry); +void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, + BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val); + +void eval_const_expr_implicit_cast(CastOp cast_op, + ConstExprValue *other_val, TypeTableEntry *other_type, + ConstExprValue *const_val); + +#endif From fa605485ea94ae9a59c8c1c0d66ee263e0bfa722 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Apr 2016 17:29:59 -0700 Subject: [PATCH 2/9] eval: support more node types --- src/all_types.hpp | 3 +- src/codegen.cpp | 11 +-- src/eval.cpp | 212 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 212 insertions(+), 14 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 45691fcb2..148356d9b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -497,8 +497,8 @@ struct AstNodeWhileExpr { }; struct AstNodeForExpr { - AstNode *elem_node; // always a symbol AstNode *array_expr; + AstNode *elem_node; // always a symbol AstNode *index_node; // always a symbol, might be null AstNode *body; @@ -1054,6 +1054,7 @@ struct EvalFnRoot { int branch_quota; int branches_used; AstNode *exceeded_quota_node; + bool abort; }; struct EvalFn { diff --git a/src/codegen.cpp b/src/codegen.cpp index a0fdc7b74..b810be0c0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1074,15 +1074,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lva AstNode *struct_expr = node->data.field_access_expr.struct_expr; TypeTableEntry *struct_type = get_expr_type(struct_expr); - Buf *name = &node->data.field_access_expr.field_name; if (struct_type->id == TypeTableEntryIdArray) { - if (buf_eql_str(name, "len")) { - return LLVMConstInt(g->builtin_types.entry_isize->type_ref, - struct_type->data.array.len, false); - } else { - zig_panic("gen_field_access_expr bad array field"); - } + Buf *name = &node->data.field_access_expr.field_name; + assert(buf_eql_str(name, "len")); + return LLVMConstInt(g->builtin_types.entry_isize->type_ref, + struct_type->data.array.len, false); } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer && struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)) { diff --git a/src/eval.cpp b/src/eval.cpp index 08a858ab2..0b472f192 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -327,6 +327,13 @@ static bool eval_container_init_expr(EvalFn *ef, AstNode *node, ConstExprValue * } } else if (container_type->id == TypeTableEntryIdVoid) { return false; + } else if (container_type->id == TypeTableEntryIdUnreachable) { + ef->root->abort = true; + ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node, + buf_sprintf("function evaluation reached unreachable expression")); + add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); + add_error_note(ef->root->codegen, msg, node, buf_sprintf("unreachable expression here")); + return true; } else { zig_panic("TODO"); } @@ -472,6 +479,194 @@ static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val return false; } +static bool eval_field_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeFieldAccessExpr); + + AstNode *struct_expr = node->data.field_access_expr.struct_expr; + TypeTableEntry *struct_type = get_resolved_expr(struct_expr)->type_entry; + + if (struct_type->id == TypeTableEntryIdArray) { + Buf *name = &node->data.field_access_expr.field_name; + assert(buf_eql_str(name, "len")); + zig_panic("TODO"); + } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer && + struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)) + { + TypeStructField *tsf = node->data.field_access_expr.type_struct_field; + assert(tsf); + if (struct_type->id == TypeTableEntryIdStruct) { + ConstExprValue struct_val = {0}; + if (eval_expr(ef, struct_expr, &struct_val)) return true; + ConstExprValue *field_value = struct_val.data.x_struct.fields[tsf->src_index]; + *out_val = *field_value; + } else { + zig_panic("TODO"); + } + } else if (struct_type->id == TypeTableEntryIdMetaType) { + zig_panic("TODO"); + } else if (struct_type->id == TypeTableEntryIdNamespace) { + zig_panic("TODO"); + } else { + zig_unreachable(); + } + + return false; +} + +static bool eval_for_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeForExpr); + + AstNode *array_node = node->data.for_expr.array_expr; + AstNode *elem_node = node->data.for_expr.elem_node; + AstNode *index_node = node->data.for_expr.index_node; + AstNode *body_node = node->data.for_expr.body; + + TypeTableEntry *array_type = get_resolved_expr(array_node)->type_entry; + + ConstExprValue array_val = {0}; + if (eval_expr(ef, array_node, &array_val)) return true; + + assert(elem_node->type == NodeTypeSymbol); + Buf *elem_var_name = &elem_node->data.symbol_expr.symbol; + + Buf *index_var_name = nullptr; + if (index_node) { + assert(index_node->type == NodeTypeSymbol); + index_var_name = &index_node->data.symbol_expr.symbol; + } + + uint64_t it_index = 0; + uint64_t array_len; + ConstExprValue **array_ptr_val; + if (array_type->id == TypeTableEntryIdArray) { + array_len = array_type->data.array.len; + array_ptr_val = array_val.data.x_array.fields; + } else if (array_type->id == TypeTableEntryIdStruct) { + ConstExprValue *len_field_val = array_val.data.x_struct.fields[1]; + array_len = len_field_val->data.x_bignum.data.x_uint; + array_ptr_val = array_val.data.x_struct.fields[0]->data.x_ptr.ptr; + } else { + zig_unreachable(); + } + + EvalScope *my_scope = allocate(1); + my_scope->block_context = body_node->block_context; + ef->scope_stack.append(my_scope); + + for (; it_index < array_len; it_index += 1) { + my_scope->vars.resize(0); + + if (index_var_name) { + my_scope->vars.add_one(); + EvalVar *index_var = &my_scope->vars.last(); + index_var->name = index_var_name; + memset(&index_var->value, 0, sizeof(ConstExprValue)); + index_var->value.ok = true; + bignum_init_unsigned(&index_var->value.data.x_bignum, it_index); + } + { + my_scope->vars.add_one(); + EvalVar *elem_var = &my_scope->vars.last(); + elem_var->name = elem_var_name; + elem_var->value = *array_ptr_val[it_index]; + } + + ConstExprValue body_val = {0}; + if (eval_expr(ef, body_node, &body_val)) return true; + + ef->root->branches_used += 1; + } + + ef->scope_stack.pop(); + + return false; +} + +static bool eval_array_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeArrayAccessExpr); + + AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; + AstNode *index_node = node->data.array_access_expr.subscript; + + TypeTableEntry *array_type = get_resolved_expr(array_ref_node)->type_entry; + + ConstExprValue array_val = {0}; + if (eval_expr(ef, array_ref_node, &array_val)) return true; + + ConstExprValue index_val = {0}; + if (eval_expr(ef, index_node, &index_val)) return true; + uint64_t index_int = index_val.data.x_bignum.data.x_uint; + + if (array_type->id == TypeTableEntryIdPointer) { + if (index_int >= array_val.data.x_ptr.len) { + zig_panic("TODO"); + } + *out_val = *array_val.data.x_ptr.ptr[index_int]; + } else if (array_type->id == TypeTableEntryIdStruct) { + assert(array_type->data.structure.is_unknown_size_array); + + ConstExprValue *len_value = array_val.data.x_struct.fields[1]; + uint64_t len_int = len_value->data.x_bignum.data.x_uint; + if (index_int >= len_int) { + zig_panic("TODO"); + } + + ConstExprValue *ptr_value = array_val.data.x_struct.fields[0]; + *out_val = *ptr_value->data.x_ptr.ptr[index_int]; + } else if (array_type->id == TypeTableEntryIdArray) { + uint64_t array_len = array_type->data.array.len; + if (index_int >= array_len) { + zig_panic("TODO"); + } + *out_val = *array_val.data.x_array.fields[index_int]; + } else { + zig_unreachable(); + } + + return false; +} + +static bool eval_bool_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeBoolLiteral); + + out_val->ok = true; + out_val->deep_const = true; + out_val->data.x_bool = node->data.bool_literal.value; + + return false; +} + +static bool eval_prefix_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypePrefixOpExpr); + + PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; + + ConstExprValue expr_val = {0}; + if (eval_expr(ef, node->data.prefix_op_expr.primary_expr, &expr_val)) return true; + + switch (prefix_op) { + case PrefixOpBoolNot: + *out_val = expr_val; + out_val->data.x_bool = !out_val->data.x_bool; + break; + case PrefixOpBinNot: + case PrefixOpNegation: + case PrefixOpAddressOf: + case PrefixOpConstAddressOf: + case PrefixOpDereference: + case PrefixOpMaybe: + case PrefixOpError: + case PrefixOpUnwrapError: + case PrefixOpUnwrapMaybe: + zig_panic("TODO"); + case PrefixOpInvalid: + zig_unreachable(); + } + + return false; +} + + static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { if (ef->root->branches_used > ef->root->branch_quota) { ef->root->exceeded_quota_node = node; @@ -492,6 +687,16 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { return eval_if_bool_expr(ef, node, out); case NodeTypeFnCallExpr: return eval_fn_call_expr(ef, node, out); + case NodeTypeFieldAccessExpr: + return eval_field_access_expr(ef, node, out); + case NodeTypeForExpr: + return eval_for_expr(ef, node, out); + case NodeTypeArrayAccessExpr: + return eval_array_access_expr(ef, node, out); + case NodeTypeBoolLiteral: + return eval_bool_literal_expr(ef, node, out); + case NodeTypePrefixOpExpr: + return eval_prefix_op_expr(ef, node, out); case NodeTypeRoot: case NodeTypeFnProto: case NodeTypeFnDef: @@ -506,17 +711,12 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: - case NodeTypePrefixOpExpr: - case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: - case NodeTypeFieldAccessExpr: case NodeTypeUse: - case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: - case NodeTypeForExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: @@ -602,6 +802,6 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va return true; } - return false; + return efr.abort; } From aa89fd3b3e4522fe9199049a4fcc6bdc69f4bfde Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Apr 2016 22:41:26 -0700 Subject: [PATCH 3/9] eval: ability to eval more things --- src/all_types.hpp | 2 +- src/analyze.cpp | 135 +++++++--------------- src/bignum.cpp | 5 + src/bignum.hpp | 2 + src/codegen.cpp | 19 ++++ src/eval.cpp | 284 +++++++++++++++++++++++++++++++++++++++++++--- src/eval.hpp | 4 +- 7 files changed, 337 insertions(+), 114 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 148356d9b..c6e0dc3d1 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -65,7 +65,6 @@ struct ConstExprValue { bool ok; // true if constant expression evalution worked bool depends_on_compile_var; bool undef; - bool deep_const; union { BigNum x_bignum; @@ -961,6 +960,7 @@ struct TypeTableEntry { LLVMZigDIType *di_type; bool zero_bits; + bool deep_const; union { TypeTableEntryPointer pointer; diff --git a/src/analyze.cpp b/src/analyze.cpp index 375076109..7420dbe68 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -207,6 +207,7 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { static TypeTableEntry *get_generic_fn_type(CodeGen *g, AstNode *decl_node) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdGenericFn); buf_init_from_str(&entry->name, "(generic function)"); + entry->deep_const = true; entry->zero_bits = true; entry->data.generic_fn.decl_node = decl_node; return entry; @@ -220,6 +221,8 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer); + entry->deep_const = is_const && child_type->deep_const; + const char *const_str = is_const ? "const " : ""; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name)); @@ -261,6 +264,8 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { assert(child_type->type_ref); assert(child_type->di_type); + entry->deep_const = child_type->deep_const; + buf_resize(&entry->name, 0); buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); @@ -344,6 +349,8 @@ static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { entry->data.error.child_type = child_type; + entry->deep_const = child_type->deep_const; + if (!type_has_bits(child_type)) { entry->type_ref = g->err_tag_type->type_ref; entry->di_type = g->err_tag_type->di_type; @@ -415,6 +422,7 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray); entry->type_ref = LLVMArrayType(child_type->type_ref, array_size); entry->zero_bits = (array_size == 0) || child_type->zero_bits; + entry->deep_const = child_type->deep_const; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name)); @@ -465,6 +473,8 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c TypeTableEntry *var_peer = get_slice_type(g, child_type, false); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); + entry->deep_const = child_type->deep_const; + buf_resize(&entry->name, 0); buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name)); @@ -550,6 +560,7 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry * buf_init_from_str(&entry->name, name); + entry->deep_const = child_type->deep_const; entry->type_ref = child_type->type_ref; entry->di_type = child_type->di_type; entry->zero_bits = child_type->zero_bits; @@ -573,6 +584,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { } TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); + fn_type->deep_const = true; fn_type->data.fn.fn_type_id = *fn_type_id; if (fn_type_id->param_info == &fn_type_id->prealloc_param_info[0]) { fn_type->data.fn.fn_type_id.param_info = &fn_type->data.fn.fn_type_id.prealloc_param_info[0]; @@ -1038,6 +1050,8 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt assert(enum_type->di_type); + enum_type->deep_const = true; + uint32_t field_count = decl_node->data.struct_decl.fields.length; enum_type->data.enumeration.field_count = field_count; @@ -1065,6 +1079,10 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt type_enum_field->type_entry = field_type; type_enum_field->value = i; + if (!field_type->deep_const) { + enum_type->deep_const = false; + } + di_enumerators[i] = LLVMZigCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i); @@ -1225,6 +1243,8 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE assert(struct_type->di_type); + struct_type->deep_const = true; + int field_count = decl_node->data.struct_decl.fields.length; struct_type->data.structure.src_field_count = field_count; @@ -1248,6 +1268,10 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE type_struct_field->src_index = i; type_struct_field->gen_index = -1; + if (!field_type->deep_const) { + struct_type->deep_const = false; + } + if (field_type->id == TypeTableEntryIdStruct) { resolve_struct_type(g, import, field_type); } else if (field_type->id == TypeTableEntryIdEnum) { @@ -2486,7 +2510,6 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; - expr->const_val.deep_const = true; return g->builtin_types.entry_void; } @@ -2494,7 +2517,6 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_type = type; - expr->const_val.deep_const = true; return g->builtin_types.entry_type; } @@ -2509,7 +2531,6 @@ static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, F Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_fn = fn; - expr->const_val.deep_const = true; return fn->type_entry; } @@ -2519,7 +2540,6 @@ static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_type = type_entry; - expr->const_val.deep_const = true; return type_entry; } @@ -2527,7 +2547,6 @@ static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_err.err = err; - expr->const_val.deep_const = true; return g->builtin_types.entry_pure_error; } @@ -2538,7 +2557,6 @@ static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, expr->const_val.ok = true; expr->const_val.depends_on_compile_var = depends_on_compile_var; expr->const_val.data.x_bool = value; - expr->const_val.deep_const = true; return g->builtin_types.entry_bool; } @@ -2546,7 +2564,6 @@ static TypeTableEntry *resolve_expr_const_val_as_null(CodeGen *g, AstNode *node, Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_maybe = nullptr; - expr->const_val.deep_const = true; return type; } @@ -2557,14 +2574,12 @@ static TypeTableEntry *resolve_expr_const_val_as_non_null(CodeGen *g, AstNode *n Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_maybe = other_val; - expr->const_val.deep_const = other_val->deep_const; return type; } static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNode *node, Buf *str) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; - expr->const_val.deep_const = true; int len_with_null = buf_len(str) + 1; expr->const_val.data.x_ptr.ptr = allocate(len_with_null); @@ -2574,14 +2589,12 @@ static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNod for (int i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &all_chars[i]; this_char->ok = true; - this_char->deep_const = true; bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]); expr->const_val.data.x_ptr.ptr[i] = this_char; } ConstExprValue *null_char = &all_chars[len_with_null - 1]; null_char->ok = true; - null_char->deep_const = true; bignum_init_unsigned(&null_char->data.x_bignum, 0); expr->const_val.data.x_ptr.ptr[len_with_null - 1] = null_char; @@ -2591,14 +2604,12 @@ static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNod static TypeTableEntry *resolve_expr_const_val_as_string_lit(CodeGen *g, AstNode *node, Buf *str) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; - expr->const_val.deep_const = true; expr->const_val.data.x_array.fields = allocate(buf_len(str)); ConstExprValue *all_chars = allocate(buf_len(str)); for (int i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &all_chars[i]; this_char->ok = true; - this_char->deep_const = true; bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]); expr->const_val.data.x_array.fields[i] = this_char; } @@ -2611,7 +2622,6 @@ 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.deep_const = true; bignum_init_unsigned(&expr->const_val.data.x_bignum, x); @@ -2623,7 +2633,6 @@ static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNo { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; - expr->const_val.deep_const = true; bignum_init_float(&expr->const_val.data.x_bignum, x); @@ -2639,7 +2648,6 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode * ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val; const_val->ok = true; - const_val->deep_const = true; if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { add_node_error(g, node, @@ -2713,12 +2721,7 @@ static bool var_is_pure(VariableTableEntry *var, TypeTableEntry *var_type, Block // variable was declared in the current function, so it's OK. return true; } - if (!var->is_const) { - return false; - } - - ConstExprValue *const_val = &get_resolved_expr(var->val_node)->const_val; - return const_val->deep_const; + return var->is_const && var->type->deep_const; } static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -3714,17 +3717,6 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, node, then_node, else_node, cond_is_const, cond_bool_val); } -static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) { - assert(int_type->id == TypeTableEntryIdInt); - - for (int i = 0; i < CIntTypeCount; i += 1) { - if (int_type == g->builtin_types.entry_c_int[i]) { - return true; - } - } - return false; -} - static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, const char *err_format, bool is_max) { @@ -3733,67 +3725,15 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor AstNode *type_node = node->data.fn_call_expr.params.at(0); TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); + if (type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; - } else if (type_entry->id == TypeTableEntryIdInt) { - ConstExprValue *const_val = &get_resolved_expr(node)->const_val; - const_val->ok = true; - const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry); - if (is_max) { - if (type_entry->data.integral.is_signed) { - int64_t val; - if (type_entry->data.integral.bit_count == 64) { - val = INT64_MAX; - } else if (type_entry->data.integral.bit_count == 32) { - val = INT32_MAX; - } else if (type_entry->data.integral.bit_count == 16) { - val = INT16_MAX; - } else if (type_entry->data.integral.bit_count == 8) { - val = INT8_MAX; - } else { - zig_unreachable(); - } - bignum_init_signed(&const_val->data.x_bignum, val); - } else { - uint64_t val; - if (type_entry->data.integral.bit_count == 64) { - val = UINT64_MAX; - } else if (type_entry->data.integral.bit_count == 32) { - val = UINT32_MAX; - } else if (type_entry->data.integral.bit_count == 16) { - val = UINT16_MAX; - } else if (type_entry->data.integral.bit_count == 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->data.integral.bit_count == 64) { - val = INT64_MIN; - } else if (type_entry->data.integral.bit_count == 32) { - val = INT32_MIN; - } else if (type_entry->data.integral.bit_count == 16) { - val = INT16_MIN; - } else if (type_entry->data.integral.bit_count == 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); - } - } + } else if (type_entry->id == TypeTableEntryIdInt || + type_entry->id == TypeTableEntryIdFloat || + type_entry->id == TypeTableEntryIdBool) + { + eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max); return type_entry; - } else if (type_entry->id == TypeTableEntryIdFloat) { - 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, false); } else { add_node_error(g, node, buf_sprintf(err_format, buf_ptr(&type_entry->name))); @@ -3810,7 +3750,8 @@ static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode * TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry; ConstExprValue *const_val = &get_resolved_expr(node)->const_val; if (other_val->ok) { - eval_const_expr_implicit_cast(node->data.fn_call_expr.cast_op, other_val, other_type, const_val); + eval_const_expr_implicit_cast(node->data.fn_call_expr.cast_op, other_val, other_type, + const_val, wanted_type); } if (need_alloca) { @@ -4238,8 +4179,6 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry } case BuiltinFnIdMemcpy: { - mark_impure_fn(context); - AstNode *dest_node = node->data.fn_call_expr.params.at(0); AstNode *src_node = node->data.fn_call_expr.params.at(1); AstNode *len_node = node->data.fn_call_expr.params.at(2); @@ -4278,8 +4217,6 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry } case BuiltinFnIdMemset: { - mark_impure_fn(context); - AstNode *dest_node = node->data.fn_call_expr.params.at(0); AstNode *char_node = node->data.fn_call_expr.params.at(1); AstNode *len_node = node->data.fn_call_expr.params.at(2); @@ -4601,6 +4538,8 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, // calling an impure fn is impure mark_impure_fn(context); } + } else { + mark_impure_fn(context); } if (handle_is_ptr(return_type)) { @@ -5602,6 +5541,10 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { param_decl_node->data.param_decl.variable = var; var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; + + if (!type->deep_const) { + fn_table_entry->is_pure = false; + } } TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.return_type; diff --git a/src/bignum.cpp b/src/bignum.cpp index 55c10e248..7046ff487 100644 --- a/src/bignum.cpp +++ b/src/bignum.cpp @@ -71,6 +71,11 @@ bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed) { } } +void bignum_truncate(BigNum *bn, int bit_count) { + assert(bn->kind == BigNumKindInt); + bn->data.x_uint &= (1LL << bit_count) - 1; +} + uint64_t bignum_to_twos_complement(BigNum *bn) { assert(bn->kind == BigNumKindInt); diff --git a/src/bignum.hpp b/src/bignum.hpp index 047169085..ac1f75e79 100644 --- a/src/bignum.hpp +++ b/src/bignum.hpp @@ -47,6 +47,8 @@ void bignum_negate(BigNum *dest, BigNum *op); void bignum_cast_to_float(BigNum *dest, BigNum *op); void bignum_cast_to_int(BigNum *dest, BigNum *op); +void bignum_truncate(BigNum *dest, int bit_count); + // returns the result of the comparison bool bignum_cmp_eq(BigNum *op1, BigNum *op2); bool bignum_cmp_neq(BigNum *op1, BigNum *op2); diff --git a/src/codegen.cpp b/src/codegen.cpp index b810be0c0..4a1ea84a0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3463,23 +3463,27 @@ static void define_builtin_types(CodeGen *g) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNamespace); buf_init_from_str(&entry->name, "(namespace)"); entry->zero_bits = true; + entry->deep_const = true; g->builtin_types.entry_namespace = entry; } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat); buf_init_from_str(&entry->name, "(float literal)"); entry->zero_bits = true; + entry->deep_const = true; g->builtin_types.entry_num_lit_float = entry; } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt); buf_init_from_str(&entry->name, "(integer literal)"); entry->zero_bits = true; + entry->deep_const = true; g->builtin_types.entry_num_lit_int = entry; } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit); buf_init_from_str(&entry->name, "(undefined)"); + entry->deep_const = true; g->builtin_types.entry_undef = entry; } @@ -3489,6 +3493,7 @@ static void define_builtin_types(CodeGen *g) { for (;;) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->type_ref = LLVMIntType(size_in_bits); + entry->deep_const = true; const char u_or_i = is_signed ? 'i' : 'u'; buf_resize(&entry->name, 0); @@ -3534,6 +3539,7 @@ static void define_builtin_types(CodeGen *g) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->type_ref = LLVMIntType(size_in_bits); + entry->deep_const = true; buf_init_from_str(&entry->name, info->name); @@ -3553,6 +3559,7 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool); entry->type_ref = LLVMInt1Type(); + entry->deep_const = true; buf_init_from_str(&entry->name, "bool"); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); @@ -3565,6 +3572,7 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); + entry->deep_const = true; entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); buf_init_from_str(&entry->name, "isize"); entry->data.integral.is_signed = true; @@ -3581,6 +3589,7 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); + entry->deep_const = true; entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); buf_init_from_str(&entry->name, "usize"); entry->data.integral.is_signed = false; @@ -3597,6 +3606,7 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); + entry->deep_const = true; entry->type_ref = LLVMFloatType(); buf_init_from_str(&entry->name, "f32"); entry->data.floating.bit_count = 32; @@ -3612,6 +3622,7 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); + entry->deep_const = true; entry->type_ref = LLVMDoubleType(); buf_init_from_str(&entry->name, "f64"); entry->data.floating.bit_count = 64; @@ -3627,6 +3638,7 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); + entry->deep_const = true; entry->type_ref = LLVMX86FP80Type(); buf_init_from_str(&entry->name, "c_long_double"); entry->data.floating.bit_count = 80; @@ -3642,6 +3654,7 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); + entry->deep_const = true; entry->type_ref = LLVMVoidType(); entry->zero_bits = true; buf_init_from_str(&entry->name, "void"); @@ -3654,6 +3667,7 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUnreachable); + entry->deep_const = true; entry->type_ref = LLVMVoidType(); entry->zero_bits = true; buf_init_from_str(&entry->name, "unreachable"); @@ -3663,6 +3677,7 @@ static void define_builtin_types(CodeGen *g) { } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMetaType); + entry->deep_const = true; buf_init_from_str(&entry->name, "type"); entry->zero_bits = true; g->builtin_types.entry_type = entry; @@ -3685,6 +3700,7 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError); + entry->deep_const = true; buf_init_from_str(&entry->name, "error"); // TODO allow overriding this type and keep track of max value and emit an @@ -3700,6 +3716,7 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); + entry->deep_const = true; entry->zero_bits = true; // only allowed at compile time buf_init_from_str(&entry->name, "@OS"); uint32_t field_count = target_os_count(); @@ -3725,6 +3742,7 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); + entry->deep_const = true; entry->zero_bits = true; // only allowed at compile time buf_init_from_str(&entry->name, "@Arch"); uint32_t field_count = target_arch_count(); @@ -3756,6 +3774,7 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); + entry->deep_const = true; entry->zero_bits = true; // only allowed at compile time buf_init_from_str(&entry->name, "@Environ"); uint32_t field_count = target_environ_count(); diff --git a/src/eval.cpp b/src/eval.cpp index 0b472f192..41263bfcb 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -360,7 +360,7 @@ static bool eval_if_bool_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val void eval_const_expr_implicit_cast(CastOp cast_op, ConstExprValue *other_val, TypeTableEntry *other_type, - ConstExprValue *const_val) + ConstExprValue *const_val, TypeTableEntry *new_type) { const_val->depends_on_compile_var = other_val->depends_on_compile_var; const_val->undef = other_val->undef; @@ -371,9 +371,30 @@ void eval_const_expr_implicit_cast(CastOp cast_op, zig_unreachable(); case CastOpNoop: case CastOpWidenOrShorten: - case CastOpPointerReinterpret: *const_val = *other_val; break; + case CastOpPointerReinterpret: + { + TypeTableEntry *other_child_type = other_type->data.pointer.child_type; + TypeTableEntry *new_child_type = new_type->data.pointer.child_type; + + if ((other_child_type->id == TypeTableEntryIdInt || + other_child_type->id == TypeTableEntryIdFloat) && + (new_child_type->id == TypeTableEntryIdInt || + new_child_type->id == TypeTableEntryIdFloat)) + { + ConstExprValue **ptr_val = allocate(1); + *ptr_val = other_val->data.x_ptr.ptr[0]; + const_val->data.x_ptr.ptr = ptr_val; + const_val->data.x_ptr.len = 1; + const_val->ok = true; + const_val->undef = other_val->undef; + const_val->depends_on_compile_var = other_val->depends_on_compile_var; + } else { + zig_panic("TODO"); + } + break; + } case CastOpPtrToInt: case CastOpIntToPtr: // can't do it @@ -435,20 +456,184 @@ void eval_const_expr_implicit_cast(CastOp cast_op, } } +static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) { + assert(int_type->id == TypeTableEntryIdInt); + + for (int i = 0; i < CIntTypeCount; i += 1) { + if (int_type == g->builtin_types.entry_c_int[i]) { + return true; + } + } + return false; +} + +void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max) { + if (type_entry->id == TypeTableEntryIdInt) { + const_val->ok = true; + const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry); + if (is_max) { + if (type_entry->data.integral.is_signed) { + int64_t val; + if (type_entry->data.integral.bit_count == 64) { + val = INT64_MAX; + } else if (type_entry->data.integral.bit_count == 32) { + val = INT32_MAX; + } else if (type_entry->data.integral.bit_count == 16) { + val = INT16_MAX; + } else if (type_entry->data.integral.bit_count == 8) { + val = INT8_MAX; + } else { + zig_unreachable(); + } + bignum_init_signed(&const_val->data.x_bignum, val); + } else { + uint64_t val; + if (type_entry->data.integral.bit_count == 64) { + val = UINT64_MAX; + } else if (type_entry->data.integral.bit_count == 32) { + val = UINT32_MAX; + } else if (type_entry->data.integral.bit_count == 16) { + val = UINT16_MAX; + } else if (type_entry->data.integral.bit_count == 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->data.integral.bit_count == 64) { + val = INT64_MIN; + } else if (type_entry->data.integral.bit_count == 32) { + val = INT32_MIN; + } else if (type_entry->data.integral.bit_count == 16) { + val = INT16_MIN; + } else if (type_entry->data.integral.bit_count == 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); + } + } + } else if (type_entry->id == TypeTableEntryIdFloat) { + zig_panic("TODO analyze_min_max_value float"); + } else if (type_entry->id == TypeTableEntryIdBool) { + const_val->ok = true; + const_val->data.x_bool = is_max; + } else { + zig_unreachable(); + } +} + +static bool eval_min_max(EvalFn *ef, AstNode *node, ConstExprValue *out_val, bool is_max) { + assert(node->type == NodeTypeFnCallExpr); + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *type_entry = resolve_expr_type(type_node); + eval_min_max_value(ef->root->codegen, type_entry, out_val, is_max); + return false; +} + +static bool eval_fn_with_overflow(EvalFn *ef, AstNode *node, ConstExprValue *out_val, + bool (*bignum_fn)(BigNum *dest, BigNum *op1, BigNum *op2)) +{ + assert(node->type == NodeTypeFnCallExpr); + + AstNode *type_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *int_type = resolve_expr_type(type_node); + assert(int_type->id == TypeTableEntryIdInt); + + AstNode *op1_node = node->data.fn_call_expr.params.at(1); + AstNode *op2_node = node->data.fn_call_expr.params.at(2); + AstNode *result_node = node->data.fn_call_expr.params.at(3); + + ConstExprValue op1_val = {0}; + if (eval_expr(ef, op1_node, &op1_val)) return true; + + ConstExprValue op2_val = {0}; + if (eval_expr(ef, op2_node, &op2_val)) return true; + + ConstExprValue result_ptr_val = {0}; + if (eval_expr(ef, result_node, &result_ptr_val)) return true; + + ConstExprValue *result_val = result_ptr_val.data.x_ptr.ptr[0]; + + out_val->ok = true; + bool overflow = bignum_fn(&result_val->data.x_bignum, &op1_val.data.x_bignum, &op2_val.data.x_bignum); + + overflow = overflow || !bignum_fits_in_bits(&result_val->data.x_bignum, + int_type->data.integral.bit_count, int_type->data.integral.is_signed); + + out_val->data.x_bool = overflow; + + if (overflow) { + bignum_truncate(&result_val->data.x_bignum, int_type->data.integral.bit_count); + } + + return false; +} + +static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeFnCallExpr); + + BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn; + switch (builtin_fn->id) { + case BuiltinFnIdMaxValue: + return eval_min_max(ef, node, out_val, true); + case BuiltinFnIdMinValue: + return eval_min_max(ef, node, out_val, false); + case BuiltinFnIdMulWithOverflow: + return eval_fn_with_overflow(ef, node, out_val, bignum_mul); + case BuiltinFnIdAddWithOverflow: + return eval_fn_with_overflow(ef, node, out_val, bignum_add); + case BuiltinFnIdSubWithOverflow: + return eval_fn_with_overflow(ef, node, out_val, bignum_sub); + case BuiltinFnIdMemcpy: + case BuiltinFnIdMemset: + case BuiltinFnIdSizeof: + case BuiltinFnIdAlignof: + case BuiltinFnIdMemberCount: + case BuiltinFnIdTypeof: + case BuiltinFnIdCInclude: + case BuiltinFnIdCDefine: + case BuiltinFnIdCUndef: + case BuiltinFnIdCompileVar: + case BuiltinFnIdConstEval: + case BuiltinFnIdCtz: + case BuiltinFnIdClz: + case BuiltinFnIdImport: + case BuiltinFnIdCImport: + case BuiltinFnIdErrName: + zig_panic("TODO"); + case BuiltinFnIdBreakpoint: + case BuiltinFnIdInvalid: + zig_unreachable(); + } + + return false; +} + static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { assert(node->type == NodeTypeFnCallExpr); + AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; CastOp cast_op = node->data.fn_call_expr.cast_op; if (node->data.fn_call_expr.is_builtin) { - zig_panic("TODO"); + return eval_fn_call_builtin(ef, node, out_val); } else if (cast_op != CastOpNoCast) { - AstNode *expr_node = node->data.fn_call_expr.params.at(0); - Expr *expr = get_resolved_expr(expr_node); - eval_const_expr_implicit_cast(cast_op, &expr->const_val, expr->type_entry, out_val); + TypeTableEntry *new_type = resolve_expr_type(fn_ref_expr); + AstNode *param_node = node->data.fn_call_expr.params.at(0); + TypeTableEntry *old_type = get_resolved_expr(param_node)->type_entry; + ConstExprValue param_val = {0}; + if (eval_expr(ef, param_node, ¶m_val)) return true; + eval_const_expr_implicit_cast(cast_op, ¶m_val, old_type, out_val, new_type); return false; } - AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; if (node->data.fn_call_expr.enum_type) { zig_panic("TODO"); } @@ -503,7 +688,12 @@ static bool eval_field_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou zig_panic("TODO"); } } else if (struct_type->id == TypeTableEntryIdMetaType) { - zig_panic("TODO"); + TypeTableEntry *child_type = resolve_expr_type(struct_expr); + if (child_type->id == TypeTableEntryIdPureError) { + *out_val = get_resolved_expr(node)->const_val; + } else { + zig_panic("TODO"); + } } else if (struct_type->id == TypeTableEntryIdNamespace) { zig_panic("TODO"); } else { @@ -630,7 +820,6 @@ static bool eval_bool_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou assert(node->type == NodeTypeBoolLiteral); out_val->ok = true; - out_val->deep_const = true; out_val->data.x_bool = node->data.bool_literal.value; return false; @@ -640,20 +829,38 @@ static bool eval_prefix_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_v assert(node->type == NodeTypePrefixOpExpr); PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; ConstExprValue expr_val = {0}; - if (eval_expr(ef, node->data.prefix_op_expr.primary_expr, &expr_val)) return true; + if (eval_expr(ef, expr_node, &expr_val)) return true; + + TypeTableEntry *expr_type = get_resolved_expr(expr_node)->type_entry; switch (prefix_op) { case PrefixOpBoolNot: *out_val = expr_val; out_val->data.x_bool = !out_val->data.x_bool; break; - case PrefixOpBinNot: - case PrefixOpNegation: + case PrefixOpDereference: + assert(expr_type->id == TypeTableEntryIdPointer); + *out_val = *expr_val.data.x_ptr.ptr[0]; + break; case PrefixOpAddressOf: case PrefixOpConstAddressOf: - case PrefixOpDereference: + { + ConstExprValue *child_val = allocate(1); + *child_val = expr_val; + + ConstExprValue **ptr_val = allocate(1); + *ptr_val = child_val; + + out_val->data.x_ptr.ptr = ptr_val; + out_val->data.x_ptr.len = 1; + out_val->ok = true; + break; + } + case PrefixOpBinNot: + case PrefixOpNegation: case PrefixOpMaybe: case PrefixOpError: case PrefixOpUnwrapError: @@ -666,6 +873,48 @@ static bool eval_prefix_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_v return false; } +static bool eval_var_decl_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeVariableDeclaration); + + assert(node->data.variable_declaration.expr); + + EvalScope *my_scope = ef->scope_stack.at(ef->scope_stack.length - 1); + + my_scope->vars.add_one(); + EvalVar *var = &my_scope->vars.last(); + var->name = &node->data.variable_declaration.symbol; + + if (eval_expr(ef, node->data.variable_declaration.expr, &var->value)) return true; + + out_val->ok = true; + + return false; +} + +static bool eval_number_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeNumberLiteral); + assert(!node->data.number_literal.overflow); + + out_val->ok = true; + if (node->data.number_literal.kind == NumLitUInt) { + bignum_init_unsigned(&out_val->data.x_bignum, node->data.number_literal.data.x_uint); + } else if (node->data.number_literal.kind == NumLitFloat) { + bignum_init_float(&out_val->data.x_bignum, node->data.number_literal.data.x_float); + } else { + zig_unreachable(); + } + + return false; +} + +static bool eval_char_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeCharLiteral); + + out_val->ok = true; + bignum_init_unsigned(&out_val->data.x_bignum, node->data.char_literal.value); + + return false; +} static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { if (ef->root->branches_used > ef->root->branch_quota) { @@ -697,6 +946,12 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { return eval_bool_literal_expr(ef, node, out); case NodeTypePrefixOpExpr: return eval_prefix_op_expr(ef, node, out); + case NodeTypeVariableDeclaration: + return eval_var_decl_expr(ef, node, out); + case NodeTypeNumberLiteral: + return eval_number_literal_expr(ef, node, out); + case NodeTypeCharLiteral: + return eval_char_literal_expr(ef, node, out); case NodeTypeRoot: case NodeTypeFnProto: case NodeTypeFnDef: @@ -704,13 +959,10 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { case NodeTypeParamDecl: case NodeTypeDirective: case NodeTypeDefer: - case NodeTypeVariableDeclaration: case NodeTypeTypeDecl: case NodeTypeErrorValueDecl: case NodeTypeUnwrapErrorExpr: - case NodeTypeNumberLiteral: case NodeTypeStringLiteral: - case NodeTypeCharLiteral: case NodeTypeSliceExpr: case NodeTypeUse: case NodeTypeNullLiteral: diff --git a/src/eval.hpp b/src/eval.hpp index 4a8d29450..f8266228f 100644 --- a/src/eval.hpp +++ b/src/eval.hpp @@ -19,6 +19,8 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, void eval_const_expr_implicit_cast(CastOp cast_op, ConstExprValue *other_val, TypeTableEntry *other_type, - ConstExprValue *const_val); + ConstExprValue *const_val, TypeTableEntry *new_type); + +void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max); #endif From be4df96e4b80a3307b3661fd5ca3114478499daf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Apr 2016 13:30:52 -0700 Subject: [PATCH 4/9] passing all tests --- src/analyze.cpp | 71 ++++++++++++++++--------- src/eval.cpp | 105 +++++++++++++++++++++++++++++++----- std/rand.zig | 1 + test/run_tests.cpp | 124 ++----------------------------------------- test/self_hosted.zig | 103 ++++++++++++++++++++++++++++++++--- 5 files changed, 240 insertions(+), 164 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 7420dbe68..f7ef77c19 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -35,7 +35,8 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node, TypeTableEntry *expected_type, uint64_t x); static AstNode *find_decl(BlockContext *context, Buf *name); -static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, bool pointer_only); +static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, + bool pointer_only, BlockContext *block_context); static TopLevelDecl *get_as_top_level_decl(AstNode *node); static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, @@ -957,6 +958,19 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t add_node_error(g, directive_node, buf_sprintf("#condition valid only on exported symbols")); } + } else if (buf_eql_str(name, "static_eval_enable")) { + if (fn_table_entry->is_extern) { + add_node_error(g, directive_node, + buf_sprintf("#static_val_enable invalid on extern functions")); + } else { + bool enable; + bool ok = resolve_const_expr_bool(g, import, import->block_context, + &directive_node->data.directive.expr, &enable); + if (!enable || !ok) { + fn_table_entry->is_pure = false; + } + // TODO cause compile error if enable is true and impure fn + } } else { add_node_error(g, directive_node, buf_sprintf("invalid directive: '%s'", buf_ptr(name))); @@ -2227,9 +2241,9 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry const_val->ok = false; } } - if (!const_val->ok) { - context->fn_entry->struct_val_expr_alloca_list.append(codegen); - } + } + if (!const_val->ok) { + context->fn_entry->struct_val_expr_alloca_list.append(codegen); } for (int i = 0; i < actual_field_count; i += 1) { @@ -2381,7 +2395,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i AstNode *decl_node = entry ? entry->value : nullptr; if (decl_node) { bool pointer_only = false; - return analyze_decl_ref(g, node, decl_node, pointer_only); + return analyze_decl_ref(g, node, decl_node, pointer_only, context); } else { add_node_error(g, node, buf_sprintf("container '%s' has no member called '%s'", @@ -2408,7 +2422,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i add_error_note(g, msg, decl_node, buf_sprintf("declared here")); } bool pointer_only = false; - return analyze_decl_ref(g, node, decl_node, pointer_only); + return analyze_decl_ref(g, node, decl_node, pointer_only, context); } else { const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)"; add_node_error(g, node, @@ -2674,8 +2688,21 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry * return g->builtin_types.entry_invalid; } -static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var) { +static bool var_is_pure(VariableTableEntry *var, BlockContext *context) { + if (var->block_context->fn_entry == context->fn_entry) { + // variable was declared in the current function, so it's OK. + return true; + } + return var->is_const && var->type->deep_const; +} + +static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var, + BlockContext *context) +{ get_resolved_expr(source_node)->variable = var; + if (!var_is_pure(var, context)) { + mark_impure_fn(context); + } if (var->is_const && var->val_node) { ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val; if (other_const_val->ok) { @@ -2686,7 +2713,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl } static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, - bool pointer_only) + bool pointer_only, BlockContext *block_context) { resolve_top_level_decl(g, decl_node, pointer_only); TopLevelDecl *tld = get_as_top_level_decl(decl_node); @@ -2696,7 +2723,7 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod if (decl_node->type == NodeTypeVariableDeclaration) { VariableTableEntry *var = decl_node->data.variable_declaration.variable; - return analyze_var_ref(g, source_node, var); + return analyze_var_ref(g, source_node, var, block_context); } else if (decl_node->type == NodeTypeFnProto) { if (decl_node->data.fn_proto.generic_params.length > 0) { TypeTableEntry *type_entry = decl_node->data.fn_proto.generic_fn_type; @@ -2716,14 +2743,6 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod } } -static bool var_is_pure(VariableTableEntry *var, TypeTableEntry *var_type, BlockContext *context) { - if (var->block_context->fn_entry == context->fn_entry) { - // variable was declared in the current function, so it's OK. - return true; - } - return var->is_const && var->type->deep_const; -} - static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only) { @@ -2740,16 +2759,13 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, VariableTableEntry *var = find_variable(g, context, variable_name); if (var) { - TypeTableEntry *var_type = analyze_var_ref(g, node, var); - if (!var_is_pure(var, var_type, context)) { - mark_impure_fn(context); - } + TypeTableEntry *var_type = analyze_var_ref(g, node, var, context); return var_type; } AstNode *decl_node = find_decl(context, variable_name); if (decl_node) { - return analyze_decl_ref(g, node, decl_node, pointer_only); + return analyze_decl_ref(g, node, decl_node, pointer_only, context); } if (import->any_imports_failed) { @@ -2992,7 +3008,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, } analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); - return resolve_expr_const_val_as_void(g, node); + // not const ok because expression has side effects + return g->builtin_types.entry_void; } case BinOpTypeBoolOr: case BinOpTypeBoolAnd: @@ -4473,12 +4490,16 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, actual_param_count += 1; } + bool ok_invocation = true; + if (fn_type->data.fn.fn_type_id.is_var_args) { if (actual_param_count < src_param_count) { + ok_invocation = false; add_node_error(g, node, buf_sprintf("expected at least %d arguments, got %d", src_param_count, actual_param_count)); } } else if (src_param_count != actual_param_count) { + ok_invocation = false; add_node_error(g, node, buf_sprintf("expected %d arguments, got %d", src_param_count, actual_param_count)); } @@ -4517,7 +4538,7 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, } FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; - if (fn_table_entry && fn_table_entry->is_pure && all_args_const_expr) { + if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && all_args_const_expr) { if (fn_table_entry->anal_state == FnAnalStateReady) { analyze_fn_body(g, fn_table_entry); } else if (fn_table_entry->anal_state == FnAnalStateProbing) { @@ -4726,7 +4747,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import if (fn_ref_expr->type == NodeTypeFieldAccessExpr && fn_ref_expr->data.field_access_expr.is_member_fn) { - struct_node = fn_ref_expr; + struct_node = fn_ref_expr->data.field_access_expr.struct_expr; } else { struct_node = nullptr; } diff --git a/src/eval.cpp b/src/eval.cpp index 41263bfcb..1e386e036 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -70,6 +70,7 @@ static bool eval_block(EvalFn *ef, AstNode *node, ConstExprValue *out) { for (int i = 0; i < node->data.block.statements.length; i += 1) { AstNode *child = node->data.block.statements.at(i); + memset(out, 0, sizeof(ConstExprValue)); if (eval_expr(ef, child, out)) return true; } @@ -109,7 +110,6 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, { assert(op1_val->ok); assert(op2_val->ok); - assert(op1_type == op2_type); switch (bin_op) { case BinOpTypeAssign: @@ -275,6 +275,7 @@ static bool eval_symbol_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) Buf *name = &node->data.symbol_expr.symbol; EvalVar *var = find_var(ef, name); + assert(var); *out_val = var->value; @@ -374,6 +375,8 @@ void eval_const_expr_implicit_cast(CastOp cast_op, *const_val = *other_val; break; case CastOpPointerReinterpret: + if (other_type->id == TypeTableEntryIdPointer && + new_type->id == TypeTableEntryIdPointer) { TypeTableEntry *other_child_type = other_type->data.pointer.child_type; TypeTableEntry *new_child_type = new_type->data.pointer.child_type; @@ -393,8 +396,47 @@ void eval_const_expr_implicit_cast(CastOp cast_op, } else { zig_panic("TODO"); } - break; + } else if (other_type->id == TypeTableEntryIdMaybe && + new_type->id == TypeTableEntryIdMaybe) + { + if (!other_val->data.x_maybe) { + *const_val = *other_val; + break; + } + + TypeTableEntry *other_ptr_type = other_type->data.maybe.child_type; + TypeTableEntry *new_ptr_type = new_type->data.maybe.child_type; + + if (other_ptr_type->id == TypeTableEntryIdPointer && + new_ptr_type->id == TypeTableEntryIdPointer) + { + TypeTableEntry *other_child_type = other_ptr_type->data.pointer.child_type; + TypeTableEntry *new_child_type = new_ptr_type->data.pointer.child_type; + + if ((other_child_type->id == TypeTableEntryIdInt || + other_child_type->id == TypeTableEntryIdFloat) && + (new_child_type->id == TypeTableEntryIdInt || + new_child_type->id == TypeTableEntryIdFloat)) + { + ConstExprValue *ptr_parent = allocate(1); + ConstExprValue **ptr_val = allocate(1); + *ptr_val = other_val->data.x_maybe->data.x_ptr.ptr[0]; + ptr_parent->data.x_ptr.ptr = ptr_val; + ptr_parent->data.x_ptr.len = 1; + ptr_parent->ok = true; + + const_val->data.x_maybe = ptr_parent; + const_val->ok = true; + const_val->undef = other_val->undef; + const_val->depends_on_compile_var = other_val->depends_on_compile_var; + } else { + zig_panic("TODO"); + } + } else { + zig_panic("TODO"); + } } + break; case CastOpPtrToInt: case CastOpIntToPtr: // can't do it @@ -684,6 +726,7 @@ static bool eval_field_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou if (eval_expr(ef, struct_expr, &struct_val)) return true; ConstExprValue *field_value = struct_val.data.x_struct.fields[tsf->src_index]; *out_val = *field_value; + assert(out_val->ok); } else { zig_panic("TODO"); } @@ -916,11 +959,45 @@ static bool eval_char_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou return false; } +static bool eval_while_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { + assert(node->type == NodeTypeWhileExpr); + + AstNode *cond_node = node->data.while_expr.condition; + AstNode *body_node = node->data.while_expr.body; + + EvalScope *my_scope = allocate(1); + my_scope->block_context = body_node->block_context; + ef->scope_stack.append(my_scope); + + for (;;) { + my_scope->vars.resize(0); + + ConstExprValue cond_val = {0}; + if (eval_expr(ef, cond_node, &cond_val)) return true; + + if (!cond_val.data.x_bool) break; + + ConstExprValue body_val = {0}; + if (eval_expr(ef, body_node, &body_val)) return true; + + ef->root->branches_used += 1; + } + + ef->scope_stack.pop(); + + return false; +} + static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { if (ef->root->branches_used > ef->root->branch_quota) { ef->root->exceeded_quota_node = node; return true; } + ConstExprValue *const_val = &get_resolved_expr(node)->const_val; + if (const_val->ok) { + *out = *const_val; + return false; + } switch (node->type) { case NodeTypeBlock: return eval_block(ef, node, out); @@ -952,23 +1029,16 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { return eval_number_literal_expr(ef, node, out); case NodeTypeCharLiteral: return eval_char_literal_expr(ef, node, out); - case NodeTypeRoot: - case NodeTypeFnProto: - case NodeTypeFnDef: - case NodeTypeFnDecl: - case NodeTypeParamDecl: - case NodeTypeDirective: + case NodeTypeWhileExpr: + return eval_while_expr(ef, node, out); case NodeTypeDefer: - case NodeTypeTypeDecl: case NodeTypeErrorValueDecl: case NodeTypeUnwrapErrorExpr: case NodeTypeStringLiteral: case NodeTypeSliceExpr: - case NodeTypeUse: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeIfVarExpr: - case NodeTypeWhileExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: @@ -976,13 +1046,22 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: - case NodeTypeAsmExpr: case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeTypeLiteral: + zig_panic("TODO"); + case NodeTypeRoot: + case NodeTypeFnProto: + case NodeTypeFnDef: + case NodeTypeFnDecl: + case NodeTypeUse: + case NodeTypeAsmExpr: + case NodeTypeParamDecl: + case NodeTypeDirective: + case NodeTypeTypeDecl: zig_unreachable(); } } @@ -1054,6 +1133,8 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va return true; } + assert(out_val->ok); + return efr.abort; } diff --git a/std/rand.zig b/std/rand.zig index 9dee46fbe..1fcfd78f7 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -7,6 +7,7 @@ pub struct Rand { index: isize, /// Initialize random state with the given seed. + #static_eval_enable(false) pub fn init(seed: u32) -> Rand { var r: Rand = undefined; r.index = 0; diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 4143fa000..f9f5707bc 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -218,6 +218,7 @@ pub fn bar_function() { )SOURCE"); add_source_file(tc, "other.zig", R"SOURCE( +#static_eval_enable(false) pub fn foo_function() -> bool { // this one conflicts with the one from foo return true; @@ -406,20 +407,6 @@ pub fn main(args: [][]u8) -> %void { } )SOURCE", "loop\nloop\nloop\nloop\n"); - add_simple_case("implicit cast after unreachable", R"SOURCE( -const io = @import("std").io; -pub fn main(args: [][]u8) -> %void { - const x = outer(); - if (x == 1234) { - %%io.stdout.printf("OK\n"); - } -} -fn inner() -> i32 { 1234 } -fn outer() -> isize { - return inner(); -} - )SOURCE", "OK\n"); - add_simple_case("@sizeof() and @typeof()", R"SOURCE( const io = @import("std").io; const x: u16 = 13; @@ -431,23 +418,6 @@ pub fn main(args: [][]u8) -> %void { } )SOURCE", "2\n"); - add_simple_case("member functions", R"SOURCE( -const io = @import("std").io; -struct Rand { - seed: u32, - pub fn get_seed(r: Rand) -> u32 { - r.seed - } -} -pub fn main(args: [][]u8) -> %void { - const r = Rand {.seed = 1234}; - if (r.get_seed() != 1234) { - %%io.stdout.printf("BAD seed\n"); - } - %%io.stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - add_simple_case("pointer dereferencing", R"SOURCE( const io = @import("std").io; @@ -565,24 +535,6 @@ pub fn main(args: [][]u8) -> %void { "min i64: -9223372036854775808\n"); - add_simple_case("else if expression", R"SOURCE( -const io = @import("std").io; -pub fn main(args: [][]u8) -> %void { - if (f(1) == 1) { - %%io.stdout.printf("OK\n"); - } -} -fn f(c: u8) -> u8 { - if (c == 0) { - 0 - } else if (c == 1) { - 1 - } else { - 2 - } -} - )SOURCE", "OK\n"); - add_simple_case("overflow intrinsics", R"SOURCE( const io = @import("std").io; pub fn main(args: [][]u8) -> %void { @@ -730,29 +682,6 @@ pub fn main(args: [][]u8) -> %void { } )SOURCE", "OK\n"); - add_simple_case("%% binary operator", R"SOURCE( -const io = @import("std").io; -error ItBroke; -fn g(x: bool) -> %isize { - if (x) { - error.ItBroke - } else { - 10 - } -} -pub fn main(args: [][]u8) -> %void { - const a = g(true) %% 3; - const b = g(false) %% 3; - if (a != 3) { - %%io.stdout.printf("BAD\n"); - } - if (b != 10) { - %%io.stdout.printf("BAD\n"); - } - %%io.stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - add_simple_case("string concatenation", R"SOURCE( const io = @import("std").io; pub fn main(args: [][]u8) -> %void { @@ -808,54 +737,6 @@ pub fn main(args: [][]u8) -> %void { } )SOURCE", "OK\n"); - add_simple_case("unwrap simple value from error", R"SOURCE( -const io = @import("std").io; -fn do() -> %isize { - 13 -} - -pub fn main(args: [][]u8) -> %void { - const i = %%do(); - if (i != 13) { - %%io.stdout.printf("BAD\n"); - } - %%io.stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - - add_simple_case("store member function in variable", R"SOURCE( -const io = @import("std").io; -struct Foo { - x: i32, - fn member(foo: Foo) -> i32 { foo.x } -} -pub fn main(args: [][]u8) -> %void { - const instance = Foo { .x = 1234, }; - const member_fn = Foo.member; - const result = member_fn(instance); - if (result != 1234) { - %%io.stdout.printf("BAD\n"); - } - %%io.stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - - add_simple_case("call member function directly", R"SOURCE( -const io = @import("std").io; -struct Foo { - x: i32, - fn member(foo: Foo) -> i32 { foo.x } -} -pub fn main(args: [][]u8) -> %void { - const instance = Foo { .x = 1234, }; - const result = Foo.member(instance); - if (result != 1234) { - %%io.stdout.printf("BAD\n"); - } - %%io.stdout.printf("OK\n"); -} - )SOURCE", "OK\n"); - add_simple_case("call result of if else expression", R"SOURCE( const io = @import("std").io; fn a() -> []u8 { "a\n" } @@ -1496,7 +1377,8 @@ fn a(x: i32) { struct Foo { y: [get()]u8, } -fn get() -> isize { 1 } +var global_var: isize = 1; +fn get() -> isize { global_var } )SOURCE", 1, ".tmp_source.zig:3:9: error: unable to evaluate constant expression"); diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 7d3f0b8e4..1becd42e7 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -123,18 +123,18 @@ fn short_circuit() { var hit_3 = false; var hit_4 = false; - if (true || { assert(false); false }) { + if (true || {assert_runtime(false); false}) { hit_1 = true; } if (false || { hit_2 = true; false }) { - assert(false); + assert_runtime(false); } if (true && { hit_3 = true; false }) { - %%io.stdout.printf("BAD 3\n"); + assert_runtime(false); } - if (false && { assert(false); false }) { - assert(false); + if (false && {assert_runtime(false); false}) { + assert_runtime(false); } else { hit_4 = true; } @@ -144,6 +144,11 @@ fn short_circuit() { assert(hit_4); } +#static_eval_enable(false) +fn assert_runtime(b: bool) { + if (!b) unreachable{} +} + #attribute("test") fn modify_operators() { var i : i32 = 0; @@ -509,6 +514,7 @@ enum Fruit { Orange, Banana, } +#static_eval_enable(false) fn non_const_switch_on_enum(fruit: Fruit) { switch (fruit) { Apple => unreachable{}, @@ -521,6 +527,7 @@ fn non_const_switch_on_enum(fruit: Fruit) { fn switch_statement() { non_const_switch(SwitchStatmentFoo.C); } +#static_eval_enable(false) fn non_const_switch(foo: SwitchStatmentFoo) { const val: i32 = switch (foo) { A => 1, @@ -549,6 +556,7 @@ enum SwitchProngWithVarEnum { Two: f32, Meh, } +#static_eval_enable(false) fn switch_prong_with_var_fn(a: SwitchProngWithVarEnum) { switch(a) { One => |x| { @@ -569,6 +577,7 @@ fn err_return_in_assignment() { %%do_err_return_in_assignment(); } +#static_eval_enable(false) fn do_err_return_in_assignment() -> %void { var x : i32 = undefined; x = %return make_a_non_err(); @@ -608,7 +617,7 @@ fn explicit_cast_maybe_pointers() { #attribute("test") fn const_expr_eval_on_single_expr_blocks() { - if (const_expr_eval_on_single_expr_blocks_fn(1, true) != 3) unreachable{} + assert(const_expr_eval_on_single_expr_blocks_fn(1, true) == 3); } fn const_expr_eval_on_single_expr_blocks_fn(x: i32, b: bool) -> i32 { @@ -736,6 +745,7 @@ fn generic_malloc_free() { mem_free(u8)(a); } const some_mem : [100]u8 = undefined; +#static_eval_enable(false) fn mem_alloc(T: type)(n: isize) -> %[]T { return (&T)(&some_mem[0])[0...n]; } @@ -789,6 +799,7 @@ var goto_counter: i32 = 0; fn goto_leave_defer_scope() { test_goto_leave_defer_scope(true); } +#static_eval_enable(false) fn test_goto_leave_defer_scope(b: bool) { var it_worked = false; @@ -820,3 +831,83 @@ fn cast_small_unsigned_to_larger_signed() { } fn cast_small_unsigned_to_larger_signed_1(x: u8) -> i16 { x } fn cast_small_unsigned_to_larger_signed_2(x: u16) -> isize { x } + + +#attribute("test") +fn implicit_cast_after_unreachable() { + assert(outer() == 1234); +} +fn inner() -> i32 { 1234 } +fn outer() -> isize { + return inner(); +} + + +#attribute("test") +fn else_if_expression() { + assert(else_if_expression_f(1) == 1); +} +fn else_if_expression_f(c: u8) -> u8 { + if (c == 0) { + 0 + } else if (c == 1) { + 1 + } else { + 2 + } +} + +#attribute("test") +fn err_binary_operator() { + const a = err_binary_operator_g(true) %% 3; + const b = err_binary_operator_g(false) %% 3; + assert(a == 3); + assert(b == 10); +} +error ItBroke; +fn err_binary_operator_g(x: bool) -> %isize { + if (x) { + error.ItBroke + } else { + 10 + } +} + +#attribute("test") +fn unwrap_simple_value_from_error() { + const i = %%unwrap_simple_value_from_error_do(); + assert(i == 13); +} +fn unwrap_simple_value_from_error_do() -> %isize { 13 } + + +#attribute("test") +fn store_member_function_in_variable() { + const instance = MemberFnTestFoo { .x = 1234, }; + const member_fn = MemberFnTestFoo.member; + const result = member_fn(instance); + assert(result == 1234); +} +struct MemberFnTestFoo { + x: i32, + fn member(foo: MemberFnTestFoo) -> i32 { foo.x } +} + +#attribute("test") +fn call_member_function_directly() { + const instance = MemberFnTestFoo { .x = 1234, }; + const result = MemberFnTestFoo.member(instance); + assert(result == 1234); +} + +#attribute("test") +fn member_functions() { + const r = MemberFnRand {.seed = 1234}; + assert(r.get_seed() == 1234); +} +struct MemberFnRand { + seed: u32, + pub fn get_seed(r: MemberFnRand) -> u32 { + r.seed + } +} From 69109bc270c3167a3534dd32fc4f4def855e0be9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Apr 2016 16:18:41 -0700 Subject: [PATCH 5/9] add error for dividing by zero in static function evaluation --- src/analyze.cpp | 60 ++++++++++------------------------------------ src/error.cpp | 2 ++ src/error.hpp | 2 ++ src/eval.cpp | 47 ++++++++++++++++++++++++++++-------- src/eval.hpp | 2 +- test/run_tests.cpp | 10 ++++++++ 6 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index f7ef77c19..0cd234e3b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2653,26 +2653,6 @@ static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNo 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_error_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, Buf *err_name) { @@ -3074,37 +3054,23 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, 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) { - ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; - if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || - (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) - { + ConstExprValue *out_val = &get_resolved_expr(node)->const_val; + int err; + if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type, + op2_val, resolved_type, out_val))) + { + if (err == ErrorDivByZero) { add_node_error(g, node, buf_sprintf("division by zero is undefined")); return g->builtin_types.entry_invalid; - } else { - return resolve_expr_const_val_as_bignum_op(g, node, bignum_div, *op1, *op2, resolved_type); + } else if (err == ErrorOverflow) { + add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type")); + return g->builtin_types.entry_invalid; } - } 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(); + return g->builtin_types.entry_invalid; } + + num_lit_fits_in_other_type(g, node, resolved_type); + return resolved_type; } case BinOpTypeUnwrapMaybe: { diff --git a/src/error.cpp b/src/error.cpp index 7690dd077..caed95f69 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -12,6 +12,8 @@ const char *err_str(int err) { case ErrorFileNotFound: return "file not found"; case ErrorFileSystem: return "file system error"; case ErrorFileTooBig: return "file too big"; + case ErrorDivByZero: return "division by zero"; + case ErrorOverflow: return "overflow"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index 742f30700..7fea3ccfb 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -19,6 +19,8 @@ enum Error { ErrorFileNotFound, ErrorFileSystem, ErrorFileTooBig, + ErrorDivByZero, + ErrorOverflow }; const char *err_str(int err); diff --git a/src/eval.cpp b/src/eval.cpp index 1e386e036..3e9d836a3 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -1,5 +1,6 @@ #include "eval.hpp" #include "analyze.hpp" +#include "error.hpp" static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val); @@ -96,16 +97,20 @@ static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) { } } -static void eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, +static int eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *)) { bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); - assert(!overflow); + if (overflow) { + return ErrorOverflow; + } + out_val->ok = true; out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; + return 0; } -void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, +int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val) { assert(op1_val->ok); @@ -126,7 +131,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, case BinOpTypeAssignBoolAnd: case BinOpTypeAssignBoolOr: out_val->ok = true; - return; + return 0; case BinOpTypeBoolOr: case BinOpTypeBoolAnd: assert(op1_type->id == TypeTableEntryIdBool); @@ -134,7 +139,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, out_val->data.x_bool = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op, op2_val->data.x_bool); out_val->ok = true; out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; - return; + return 0; case BinOpTypeCmpEq: case BinOpTypeCmpNotEq: case BinOpTypeCmpLessThan: @@ -181,7 +186,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; out_val->data.x_bool = answer; out_val->ok = true; - return; + return 0; } case BinOpTypeAdd: return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add); @@ -215,7 +220,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) { - zig_panic("TODO handle errors in eval"); + return ErrorDivByZero; } else { return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_div); } @@ -249,7 +254,26 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) BinOpType bin_op = node->data.bin_op_expr.bin_op; - eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val); + int err; + if ((err = eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val))) { + ef->root->abort = true; + if (err == ErrorDivByZero) { + ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node, + buf_sprintf("function evaluation caused division by zero")); + add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); + add_error_note(ef->root->codegen, msg, node, buf_sprintf("division by zero here")); + } else if (err == ErrorOverflow) { + ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node, + buf_sprintf("function evaluation caused overflow")); + add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); + add_error_note(ef->root->codegen, msg, node, buf_sprintf("overflow occurred here")); + } else { + zig_unreachable(); + } + return true; + } + + assert(out_val->ok); return false; } @@ -1133,8 +1157,11 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va return true; } - assert(out_val->ok); + if (efr.abort) { + return true; + } - return efr.abort; + assert(out_val->ok); + return false; } diff --git a/src/eval.hpp b/src/eval.hpp index f8266228f..e0236b714 100644 --- a/src/eval.hpp +++ b/src/eval.hpp @@ -14,7 +14,7 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va AstNode *struct_node); bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry); -void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, +int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val); void eval_const_expr_implicit_cast(CastOp cast_op, diff --git a/test/run_tests.cpp b/test/run_tests.cpp index f9f5707bc..5b3d68e72 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1481,6 +1481,16 @@ fn foo() { const pointer = &array[0]; } )SOURCE", 1, ".tmp_source.zig:4:27: error: out of bounds array access"); + + add_compile_fail_case("compile time division by zero", R"SOURCE( +const x = foo(0); +fn foo(x: i32) -> i32 { + 1 / x +} + )SOURCE", 3, + ".tmp_source.zig:3:1: error: function evaluation caused division by zero", + ".tmp_source.zig:2:14: note: called from here", + ".tmp_source.zig:4:7: note: division by zero here"); } ////////////////////////////////////////////////////////////////////////////// From 3c27cb25279049cfdcde99d49045f5b8ec8981ba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Apr 2016 17:33:46 -0700 Subject: [PATCH 6/9] more eval tests and fix eval call analyze code --- src/analyze.cpp | 17 +++++--------- src/eval.cpp | 4 +++- test/run_tests.cpp | 15 ++++++++++++ test/self_hosted.zig | 56 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 0cd234e3b..f24d538ad 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4504,28 +4504,23 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, } FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; - if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && all_args_const_expr) { + if (ok_invocation && fn_table_entry && fn_table_entry->is_pure) { if (fn_table_entry->anal_state == FnAnalStateReady) { analyze_fn_body(g, fn_table_entry); - } else if (fn_table_entry->anal_state == FnAnalStateProbing) { - mark_impure_fn(context); } - if (fn_table_entry->is_pure) { - if (fn_table_entry->anal_state == FnAnalStateComplete) { + if (all_args_const_expr) { + if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) { ConstExprValue *result_val = &get_resolved_expr(node)->const_val; if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) { // function evaluation generated an error return g->builtin_types.entry_invalid; } return return_type; - } else if (fn_table_entry->anal_state == FnAnalStateSkipped) { - return g->builtin_types.entry_invalid; } - } else { - // calling an impure fn is impure - mark_impure_fn(context); } - } else { + } + if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure) { + // calling an impure fn is impure mark_impure_fn(context); } diff --git a/src/eval.cpp b/src/eval.cpp index 3e9d836a3..34656c41e 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -713,7 +713,9 @@ static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val } if (!fn_table_entry) { - zig_panic("TODO"); + ConstExprValue fn_val = {0}; + if (eval_expr(ef, fn_ref_expr, &fn_val)) return true; + fn_table_entry = fn_val.data.x_fn; } int param_count = node->data.fn_call_expr.params.length; diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 5b3d68e72..461ee4172 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1491,6 +1491,21 @@ fn foo(x: i32) -> i32 { ".tmp_source.zig:3:1: error: function evaluation caused division by zero", ".tmp_source.zig:2:14: note: called from here", ".tmp_source.zig:4:7: note: division by zero here"); + + add_compile_fail_case("branch on undefined value", R"SOURCE( +const x = if (undefined) true else false; + )SOURCE", 1, ".tmp_source.zig:2:15: error: branch on undefined value"); + + + add_compile_fail_case("endless loop in function evaluation", R"SOURCE( +const seventh_fib_number = fibbonaci(7); +fn fibbonaci(x: i32) -> i32 { + return fibbonaci(x - 1) + fibbonaci(x - 2); +} + )SOURCE", 3, + ".tmp_source.zig:3:1: error: function evaluation exceeded 1000 branches", + ".tmp_source.zig:2:37: note: called from here", + ".tmp_source.zig:4:40: note: quota exceeded here"); } ////////////////////////////////////////////////////////////////////////////// diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 1becd42e7..4513b06dd 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -415,9 +415,7 @@ error err2; #attribute("test") fn fn_call_of_struct_field() { - if (call_struct_field(Foo {.ptr = a_func,}) != 13) { - unreachable{}; - } + assert(call_struct_field(Foo {.ptr = a_func,}) == 13); } struct Foo { @@ -911,3 +909,55 @@ struct MemberFnRand { r.seed } } + +#attribute("test") +fn static_function_evaluation() { + assert(statically_added_number == 3); +} +const statically_added_number = static_add(1, 2); +fn static_add(a: i32, b: i32) -> i32 { a + b } + + +#attribute("test") +fn statically_initalized_list() { + assert(static_point_list[0].x == 1); + assert(static_point_list[0].y == 2); + assert(static_point_list[1].x == 3); + assert(static_point_list[1].y == 4); +} +struct Point { + x: i32, + y: i32, +} +const static_point_list = []Point { make_point(1, 2), make_point(3, 4) }; +fn make_point(x: i32, y: i32) -> Point { + return Point { + .x = x, + .y = y, + }; +} + + +#attribute("test") +fn static_eval_recursive() { + assert(seventh_fib_number == 21); +} +const seventh_fib_number = fibbonaci(7); +fn fibbonaci(x: i32) -> i32 { + if (x <= 1) return 1; + return fibbonaci(x - 1) + fibbonaci(x - 2); +} + +#attribute("test") +fn static_eval_while() { + assert(static_eval_while_number == 1); +} +const static_eval_while_number = static_while_loop_1(); +fn static_while_loop_1() -> i32 { + return while_loop_2(); +} +fn static_while_loop_2() -> i32 { + while (true) { + return 1; + } +} From 3f9632bb404584db872ed8e2318f770cde8b16ee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 13 Apr 2016 10:13:49 -0700 Subject: [PATCH 7/9] eval: support array literals --- src/eval.cpp | 22 ++++++++++++++++++++++ test/self_hosted.zig | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/eval.cpp b/src/eval.cpp index 34656c41e..b3120118c 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -359,6 +359,28 @@ static bool eval_container_init_expr(EvalFn *ef, AstNode *node, ConstExprValue * add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); add_error_note(ef->root->codegen, msg, node, buf_sprintf("unreachable expression here")); return true; + } else if (container_type->id == TypeTableEntryIdStruct && + container_type->data.structure.is_unknown_size_array && + kind == ContainerInitKindArray) + { + + int elem_count = container_init_expr->entries.length; + + out_val->ok = true; + out_val->data.x_array.fields = allocate(elem_count); + + for (int i = 0; i < elem_count; i += 1) { + AstNode *elem_node = container_init_expr->entries.at(i); + + ConstExprValue *elem_val = allocate(1); + if (eval_expr(ef, elem_node, elem_val)) return true; + + assert(elem_val->ok); + + out_val->data.x_array.fields[i] = elem_val; + out_val->depends_on_compile_var = out_val->depends_on_compile_var || + elem_val->depends_on_compile_var; + } } else { zig_panic("TODO"); } diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 4513b06dd..b36627855 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -961,3 +961,17 @@ fn static_while_loop_2() -> i32 { return 1; } } + +#attribute("test") +fn static_eval_list_init() { + assert(static_vec3.data[2] == 1.0); +} +const static_vec3 = vec3(0.0, 0.0, 1.0); +pub struct Vec3 { + data: [3]f32, +} +pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 { + Vec3 { + .data = []f32 { x, y, z, }, + } +} From 83a59c4d077ead78a8c0ccecf8d4f7970309bf76 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 13 Apr 2016 12:28:07 -0700 Subject: [PATCH 8/9] fix evaluating generic functions in wrong context --- src/analyze.cpp | 2 +- src/codegen.cpp | 1 + src/parser.cpp | 16 +++++++++++----- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index f24d538ad..244bdc5fc 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4564,7 +4564,7 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp generic_fn_type_id->generic_param_count = actual_param_count; generic_fn_type_id->generic_params = allocate(actual_param_count); - BlockContext *child_context = import->block_context; + BlockContext *child_context = decl_node->owner->block_context; for (int i = 0; i < actual_param_count; i += 1) { AstNode *generic_param_decl_node = decl_node->data.fn_proto.generic_params.at(i); assert(generic_param_decl_node->type == NodeTypeParamDecl); diff --git a/src/codegen.cpp b/src/codegen.cpp index 4a1ea84a0..70648d252 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3033,6 +3033,7 @@ static void gen_const_globals(CodeGen *g) { } else { expr->const_llvm_val = gen_const_val(g, type_entry, const_val); } + assert(expr->const_llvm_val); } } diff --git a/src/parser.cpp b/src/parser.cpp index 3c9bdaa78..650a7e614 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3036,15 +3036,21 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { // none break; case NodeTypePrefixOpExpr: - clone_subtree_field(&new_node->data.prefix_op_expr.primary_expr, old_node->data.prefix_op_expr.primary_expr, next_node_index); + clone_subtree_field(&new_node->data.prefix_op_expr.primary_expr, + old_node->data.prefix_op_expr.primary_expr, next_node_index); break; case NodeTypeFnCallExpr: - clone_subtree_field(&new_node->data.fn_call_expr.fn_ref_expr, old_node->data.fn_call_expr.fn_ref_expr, next_node_index); - clone_subtree_list(&new_node->data.fn_call_expr.params, &old_node->data.fn_call_expr.params, next_node_index); + assert(!old_node->data.fn_call_expr.resolved_expr.has_global_const); + clone_subtree_field(&new_node->data.fn_call_expr.fn_ref_expr, + old_node->data.fn_call_expr.fn_ref_expr, next_node_index); + clone_subtree_list(&new_node->data.fn_call_expr.params, + &old_node->data.fn_call_expr.params, next_node_index); break; case NodeTypeArrayAccessExpr: - clone_subtree_field(&new_node->data.array_access_expr.array_ref_expr, old_node->data.array_access_expr.array_ref_expr, next_node_index); - clone_subtree_field(&new_node->data.array_access_expr.subscript, old_node->data.array_access_expr.subscript, next_node_index); + clone_subtree_field(&new_node->data.array_access_expr.array_ref_expr, + old_node->data.array_access_expr.array_ref_expr, next_node_index); + clone_subtree_field(&new_node->data.array_access_expr.subscript, + old_node->data.array_access_expr.subscript, next_node_index); break; case NodeTypeSliceExpr: clone_subtree_field(&new_node->data.slice_expr.array_ref_expr, old_node->data.slice_expr.array_ref_expr, next_node_index); From fcedc35551cc6b14756499414e47c33004de3be4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Apr 2016 10:39:03 -0700 Subject: [PATCH 9/9] fix crash with generic function and implicit cast --- src/ast_render.cpp | 8 ++++++-- src/parser.cpp | 6 ++++-- test/run_tests.cpp | 2 +- test/self_hosted.zig | 11 +++++++++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 59a287b88..6b88579b4 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -326,9 +326,9 @@ static void render_node(AstRender *ar, AstNode *node) { AstNode *statement = node->data.block.statements.at(i); print_indent(ar); render_node(ar, statement); + fprintf(ar->f, ";\n"); } ar->indent -= ar->indent_size; - fprintf(ar->f, "\n"); print_indent(ar); fprintf(ar->f, "}"); break; @@ -438,7 +438,11 @@ static void render_node(AstRender *ar, AstNode *node) { fprintf(ar->f, ")"); break; case NodeTypeArrayAccessExpr: - zig_panic("TODO"); + render_node(ar, node->data.array_access_expr.array_ref_expr); + fprintf(ar->f, "["); + render_node(ar, node->data.array_access_expr.subscript); + fprintf(ar->f, "]"); + break; case NodeTypeSliceExpr: zig_panic("TODO"); case NodeTypeFieldAccessExpr: diff --git a/src/parser.cpp b/src/parser.cpp index 650a7e614..0bd2f5783 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2927,6 +2927,7 @@ static void clone_subtree_list(ZigList *dest, ZigList *src dest->resize(src->length); for (int i = 0; i < src->length; i += 1) { dest->at(i) = ast_clone_subtree(src->at(i), next_node_index); + dest->at(i)->parent_field = &dest->at(i); } } @@ -2958,11 +2959,12 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { memcpy(new_node, old_node, sizeof(AstNode)); new_node->create_index = *next_node_index; *next_node_index += 1; + new_node->parent_field = nullptr; switch (new_node->type) { case NodeTypeRoot: - clone_subtree_list(&new_node->data.root.top_level_decls, &old_node->data.root.top_level_decls, - next_node_index); + clone_subtree_list(&new_node->data.root.top_level_decls, + &old_node->data.root.top_level_decls, next_node_index); break; case NodeTypeFnProto: clone_subtree_tld(&new_node->data.fn_proto.top_level_decl, &old_node->data.fn_proto.top_level_decl, diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 461ee4172..30fc1fdcf 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1667,7 +1667,7 @@ extern void (*fn_ptr)(void); )SOURCE", 2, "pub extern var fn_ptr: ?extern fn();", R"SOURCE(pub inline fn foo() { - (??fn_ptr)() + (??fn_ptr)(); })SOURCE"); diff --git a/test/self_hosted.zig b/test/self_hosted.zig index b36627855..33b1834d9 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -975,3 +975,14 @@ pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 { .data = []f32 { x, y, z, }, } } + + +#attribute("test") +fn generic_fn_with_implicit_cast() { + assert(get_first_byte(u8)([]u8 {13}) == 13); + assert(get_first_byte(u16)([]u16 {0, 13}) == 0); +} +fn get_byte(ptr: ?&u8) -> u8 {*??ptr} +fn get_first_byte(T: type)(mem: []T) -> u8 { + get_byte((&u8)(&mem[0])) +}