diff --git a/CMakeLists.txt b/CMakeLists.txt index f3cba1c289..cf0f41dd06 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 e71330be4a..45691fcb2d 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 62b560f0c3..3750761094 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 0000000000..08a858ab2c --- /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 0000000000..4a8d294502 --- /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