Merge branch 'eval'

master
Andrew Kelley 2016-04-14 10:40:08 -07:00
commit 579856e502
15 changed files with 1693 additions and 469 deletions

View File

@ -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"

View File

@ -496,8 +496,8 @@ struct AstNodeWhileExpr {
};
struct AstNodeForExpr {
AstNode *elem_node; // always a symbol
AstNode *array_expr;
AstNode *elem_node; // always a symbol
AstNode *index_node; // always a symbol, might be null
AstNode *body;
@ -960,6 +960,7 @@ struct TypeTableEntry {
LLVMZigDIType *di_type;
bool zero_bits;
bool deep_const;
union {
TypeTableEntryPointer pointer;
@ -1005,6 +1006,13 @@ struct ImportTableEntry {
ZigList<AstNode *> 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<AstNode *> cast_alloca_list;
ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
@ -1027,6 +1037,33 @@ struct FnTableEntry {
ZigList<AstNode *> goto_list;
};
struct EvalVar {
Buf *name;
ConstExprValue value;
};
struct EvalScope {
BlockContext *block_context;
ZigList<EvalVar> vars;
};
struct EvalFnRoot {
CodeGen *codegen;
FnTableEntry *fn;
AstNode *call_node;
int branch_quota;
int branches_used;
AstNode *exceeded_quota_node;
bool abort;
};
struct EvalFn {
EvalFnRoot *root;
FnTableEntry *fn;
ConstExprValue *return_expr;
ZigList<EvalScope*> scope_stack;
};
enum BuiltinFnId {
BuiltinFnIdInvalid,
BuiltinFnIdMemcpy,

View File

@ -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);
@ -34,7 +35,8 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node,
static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node,
TypeTableEntry *expected_type, uint64_t x);
static AstNode *find_decl(BlockContext *context, Buf *name);
static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, bool pointer_only);
static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node,
bool pointer_only, BlockContext *block_context);
static TopLevelDecl *get_as_top_level_decl(AstNode *node);
static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
BlockContext *context, AstNode *source_node,
@ -206,6 +208,7 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
static TypeTableEntry *get_generic_fn_type(CodeGen *g, AstNode *decl_node) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdGenericFn);
buf_init_from_str(&entry->name, "(generic function)");
entry->deep_const = true;
entry->zero_bits = true;
entry->data.generic_fn.decl_node = decl_node;
return entry;
@ -219,6 +222,8 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
} else {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
entry->deep_const = is_const && child_type->deep_const;
const char *const_str = is_const ? "const " : "";
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name));
@ -260,6 +265,8 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
assert(child_type->type_ref);
assert(child_type->di_type);
entry->deep_const = child_type->deep_const;
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
@ -343,6 +350,8 @@ static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
entry->data.error.child_type = child_type;
entry->deep_const = child_type->deep_const;
if (!type_has_bits(child_type)) {
entry->type_ref = g->err_tag_type->type_ref;
entry->di_type = g->err_tag_type->di_type;
@ -414,6 +423,7 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
entry->type_ref = LLVMArrayType(child_type->type_ref, array_size);
entry->zero_bits = (array_size == 0) || child_type->zero_bits;
entry->deep_const = child_type->deep_const;
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name));
@ -464,6 +474,8 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
TypeTableEntry *var_peer = get_slice_type(g, child_type, false);
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
entry->deep_const = child_type->deep_const;
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name));
@ -549,6 +561,7 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *
buf_init_from_str(&entry->name, name);
entry->deep_const = child_type->deep_const;
entry->type_ref = child_type->type_ref;
entry->di_type = child_type->di_type;
entry->zero_bits = child_type->zero_bits;
@ -572,6 +585,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
}
TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
fn_type->deep_const = true;
fn_type->data.fn.fn_type_id = *fn_type_id;
if (fn_type_id->param_info == &fn_type_id->prealloc_param_info[0]) {
fn_type->data.fn.fn_type_id.param_info = &fn_type->data.fn.fn_type_id.prealloc_param_info[0];
@ -944,6 +958,19 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
add_node_error(g, directive_node,
buf_sprintf("#condition valid only on exported symbols"));
}
} else if (buf_eql_str(name, "static_eval_enable")) {
if (fn_table_entry->is_extern) {
add_node_error(g, directive_node,
buf_sprintf("#static_val_enable invalid on extern functions"));
} else {
bool enable;
bool ok = resolve_const_expr_bool(g, import, import->block_context,
&directive_node->data.directive.expr, &enable);
if (!enable || !ok) {
fn_table_entry->is_pure = false;
}
// TODO cause compile error if enable is true and impure fn
}
} else {
add_node_error(g, directive_node,
buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
@ -1037,6 +1064,8 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
assert(enum_type->di_type);
enum_type->deep_const = true;
uint32_t field_count = decl_node->data.struct_decl.fields.length;
enum_type->data.enumeration.field_count = field_count;
@ -1064,6 +1093,10 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
type_enum_field->type_entry = field_type;
type_enum_field->value = i;
if (!field_type->deep_const) {
enum_type->deep_const = false;
}
di_enumerators[i] = LLVMZigCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i);
@ -1224,6 +1257,8 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
assert(struct_type->di_type);
struct_type->deep_const = true;
int field_count = decl_node->data.struct_decl.fields.length;
struct_type->data.structure.src_field_count = field_count;
@ -1247,6 +1282,10 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
type_struct_field->src_index = i;
type_struct_field->gen_index = -1;
if (!field_type->deep_const) {
struct_type->deep_const = false;
}
if (field_type->id == TypeTableEntryIdStruct) {
resolve_struct_type(g, import, field_type);
} else if (field_type->id == TypeTableEntryIdEnum) {
@ -1374,6 +1413,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, '_');
@ -2201,9 +2241,9 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
const_val->ok = false;
}
}
if (!const_val->ok) {
context->fn_entry->struct_val_expr_alloca_list.append(codegen);
}
}
if (!const_val->ok) {
context->fn_entry->struct_val_expr_alloca_list.append(codegen);
}
for (int i = 0; i < actual_field_count; i += 1) {
@ -2355,7 +2395,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
AstNode *decl_node = entry ? entry->value : nullptr;
if (decl_node) {
bool pointer_only = false;
return analyze_decl_ref(g, node, decl_node, pointer_only);
return analyze_decl_ref(g, node, decl_node, pointer_only, context);
} else {
add_node_error(g, node,
buf_sprintf("container '%s' has no member called '%s'",
@ -2382,7 +2422,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
add_error_note(g, msg, decl_node, buf_sprintf("declared here"));
}
bool pointer_only = false;
return analyze_decl_ref(g, node, decl_node, pointer_only);
return analyze_decl_ref(g, node, decl_node, pointer_only, context);
} else {
const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)";
add_node_error(g, node,
@ -2443,6 +2483,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)
{
@ -2607,26 +2653,6 @@ static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNo
return g->builtin_types.entry_num_lit_float;
}
static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *node,
bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), AstNode *op1, AstNode *op2,
TypeTableEntry *resolved_type)
{
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
const_val->ok = true;
if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
add_node_error(g, node,
buf_sprintf("value cannot be represented in any integer type"));
} else {
num_lit_fits_in_other_type(g, node, resolved_type);
}
return resolved_type;
}
static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
BlockContext *context, AstNode *node, Buf *err_name)
{
@ -2642,8 +2668,21 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *
return g->builtin_types.entry_invalid;
}
static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var) {
static bool var_is_pure(VariableTableEntry *var, BlockContext *context) {
if (var->block_context->fn_entry == context->fn_entry) {
// variable was declared in the current function, so it's OK.
return true;
}
return var->is_const && var->type->deep_const;
}
static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var,
BlockContext *context)
{
get_resolved_expr(source_node)->variable = var;
if (!var_is_pure(var, context)) {
mark_impure_fn(context);
}
if (var->is_const && var->val_node) {
ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val;
if (other_const_val->ok) {
@ -2654,7 +2693,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl
}
static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node,
bool pointer_only)
bool pointer_only, BlockContext *block_context)
{
resolve_top_level_decl(g, decl_node, pointer_only);
TopLevelDecl *tld = get_as_top_level_decl(decl_node);
@ -2664,7 +2703,7 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod
if (decl_node->type == NodeTypeVariableDeclaration) {
VariableTableEntry *var = decl_node->data.variable_declaration.variable;
return analyze_var_ref(g, source_node, var);
return analyze_var_ref(g, source_node, var, block_context);
} else if (decl_node->type == NodeTypeFnProto) {
if (decl_node->data.fn_proto.generic_params.length > 0) {
TypeTableEntry *type_entry = decl_node->data.fn_proto.generic_fn_type;
@ -2700,12 +2739,13 @@ 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, context);
return var_type;
}
AstNode *decl_node = find_decl(context, variable_name);
if (decl_node) {
return analyze_decl_ref(g, node, decl_node, pointer_only);
return analyze_decl_ref(g, node, decl_node, pointer_only, context);
}
if (import->any_imports_failed) {
@ -2840,71 +2880,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 +2919,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 +2949,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,6 +2988,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);
// not const ok because expression has side effects
return g->builtin_types.entry_void;
}
case BinOpTypeBoolOr:
@ -3106,37 +3054,23 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
return resolved_type;
}
if (bin_op_type == BinOpTypeAdd) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_add, *op1, *op2, resolved_type);
} else if (bin_op_type == BinOpTypeSub) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_sub, *op1, *op2, resolved_type);
} else if (bin_op_type == BinOpTypeMult) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_mul, *op1, *op2, resolved_type);
} else if (bin_op_type == BinOpTypeDiv) {
ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||
(is_float && op2_val->data.x_bignum.data.x_float == 0.0))
{
ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
int err;
if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type,
op2_val, resolved_type, out_val)))
{
if (err == ErrorDivByZero) {
add_node_error(g, node, buf_sprintf("division by zero is undefined"));
return g->builtin_types.entry_invalid;
} else {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_div, *op1, *op2, resolved_type);
} else if (err == ErrorOverflow) {
add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type"));
return g->builtin_types.entry_invalid;
}
} else if (bin_op_type == BinOpTypeMod) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_mod, *op1, *op2, resolved_type);
} else if (bin_op_type == BinOpTypeBinOr) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_or, *op1, *op2, resolved_type);
} else if (bin_op_type == BinOpTypeBinAnd) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_and, *op1, *op2, resolved_type);
} else if (bin_op_type == BinOpTypeBinXor) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_xor, *op1, *op2, resolved_type);
} else if (bin_op_type == BinOpTypeBitShiftLeft) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_shl, *op1, *op2, resolved_type);
} else if (bin_op_type == BinOpTypeBitShiftRight) {
return resolve_expr_const_val_as_bignum_op(g, node, bignum_shr, *op1, *op2, resolved_type);
} else {
zig_unreachable();
return g->builtin_types.entry_invalid;
}
num_lit_fits_in_other_type(g, node, resolved_type);
return resolved_type;
}
case BinOpTypeUnwrapMaybe:
{
@ -3720,6 +3654,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),
@ -3762,17 +3700,6 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import,
node, then_node, else_node, cond_is_const, cond_bool_val);
}
static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) {
assert(int_type->id == TypeTableEntryIdInt);
for (int i = 0; i < CIntTypeCount; i += 1) {
if (int_type == g->builtin_types.entry_c_int[i]) {
return true;
}
}
return false;
}
static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
AstNode *node, const char *err_format, bool is_max)
{
@ -3781,67 +3708,15 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
AstNode *type_node = node->data.fn_call_expr.params.at(0);
TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
if (type_entry->id == TypeTableEntryIdInvalid) {
return g->builtin_types.entry_invalid;
} else if (type_entry->id == TypeTableEntryIdInt) {
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
const_val->ok = true;
const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry);
if (is_max) {
if (type_entry->data.integral.is_signed) {
int64_t val;
if (type_entry->data.integral.bit_count == 64) {
val = INT64_MAX;
} else if (type_entry->data.integral.bit_count == 32) {
val = INT32_MAX;
} else if (type_entry->data.integral.bit_count == 16) {
val = INT16_MAX;
} else if (type_entry->data.integral.bit_count == 8) {
val = INT8_MAX;
} else {
zig_unreachable();
}
bignum_init_signed(&const_val->data.x_bignum, val);
} else {
uint64_t val;
if (type_entry->data.integral.bit_count == 64) {
val = UINT64_MAX;
} else if (type_entry->data.integral.bit_count == 32) {
val = UINT32_MAX;
} else if (type_entry->data.integral.bit_count == 16) {
val = UINT16_MAX;
} else if (type_entry->data.integral.bit_count == 8) {
val = UINT8_MAX;
} else {
zig_unreachable();
}
bignum_init_unsigned(&const_val->data.x_bignum, val);
}
} else {
if (type_entry->data.integral.is_signed) {
int64_t val;
if (type_entry->data.integral.bit_count == 64) {
val = INT64_MIN;
} else if (type_entry->data.integral.bit_count == 32) {
val = INT32_MIN;
} else if (type_entry->data.integral.bit_count == 16) {
val = INT16_MIN;
} else if (type_entry->data.integral.bit_count == 8) {
val = INT8_MIN;
} else {
zig_unreachable();
}
bignum_init_signed(&const_val->data.x_bignum, val);
} else {
bignum_init_unsigned(&const_val->data.x_bignum, 0);
}
}
} else if (type_entry->id == TypeTableEntryIdInt ||
type_entry->id == TypeTableEntryIdFloat ||
type_entry->id == TypeTableEntryIdBool)
{
eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
return type_entry;
} else if (type_entry->id == TypeTableEntryIdFloat) {
zig_panic("TODO analyze_min_max_value float");
return type_entry;
} else if (type_entry->id == TypeTableEntryIdBool) {
return resolve_expr_const_val_as_bool(g, node, is_max, false);
} else {
add_node_error(g, node,
buf_sprintf(err_format, buf_ptr(&type_entry->name)));
@ -3849,92 +3724,19 @@ 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<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 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, wanted_type);
}
if (need_alloca) {
if (context->fn_entry) {
context->fn_entry->cast_alloca_list.append(node);
@ -4630,13 +4432,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,31 +4452,49 @@ 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;
}
bool ok_invocation = true;
if (fn_type->data.fn.fn_type_id.is_var_args) {
if (actual_param_count < src_param_count) {
ok_invocation = false;
add_node_error(g, node,
buf_sprintf("expected at least %d arguments, got %d", src_param_count, actual_param_count));
}
} else if (src_param_count != actual_param_count) {
ok_invocation = false;
add_node_error(g, node,
buf_sprintf("expected %d arguments, got %d", src_param_count, actual_param_count));
}
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 +4503,27 @@ 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 (ok_invocation && fn_table_entry && fn_table_entry->is_pure) {
if (fn_table_entry->anal_state == FnAnalStateReady) {
analyze_fn_body(g, fn_table_entry);
}
if (all_args_const_expr) {
if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) {
ConstExprValue *result_val = &get_resolved_expr(node)->const_val;
if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) {
// function evaluation generated an error
return g->builtin_types.entry_invalid;
}
return return_type;
}
}
}
if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure) {
// 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 +4532,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,
@ -4721,7 +4564,7 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp
generic_fn_type_id->generic_param_count = actual_param_count;
generic_fn_type_id->generic_params = allocate<GenericParamValue>(actual_param_count);
BlockContext *child_context = import->block_context;
BlockContext *child_context = decl_node->owner->block_context;
for (int i = 0; i < actual_param_count; i += 1) {
AstNode *generic_param_decl_node = decl_node->data.fn_proto.generic_params.at(i);
assert(generic_param_decl_node->type == NodeTypeParamDecl);
@ -4861,17 +4704,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->data.field_access_expr.struct_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 +5264,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 +5486,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;
@ -5676,6 +5523,10 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
param_decl_node->data.param_decl.variable = var;
var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
if (!type->deep_const) {
fn_table_entry->is_pure = false;
}
}
TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.return_type;
@ -5697,6 +5548,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 +5858,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);
}
}
}

View File

@ -326,9 +326,9 @@ static void render_node(AstRender *ar, AstNode *node) {
AstNode *statement = node->data.block.statements.at(i);
print_indent(ar);
render_node(ar, statement);
fprintf(ar->f, ";\n");
}
ar->indent -= ar->indent_size;
fprintf(ar->f, "\n");
print_indent(ar);
fprintf(ar->f, "}");
break;
@ -438,7 +438,11 @@ static void render_node(AstRender *ar, AstNode *node) {
fprintf(ar->f, ")");
break;
case NodeTypeArrayAccessExpr:
zig_panic("TODO");
render_node(ar, node->data.array_access_expr.array_ref_expr);
fprintf(ar->f, "[");
render_node(ar, node->data.array_access_expr.subscript);
fprintf(ar->f, "]");
break;
case NodeTypeSliceExpr:
zig_panic("TODO");
case NodeTypeFieldAccessExpr:

View File

@ -71,6 +71,11 @@ bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed) {
}
}
void bignum_truncate(BigNum *bn, int bit_count) {
assert(bn->kind == BigNumKindInt);
bn->data.x_uint &= (1LL << bit_count) - 1;
}
uint64_t bignum_to_twos_complement(BigNum *bn) {
assert(bn->kind == BigNumKindInt);

View File

@ -47,6 +47,8 @@ void bignum_negate(BigNum *dest, BigNum *op);
void bignum_cast_to_float(BigNum *dest, BigNum *op);
void bignum_cast_to_int(BigNum *dest, BigNum *op);
void bignum_truncate(BigNum *dest, int bit_count);
// returns the result of the comparison
bool bignum_cmp_eq(BigNum *op1, BigNum *op2);
bool bignum_cmp_neq(BigNum *op1, BigNum *op2);

View File

@ -1074,15 +1074,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lva
AstNode *struct_expr = node->data.field_access_expr.struct_expr;
TypeTableEntry *struct_type = get_expr_type(struct_expr);
Buf *name = &node->data.field_access_expr.field_name;
if (struct_type->id == TypeTableEntryIdArray) {
if (buf_eql_str(name, "len")) {
return LLVMConstInt(g->builtin_types.entry_isize->type_ref,
struct_type->data.array.len, false);
} else {
zig_panic("gen_field_access_expr bad array field");
}
Buf *name = &node->data.field_access_expr.field_name;
assert(buf_eql_str(name, "len"));
return LLVMConstInt(g->builtin_types.entry_isize->type_ref,
struct_type->data.array.len, false);
} else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
{
@ -3036,6 +3033,7 @@ static void gen_const_globals(CodeGen *g) {
} else {
expr->const_llvm_val = gen_const_val(g, type_entry, const_val);
}
assert(expr->const_llvm_val);
}
}
@ -3466,23 +3464,27 @@ static void define_builtin_types(CodeGen *g) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNamespace);
buf_init_from_str(&entry->name, "(namespace)");
entry->zero_bits = true;
entry->deep_const = true;
g->builtin_types.entry_namespace = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat);
buf_init_from_str(&entry->name, "(float literal)");
entry->zero_bits = true;
entry->deep_const = true;
g->builtin_types.entry_num_lit_float = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt);
buf_init_from_str(&entry->name, "(integer literal)");
entry->zero_bits = true;
entry->deep_const = true;
g->builtin_types.entry_num_lit_int = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit);
buf_init_from_str(&entry->name, "(undefined)");
entry->deep_const = true;
g->builtin_types.entry_undef = entry;
}
@ -3492,6 +3494,7 @@ static void define_builtin_types(CodeGen *g) {
for (;;) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(size_in_bits);
entry->deep_const = true;
const char u_or_i = is_signed ? 'i' : 'u';
buf_resize(&entry->name, 0);
@ -3537,6 +3540,7 @@ static void define_builtin_types(CodeGen *g) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(size_in_bits);
entry->deep_const = true;
buf_init_from_str(&entry->name, info->name);
@ -3556,6 +3560,7 @@ static void define_builtin_types(CodeGen *g) {
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
entry->type_ref = LLVMInt1Type();
entry->deep_const = true;
buf_init_from_str(&entry->name, "bool");
uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
@ -3568,6 +3573,7 @@ static void define_builtin_types(CodeGen *g) {
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->deep_const = true;
entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
buf_init_from_str(&entry->name, "isize");
entry->data.integral.is_signed = true;
@ -3584,6 +3590,7 @@ static void define_builtin_types(CodeGen *g) {
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->deep_const = true;
entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
buf_init_from_str(&entry->name, "usize");
entry->data.integral.is_signed = false;
@ -3600,6 +3607,7 @@ static void define_builtin_types(CodeGen *g) {
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
entry->deep_const = true;
entry->type_ref = LLVMFloatType();
buf_init_from_str(&entry->name, "f32");
entry->data.floating.bit_count = 32;
@ -3615,6 +3623,7 @@ static void define_builtin_types(CodeGen *g) {
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
entry->deep_const = true;
entry->type_ref = LLVMDoubleType();
buf_init_from_str(&entry->name, "f64");
entry->data.floating.bit_count = 64;
@ -3630,6 +3639,7 @@ static void define_builtin_types(CodeGen *g) {
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
entry->deep_const = true;
entry->type_ref = LLVMX86FP80Type();
buf_init_from_str(&entry->name, "c_long_double");
entry->data.floating.bit_count = 80;
@ -3645,6 +3655,7 @@ static void define_builtin_types(CodeGen *g) {
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
entry->deep_const = true;
entry->type_ref = LLVMVoidType();
entry->zero_bits = true;
buf_init_from_str(&entry->name, "void");
@ -3657,6 +3668,7 @@ static void define_builtin_types(CodeGen *g) {
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUnreachable);
entry->deep_const = true;
entry->type_ref = LLVMVoidType();
entry->zero_bits = true;
buf_init_from_str(&entry->name, "unreachable");
@ -3666,6 +3678,7 @@ static void define_builtin_types(CodeGen *g) {
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMetaType);
entry->deep_const = true;
buf_init_from_str(&entry->name, "type");
entry->zero_bits = true;
g->builtin_types.entry_type = entry;
@ -3688,6 +3701,7 @@ static void define_builtin_types(CodeGen *g) {
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError);
entry->deep_const = true;
buf_init_from_str(&entry->name, "error");
// TODO allow overriding this type and keep track of max value and emit an
@ -3703,6 +3717,7 @@ static void define_builtin_types(CodeGen *g) {
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
entry->deep_const = true;
entry->zero_bits = true; // only allowed at compile time
buf_init_from_str(&entry->name, "@OS");
uint32_t field_count = target_os_count();
@ -3728,6 +3743,7 @@ static void define_builtin_types(CodeGen *g) {
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
entry->deep_const = true;
entry->zero_bits = true; // only allowed at compile time
buf_init_from_str(&entry->name, "@Arch");
uint32_t field_count = target_arch_count();
@ -3759,6 +3775,7 @@ static void define_builtin_types(CodeGen *g) {
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
entry->deep_const = true;
entry->zero_bits = true; // only allowed at compile time
buf_init_from_str(&entry->name, "@Environ");
uint32_t field_count = target_environ_count();

View File

@ -12,6 +12,8 @@ const char *err_str(int err) {
case ErrorFileNotFound: return "file not found";
case ErrorFileSystem: return "file system error";
case ErrorFileTooBig: return "file too big";
case ErrorDivByZero: return "division by zero";
case ErrorOverflow: return "overflow";
}
return "(invalid error)";
}

View File

@ -19,6 +19,8 @@ enum Error {
ErrorFileNotFound,
ErrorFileSystem,
ErrorFileTooBig,
ErrorDivByZero,
ErrorOverflow
};
const char *err_str(int err);

1191
src/eval.cpp Normal file

File diff suppressed because it is too large Load Diff

26
src/eval.hpp Normal file
View File

@ -0,0 +1,26 @@
/*
* 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);
int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val);
void eval_const_expr_implicit_cast(CastOp cast_op,
ConstExprValue *other_val, TypeTableEntry *other_type,
ConstExprValue *const_val, TypeTableEntry *new_type);
void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max);
#endif

View File

@ -2927,6 +2927,7 @@ static void clone_subtree_list(ZigList<AstNode *> *dest, ZigList<AstNode *> *src
dest->resize(src->length);
for (int i = 0; i < src->length; i += 1) {
dest->at(i) = ast_clone_subtree(src->at(i), next_node_index);
dest->at(i)->parent_field = &dest->at(i);
}
}
@ -2958,11 +2959,12 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) {
memcpy(new_node, old_node, sizeof(AstNode));
new_node->create_index = *next_node_index;
*next_node_index += 1;
new_node->parent_field = nullptr;
switch (new_node->type) {
case NodeTypeRoot:
clone_subtree_list(&new_node->data.root.top_level_decls, &old_node->data.root.top_level_decls,
next_node_index);
clone_subtree_list(&new_node->data.root.top_level_decls,
&old_node->data.root.top_level_decls, next_node_index);
break;
case NodeTypeFnProto:
clone_subtree_tld(&new_node->data.fn_proto.top_level_decl, &old_node->data.fn_proto.top_level_decl,
@ -3036,15 +3038,21 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) {
// none
break;
case NodeTypePrefixOpExpr:
clone_subtree_field(&new_node->data.prefix_op_expr.primary_expr, old_node->data.prefix_op_expr.primary_expr, next_node_index);
clone_subtree_field(&new_node->data.prefix_op_expr.primary_expr,
old_node->data.prefix_op_expr.primary_expr, next_node_index);
break;
case NodeTypeFnCallExpr:
clone_subtree_field(&new_node->data.fn_call_expr.fn_ref_expr, old_node->data.fn_call_expr.fn_ref_expr, next_node_index);
clone_subtree_list(&new_node->data.fn_call_expr.params, &old_node->data.fn_call_expr.params, next_node_index);
assert(!old_node->data.fn_call_expr.resolved_expr.has_global_const);
clone_subtree_field(&new_node->data.fn_call_expr.fn_ref_expr,
old_node->data.fn_call_expr.fn_ref_expr, next_node_index);
clone_subtree_list(&new_node->data.fn_call_expr.params,
&old_node->data.fn_call_expr.params, next_node_index);
break;
case NodeTypeArrayAccessExpr:
clone_subtree_field(&new_node->data.array_access_expr.array_ref_expr, old_node->data.array_access_expr.array_ref_expr, next_node_index);
clone_subtree_field(&new_node->data.array_access_expr.subscript, old_node->data.array_access_expr.subscript, next_node_index);
clone_subtree_field(&new_node->data.array_access_expr.array_ref_expr,
old_node->data.array_access_expr.array_ref_expr, next_node_index);
clone_subtree_field(&new_node->data.array_access_expr.subscript,
old_node->data.array_access_expr.subscript, next_node_index);
break;
case NodeTypeSliceExpr:
clone_subtree_field(&new_node->data.slice_expr.array_ref_expr, old_node->data.slice_expr.array_ref_expr, next_node_index);

View File

@ -7,6 +7,7 @@ pub struct Rand {
index: isize,
/// Initialize random state with the given seed.
#static_eval_enable(false)
pub fn init(seed: u32) -> Rand {
var r: Rand = undefined;
r.index = 0;

View File

@ -218,6 +218,7 @@ pub fn bar_function() {
)SOURCE");
add_source_file(tc, "other.zig", R"SOURCE(
#static_eval_enable(false)
pub fn foo_function() -> bool {
// this one conflicts with the one from foo
return true;
@ -406,20 +407,6 @@ pub fn main(args: [][]u8) -> %void {
}
)SOURCE", "loop\nloop\nloop\nloop\n");
add_simple_case("implicit cast after unreachable", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
const x = outer();
if (x == 1234) {
%%io.stdout.printf("OK\n");
}
}
fn inner() -> i32 { 1234 }
fn outer() -> isize {
return inner();
}
)SOURCE", "OK\n");
add_simple_case("@sizeof() and @typeof()", R"SOURCE(
const io = @import("std").io;
const x: u16 = 13;
@ -431,23 +418,6 @@ pub fn main(args: [][]u8) -> %void {
}
)SOURCE", "2\n");
add_simple_case("member functions", R"SOURCE(
const io = @import("std").io;
struct Rand {
seed: u32,
pub fn get_seed(r: Rand) -> u32 {
r.seed
}
}
pub fn main(args: [][]u8) -> %void {
const r = Rand {.seed = 1234};
if (r.get_seed() != 1234) {
%%io.stdout.printf("BAD seed\n");
}
%%io.stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("pointer dereferencing", R"SOURCE(
const io = @import("std").io;
@ -565,24 +535,6 @@ pub fn main(args: [][]u8) -> %void {
"min i64: -9223372036854775808\n");
add_simple_case("else if expression", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
if (f(1) == 1) {
%%io.stdout.printf("OK\n");
}
}
fn f(c: u8) -> u8 {
if (c == 0) {
0
} else if (c == 1) {
1
} else {
2
}
}
)SOURCE", "OK\n");
add_simple_case("overflow intrinsics", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
@ -730,29 +682,6 @@ pub fn main(args: [][]u8) -> %void {
}
)SOURCE", "OK\n");
add_simple_case("%% binary operator", R"SOURCE(
const io = @import("std").io;
error ItBroke;
fn g(x: bool) -> %isize {
if (x) {
error.ItBroke
} else {
10
}
}
pub fn main(args: [][]u8) -> %void {
const a = g(true) %% 3;
const b = g(false) %% 3;
if (a != 3) {
%%io.stdout.printf("BAD\n");
}
if (b != 10) {
%%io.stdout.printf("BAD\n");
}
%%io.stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("string concatenation", R"SOURCE(
const io = @import("std").io;
pub fn main(args: [][]u8) -> %void {
@ -808,54 +737,6 @@ pub fn main(args: [][]u8) -> %void {
}
)SOURCE", "OK\n");
add_simple_case("unwrap simple value from error", R"SOURCE(
const io = @import("std").io;
fn do() -> %isize {
13
}
pub fn main(args: [][]u8) -> %void {
const i = %%do();
if (i != 13) {
%%io.stdout.printf("BAD\n");
}
%%io.stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("store member function in variable", R"SOURCE(
const io = @import("std").io;
struct Foo {
x: i32,
fn member(foo: Foo) -> i32 { foo.x }
}
pub fn main(args: [][]u8) -> %void {
const instance = Foo { .x = 1234, };
const member_fn = Foo.member;
const result = member_fn(instance);
if (result != 1234) {
%%io.stdout.printf("BAD\n");
}
%%io.stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("call member function directly", R"SOURCE(
const io = @import("std").io;
struct Foo {
x: i32,
fn member(foo: Foo) -> i32 { foo.x }
}
pub fn main(args: [][]u8) -> %void {
const instance = Foo { .x = 1234, };
const result = Foo.member(instance);
if (result != 1234) {
%%io.stdout.printf("BAD\n");
}
%%io.stdout.printf("OK\n");
}
)SOURCE", "OK\n");
add_simple_case("call result of if else expression", R"SOURCE(
const io = @import("std").io;
fn a() -> []u8 { "a\n" }
@ -1496,7 +1377,8 @@ fn a(x: i32) {
struct Foo {
y: [get()]u8,
}
fn get() -> isize { 1 }
var global_var: isize = 1;
fn get() -> isize { global_var }
)SOURCE", 1, ".tmp_source.zig:3:9: error: unable to evaluate constant expression");
@ -1599,6 +1481,31 @@ fn foo() {
const pointer = &array[0];
}
)SOURCE", 1, ".tmp_source.zig:4:27: error: out of bounds array access");
add_compile_fail_case("compile time division by zero", R"SOURCE(
const x = foo(0);
fn foo(x: i32) -> i32 {
1 / x
}
)SOURCE", 3,
".tmp_source.zig:3:1: error: function evaluation caused division by zero",
".tmp_source.zig:2:14: note: called from here",
".tmp_source.zig:4:7: note: division by zero here");
add_compile_fail_case("branch on undefined value", R"SOURCE(
const x = if (undefined) true else false;
)SOURCE", 1, ".tmp_source.zig:2:15: error: branch on undefined value");
add_compile_fail_case("endless loop in function evaluation", R"SOURCE(
const seventh_fib_number = fibbonaci(7);
fn fibbonaci(x: i32) -> i32 {
return fibbonaci(x - 1) + fibbonaci(x - 2);
}
)SOURCE", 3,
".tmp_source.zig:3:1: error: function evaluation exceeded 1000 branches",
".tmp_source.zig:2:37: note: called from here",
".tmp_source.zig:4:40: note: quota exceeded here");
}
//////////////////////////////////////////////////////////////////////////////
@ -1760,7 +1667,7 @@ extern void (*fn_ptr)(void);
)SOURCE", 2,
"pub extern var fn_ptr: ?extern fn();",
R"SOURCE(pub inline fn foo() {
(??fn_ptr)()
(??fn_ptr)();
})SOURCE");

View File

@ -123,18 +123,18 @@ fn short_circuit() {
var hit_3 = false;
var hit_4 = false;
if (true || { assert(false); false }) {
if (true || {assert_runtime(false); false}) {
hit_1 = true;
}
if (false || { hit_2 = true; false }) {
assert(false);
assert_runtime(false);
}
if (true && { hit_3 = true; false }) {
%%io.stdout.printf("BAD 3\n");
assert_runtime(false);
}
if (false && { assert(false); false }) {
assert(false);
if (false && {assert_runtime(false); false}) {
assert_runtime(false);
} else {
hit_4 = true;
}
@ -144,6 +144,11 @@ fn short_circuit() {
assert(hit_4);
}
#static_eval_enable(false)
fn assert_runtime(b: bool) {
if (!b) unreachable{}
}
#attribute("test")
fn modify_operators() {
var i : i32 = 0;
@ -410,9 +415,7 @@ error err2;
#attribute("test")
fn fn_call_of_struct_field() {
if (call_struct_field(Foo {.ptr = a_func,}) != 13) {
unreachable{};
}
assert(call_struct_field(Foo {.ptr = a_func,}) == 13);
}
struct Foo {
@ -509,6 +512,7 @@ enum Fruit {
Orange,
Banana,
}
#static_eval_enable(false)
fn non_const_switch_on_enum(fruit: Fruit) {
switch (fruit) {
Apple => unreachable{},
@ -521,6 +525,7 @@ fn non_const_switch_on_enum(fruit: Fruit) {
fn switch_statement() {
non_const_switch(SwitchStatmentFoo.C);
}
#static_eval_enable(false)
fn non_const_switch(foo: SwitchStatmentFoo) {
const val: i32 = switch (foo) {
A => 1,
@ -549,6 +554,7 @@ enum SwitchProngWithVarEnum {
Two: f32,
Meh,
}
#static_eval_enable(false)
fn switch_prong_with_var_fn(a: SwitchProngWithVarEnum) {
switch(a) {
One => |x| {
@ -569,6 +575,7 @@ fn err_return_in_assignment() {
%%do_err_return_in_assignment();
}
#static_eval_enable(false)
fn do_err_return_in_assignment() -> %void {
var x : i32 = undefined;
x = %return make_a_non_err();
@ -608,7 +615,7 @@ fn explicit_cast_maybe_pointers() {
#attribute("test")
fn const_expr_eval_on_single_expr_blocks() {
if (const_expr_eval_on_single_expr_blocks_fn(1, true) != 3) unreachable{}
assert(const_expr_eval_on_single_expr_blocks_fn(1, true) == 3);
}
fn const_expr_eval_on_single_expr_blocks_fn(x: i32, b: bool) -> i32 {
@ -736,6 +743,7 @@ fn generic_malloc_free() {
mem_free(u8)(a);
}
const some_mem : [100]u8 = undefined;
#static_eval_enable(false)
fn mem_alloc(T: type)(n: isize) -> %[]T {
return (&T)(&some_mem[0])[0...n];
}
@ -789,6 +797,7 @@ var goto_counter: i32 = 0;
fn goto_leave_defer_scope() {
test_goto_leave_defer_scope(true);
}
#static_eval_enable(false)
fn test_goto_leave_defer_scope(b: bool) {
var it_worked = false;
@ -820,3 +829,160 @@ fn cast_small_unsigned_to_larger_signed() {
}
fn cast_small_unsigned_to_larger_signed_1(x: u8) -> i16 { x }
fn cast_small_unsigned_to_larger_signed_2(x: u16) -> isize { x }
#attribute("test")
fn implicit_cast_after_unreachable() {
assert(outer() == 1234);
}
fn inner() -> i32 { 1234 }
fn outer() -> isize {
return inner();
}
#attribute("test")
fn else_if_expression() {
assert(else_if_expression_f(1) == 1);
}
fn else_if_expression_f(c: u8) -> u8 {
if (c == 0) {
0
} else if (c == 1) {
1
} else {
2
}
}
#attribute("test")
fn err_binary_operator() {
const a = err_binary_operator_g(true) %% 3;
const b = err_binary_operator_g(false) %% 3;
assert(a == 3);
assert(b == 10);
}
error ItBroke;
fn err_binary_operator_g(x: bool) -> %isize {
if (x) {
error.ItBroke
} else {
10
}
}
#attribute("test")
fn unwrap_simple_value_from_error() {
const i = %%unwrap_simple_value_from_error_do();
assert(i == 13);
}
fn unwrap_simple_value_from_error_do() -> %isize { 13 }
#attribute("test")
fn store_member_function_in_variable() {
const instance = MemberFnTestFoo { .x = 1234, };
const member_fn = MemberFnTestFoo.member;
const result = member_fn(instance);
assert(result == 1234);
}
struct MemberFnTestFoo {
x: i32,
fn member(foo: MemberFnTestFoo) -> i32 { foo.x }
}
#attribute("test")
fn call_member_function_directly() {
const instance = MemberFnTestFoo { .x = 1234, };
const result = MemberFnTestFoo.member(instance);
assert(result == 1234);
}
#attribute("test")
fn member_functions() {
const r = MemberFnRand {.seed = 1234};
assert(r.get_seed() == 1234);
}
struct MemberFnRand {
seed: u32,
pub fn get_seed(r: MemberFnRand) -> u32 {
r.seed
}
}
#attribute("test")
fn static_function_evaluation() {
assert(statically_added_number == 3);
}
const statically_added_number = static_add(1, 2);
fn static_add(a: i32, b: i32) -> i32 { a + b }
#attribute("test")
fn statically_initalized_list() {
assert(static_point_list[0].x == 1);
assert(static_point_list[0].y == 2);
assert(static_point_list[1].x == 3);
assert(static_point_list[1].y == 4);
}
struct Point {
x: i32,
y: i32,
}
const static_point_list = []Point { make_point(1, 2), make_point(3, 4) };
fn make_point(x: i32, y: i32) -> Point {
return Point {
.x = x,
.y = y,
};
}
#attribute("test")
fn static_eval_recursive() {
assert(seventh_fib_number == 21);
}
const seventh_fib_number = fibbonaci(7);
fn fibbonaci(x: i32) -> i32 {
if (x <= 1) return 1;
return fibbonaci(x - 1) + fibbonaci(x - 2);
}
#attribute("test")
fn static_eval_while() {
assert(static_eval_while_number == 1);
}
const static_eval_while_number = static_while_loop_1();
fn static_while_loop_1() -> i32 {
return while_loop_2();
}
fn static_while_loop_2() -> i32 {
while (true) {
return 1;
}
}
#attribute("test")
fn static_eval_list_init() {
assert(static_vec3.data[2] == 1.0);
}
const static_vec3 = vec3(0.0, 0.0, 1.0);
pub struct Vec3 {
data: [3]f32,
}
pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 {
Vec3 {
.data = []f32 { x, y, z, },
}
}
#attribute("test")
fn generic_fn_with_implicit_cast() {
assert(get_first_byte(u8)([]u8 {13}) == 13);
assert(get_first_byte(u16)([]u16 {0, 13}) == 0);
}
fn get_byte(ptr: ?&u8) -> u8 {*??ptr}
fn get_first_byte(T: type)(mem: []T) -> u8 {
get_byte((&u8)(&mem[0]))
}