zig/src/eval.cpp
Andrew Kelley 12fcbecbf8 IR: add more instructions
* MaybeWrap
 * TestErr
 * UnwrapErrCode
 * UnwrapErrPayload
 * ErrUnionTypeChild
 * ErrWrapCode
 * ErrWrapPayload
2016-12-17 16:16:17 -05:00

430 lines
17 KiB
C++

/*
* Copyright (c) 2016 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "eval.hpp"
#include "analyze.hpp"
#include "error.hpp"
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_pure_err == b->data.x_pure_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 TypeTableEntryIdUnion:
zig_panic("TODO");
case TypeTableEntryIdUndefLit:
zig_panic("TODO");
case TypeTableEntryIdNullLit:
zig_panic("TODO");
case TypeTableEntryIdMaybe:
zig_panic("TODO");
case TypeTableEntryIdErrorUnion:
zig_panic("TODO");
case TypeTableEntryIdTypeDecl:
zig_panic("TODO");
case TypeTableEntryIdNamespace:
zig_panic("TODO");
case TypeTableEntryIdBlock:
zig_panic("TODO");
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdInvalid:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdVar:
zig_unreachable();
}
zig_unreachable();
}
static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) {
if (bin_op == BinOpTypeBoolOr || bin_op == BinOpTypeAssignBoolOr) {
return a || b;
} else if (bin_op == BinOpTypeBoolAnd || bin_op == BinOpTypeAssignBoolAnd) {
return a && b;
} else {
zig_unreachable();
}
}
static uint64_t max_unsigned_val(TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
if (type_entry->data.integral.bit_count == 64) {
return UINT64_MAX;
} else if (type_entry->data.integral.bit_count == 32) {
return UINT32_MAX;
} else if (type_entry->data.integral.bit_count == 16) {
return UINT16_MAX;
} else if (type_entry->data.integral.bit_count == 8) {
return UINT8_MAX;
} else {
zig_unreachable();
}
}
static int64_t max_signed_val(TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
if (type_entry->data.integral.bit_count == 64) {
return INT64_MAX;
} else if (type_entry->data.integral.bit_count == 32) {
return INT32_MAX;
} else if (type_entry->data.integral.bit_count == 16) {
return INT16_MAX;
} else if (type_entry->data.integral.bit_count == 8) {
return INT8_MAX;
} else {
zig_unreachable();
}
}
static int64_t min_signed_val(TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
if (type_entry->data.integral.bit_count == 64) {
return INT64_MIN;
} else if (type_entry->data.integral.bit_count == 32) {
return INT32_MIN;
} else if (type_entry->data.integral.bit_count == 16) {
return INT16_MIN;
} else if (type_entry->data.integral.bit_count == 8) {
return INT8_MIN;
} 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 *),
TypeTableEntry *type, bool wrapping_op)
{
bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
if (overflow) {
return ErrorOverflow;
}
if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum,
type->data.integral.bit_count, type->data.integral.is_signed))
{
if (wrapping_op) {
if (type->data.integral.is_signed) {
out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative;
} else if (out_val->data.x_bignum.is_negative) {
out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
out_val->data.x_bignum.is_negative = false;
} else {
bignum_truncate(&out_val->data.x_bignum, type->data.integral.bit_count);
}
} else {
return ErrorOverflow;
}
}
out_val->special = ConstValSpecialStatic;
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->special != ConstValSpecialRuntime);
assert(op2_val->special != ConstValSpecialRuntime);
assert(op1_type->id != TypeTableEntryIdInvalid);
assert(op2_type->id != TypeTableEntryIdInvalid);
switch (bin_op) {
case BinOpTypeAssign:
*out_val = *op2_val;
return 0;
case BinOpTypeBoolOr:
case BinOpTypeBoolAnd:
case BinOpTypeAssignBoolAnd:
case BinOpTypeAssignBoolOr:
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->special = ConstValSpecialStatic;
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->special = ConstValSpecialStatic;
return 0;
}
case BinOpTypeAdd:
case BinOpTypeAssignPlus:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, false);
case BinOpTypeAddWrap:
case BinOpTypeAssignPlusWrap:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, true);
case BinOpTypeBinOr:
case BinOpTypeAssignBitOr:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_or, op1_type, false);
case BinOpTypeBinXor:
case BinOpTypeAssignBitXor:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_xor, op1_type, false);
case BinOpTypeBinAnd:
case BinOpTypeAssignBitAnd:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_and, op1_type, false);
case BinOpTypeBitShiftLeft:
case BinOpTypeAssignBitShiftLeft:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, false);
case BinOpTypeBitShiftLeftWrap:
case BinOpTypeAssignBitShiftLeftWrap:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, true);
case BinOpTypeBitShiftRight:
case BinOpTypeAssignBitShiftRight:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shr, op1_type, false);
case BinOpTypeSub:
case BinOpTypeAssignMinus:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, false);
case BinOpTypeSubWrap:
case BinOpTypeAssignMinusWrap:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, true);
case BinOpTypeMult:
case BinOpTypeAssignTimes:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, false);
case BinOpTypeMultWrap:
case BinOpTypeAssignTimesWrap:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, true);
case BinOpTypeDiv:
case BinOpTypeAssignDiv:
{
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, op1_type, false);
}
}
case BinOpTypeMod:
case BinOpTypeAssignMod:
return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type, false);
case BinOpTypeUnwrapMaybe:
zig_panic("TODO");
case BinOpTypeArrayCat:
case BinOpTypeArrayMult:
case BinOpTypeInvalid:
zig_unreachable();
}
zig_unreachable();
}
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->special = other_val->special;
assert(other_val != const_val);
switch (cast_op) {
case CastOpNoCast:
zig_unreachable();
case CastOpNoop:
case CastOpWidenOrShorten:
*const_val = *other_val;
break;
case CastOpPointerReinterpret:
zig_panic("TODO compile time pointer reinterpret");
break;
case CastOpPtrToInt:
case CastOpIntToPtr:
case CastOpResizeSlice:
case CastOpBytesToSlice:
// can't do it
break;
case CastOpToUnknownSizeArray:
{
assert(other_type->id == TypeTableEntryIdArray);
assert(other_val->data.x_array.size == other_type->data.array.len);
const_val->data.x_struct.fields = allocate<ConstExprValue>(2);
ConstExprValue *ptr_field = &const_val->data.x_struct.fields[slice_ptr_index];
ConstExprValue *len_field = &const_val->data.x_struct.fields[slice_len_index];
ptr_field->special = ConstValSpecialStatic;
ptr_field->data.x_ptr.base_ptr = other_val;
len_field->special = ConstValSpecialStatic;
bignum_init_unsigned(&len_field->data.x_bignum, other_type->data.array.len);
const_val->special = ConstValSpecialStatic;
break;
}
case CastOpErrToInt:
{
uint64_t value;
if (other_type->id == TypeTableEntryIdErrorUnion) {
value = other_val->data.x_err_union.err ? other_val->data.x_err_union.err->value : 0;
} else if (other_type->id == TypeTableEntryIdPureError) {
value = other_val->data.x_pure_err->value;
} else {
zig_unreachable();
}
bignum_init_unsigned(&const_val->data.x_bignum, value);
const_val->special = ConstValSpecialStatic;
break;
}
case CastOpIntToFloat:
bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum);
const_val->special = ConstValSpecialStatic;
break;
case CastOpFloatToInt:
bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum);
const_val->special = ConstValSpecialStatic;
break;
case CastOpBoolToInt:
bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0);
const_val->special = ConstValSpecialStatic;
break;
case CastOpIntToEnum:
{
uint64_t value = other_val->data.x_bignum.data.x_uint;
assert(new_type->id == TypeTableEntryIdEnum);
assert(value < new_type->data.enumeration.src_field_count);
const_val->data.x_enum.tag = value;
const_val->data.x_enum.payload = NULL;
const_val->special = ConstValSpecialStatic;
break;
}
case CastOpEnumToInt:
bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_enum.tag);
const_val->special = ConstValSpecialStatic;
break;
}
}
static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) {
assert(int_type->id == TypeTableEntryIdInt);
for (size_t 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->special = ConstValSpecialStatic;
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 = max_signed_val(type_entry);
bignum_init_signed(&const_val->data.x_bignum, val);
} else {
uint64_t val = max_unsigned_val(type_entry);
bignum_init_unsigned(&const_val->data.x_bignum, val);
}
} else {
if (type_entry->data.integral.is_signed) {
int64_t val = min_signed_val(type_entry);
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->special = ConstValSpecialStatic;
const_val->data.x_bool = is_max;
} else if (type_entry->id == TypeTableEntryIdVoid) {
// nothing to do
} else {
zig_unreachable();
}
}