mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 22:33:08 +00:00
* MaybeWrap * TestErr * UnwrapErrCode * UnwrapErrPayload * ErrUnionTypeChild * ErrWrapCode * ErrWrapPayload
430 lines
17 KiB
C++
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();
|
|
}
|
|
}
|