diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2016-04-14 10:40:08 -0700 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2016-04-14 10:40:08 -0700 |
| commit | 579856e502eab87ea8a73a76dc63a2108e5a8cc8 (patch) | |
| tree | feb34b0985b29f45073ac81e21982a368e0a3542 /src/eval.cpp | |
| parent | 5a479720ec5786145dac6c85deae4e322bd5972e (diff) | |
| parent | fcedc35551cc6b14756499414e47c33004de3be4 (diff) | |
| download | zig-579856e502eab87ea8a73a76dc63a2108e5a8cc8.tar.gz zig-579856e502eab87ea8a73a76dc63a2108e5a8cc8.zip | |
Merge branch 'eval'
Diffstat (limited to 'src/eval.cpp')
| -rw-r--r-- | src/eval.cpp | 1191 |
1 files changed, 1191 insertions, 0 deletions
diff --git a/src/eval.cpp b/src/eval.cpp new file mode 100644 index 0000000000..b3120118c6 --- /dev/null +++ b/src/eval.cpp @@ -0,0 +1,1191 @@ +#include "eval.hpp" +#include "analyze.hpp" +#include "error.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<EvalScope>(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); + memset(out, 0, sizeof(ConstExprValue)); + 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 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); + 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; +} + +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); + assert(op2_val->ok); + + 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 0; + 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 0; + 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 0; + } + 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)) + { + return ErrorDivByZero; + } 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; + + 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; +} + +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); + assert(var); + + *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<ConstExprValue*>(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<ConstExprValue>(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 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 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<ConstExprValue*>(elem_count); + + for (int i = 0; i < elem_count; i += 1) { + AstNode *elem_node = container_init_expr->entries.at(i); + + ConstExprValue *elem_val = allocate<ConstExprValue>(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"); + } + + + 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, TypeTableEntry *new_type) +{ + 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: + *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; + + 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<ConstExprValue*>(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"); + } + } 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<ConstExprValue>(1); + ConstExprValue **ptr_val = allocate<ConstExprValue*>(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 + break; + case CastOpToUnknownSizeArray: + { + assert(other_type->id == TypeTableEntryIdArray); + + ConstExprValue *all_fields = allocate<ConstExprValue>(2); + ConstExprValue *ptr_field = &all_fields[0]; + ConstExprValue *len_field = &all_fields[1]; + + const_val->data.x_struct.fields = allocate<ConstExprValue*>(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 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) { + return eval_fn_call_builtin(ef, node, out_val); + } else if (cast_op != CastOpNoCast) { + 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; + } + + 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) { + 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; + ConstExprValue *args = allocate<ConstExprValue>(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_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; + assert(out_val->ok); + } else { + zig_panic("TODO"); + } + } else if (struct_type->id == TypeTableEntryIdMetaType) { + 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 { + 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<EvalScope>(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->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; + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + + ConstExprValue expr_val = {0}; + 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 PrefixOpDereference: + assert(expr_type->id == TypeTableEntryIdPointer); + *out_val = *expr_val.data.x_ptr.ptr[0]; + break; + case PrefixOpAddressOf: + case PrefixOpConstAddressOf: + { + ConstExprValue *child_val = allocate<ConstExprValue>(1); + *child_val = expr_val; + + ConstExprValue **ptr_val = allocate<ConstExprValue*>(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: + case PrefixOpUnwrapMaybe: + zig_panic("TODO"); + case PrefixOpInvalid: + zig_unreachable(); + } + + 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_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<EvalScope>(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); + 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 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 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 NodeTypeWhileExpr: + return eval_while_expr(ef, node, out); + case NodeTypeDefer: + case NodeTypeErrorValueDecl: + case NodeTypeUnwrapErrorExpr: + case NodeTypeStringLiteral: + case NodeTypeSliceExpr: + case NodeTypeNullLiteral: + case NodeTypeUndefinedLiteral: + case NodeTypeIfVarExpr: + case NodeTypeSwitchExpr: + case NodeTypeSwitchProng: + case NodeTypeSwitchRange: + case NodeTypeLabel: + case NodeTypeGoto: + case NodeTypeBreak: + case NodeTypeContinue: + 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(); + } +} + +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<EvalScope>(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<ConstExprValue>(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; + } + + if (efr.abort) { + return true; + } + + assert(out_val->ok); + return false; +} + |
