aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2016-04-14 10:40:08 -0700
committerAndrew Kelley <superjoe30@gmail.com>2016-04-14 10:40:08 -0700
commit579856e502eab87ea8a73a76dc63a2108e5a8cc8 (patch)
treefeb34b0985b29f45073ac81e21982a368e0a3542
parent5a479720ec5786145dac6c85deae4e322bd5972e (diff)
parentfcedc35551cc6b14756499414e47c33004de3be4 (diff)
downloadzig-579856e502eab87ea8a73a76dc63a2108e5a8cc8.tar.gz
zig-579856e502eab87ea8a73a76dc63a2108e5a8cc8.zip
Merge branch 'eval'
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/all_types.hpp39
-rw-r--r--src/analyze.cpp497
-rw-r--r--src/ast_render.cpp8
-rw-r--r--src/bignum.cpp5
-rw-r--r--src/bignum.hpp2
-rw-r--r--src/codegen.cpp31
-rw-r--r--src/error.cpp2
-rw-r--r--src/error.hpp2
-rw-r--r--src/eval.cpp1191
-rw-r--r--src/eval.hpp26
-rw-r--r--src/parser.cpp22
-rw-r--r--std/rand.zig1
-rw-r--r--test/run_tests.cpp151
-rw-r--r--test/self_hosted.zig184
15 files changed, 1693 insertions, 469 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f3cba1c289..cf0f41dd06 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -41,6 +41,7 @@ set(ZIG_SOURCES
"${CMAKE_SOURCE_DIR}/src/bignum.cpp"
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
"${CMAKE_SOURCE_DIR}/src/parser.cpp"
+ "${CMAKE_SOURCE_DIR}/src/eval.cpp"
"${CMAKE_SOURCE_DIR}/src/analyze.cpp"
"${CMAKE_SOURCE_DIR}/src/codegen.cpp"
"${CMAKE_SOURCE_DIR}/src/buffer.cpp"
diff --git a/src/all_types.hpp b/src/all_types.hpp
index e71330be4a..c6e0dc3d17 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -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,
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 62b560f0c3..244bdc5fc6 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -13,6 +13,7 @@
#include "parseh.hpp"
#include "config.h"
#include "ast_render.hpp"
+#include "eval.hpp"
static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node);
@@ -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);
- }
- }
- return type_entry;
- } else if (type_entry->id == TypeTableEntryIdFloat) {
- zig_panic("TODO analyze_min_max_value float");
+ } 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 == 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);
+ }
}
}
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 59a287b886..6b88579b49 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -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:
diff --git a/src/bignum.cpp b/src/bignum.cpp
index 55c10e248e..7046ff4874 100644
--- a/src/bignum.cpp
+++ b/src/bignum.cpp
@@ -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);
diff --git a/src/bignum.hpp b/src/bignum.hpp
index 047169085a..ac1f75e791 100644
--- a/src/bignum.hpp
+++ b/src/bignum.hpp
@@ -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);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index a0fdc7b749..70648d252f 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -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();
diff --git a/src/error.cpp b/src/error.cpp
index 7690dd0776..caed95f693 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -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)";
}
diff --git a/src/error.hpp b/src/error.hpp
index 742f30700e..7fea3ccfbb 100644
--- a/src/error.hpp
+++ b/src/error.hpp
@@ -19,6 +19,8 @@ enum Error {
ErrorFileNotFound,
ErrorFileSystem,
ErrorFileTooBig,
+ ErrorDivByZero,
+ ErrorOverflow
};
const char *err_str(int err);
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, &param_val)) return true;
+ eval_const_expr_implicit_cast(cast_op, &param_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;
+}
+
diff --git a/src/eval.hpp b/src/eval.hpp
new file mode 100644
index 0000000000..e0236b7146
--- /dev/null
+++ b/src/eval.hpp
@@ -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
diff --git a/src/parser.cpp b/src/parser.cpp
index 3c9bdaa78c..0bd2f57830 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -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);
diff --git a/std/rand.zig b/std/rand.zig
index 9dee46fbe5..1fcfd78f75 100644
--- a/std/rand.zig
+++ b/std/rand.zig
@@ -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;
diff --git a/test/run_tests.cpp b/test/run_tests.cpp
index 4143fa000e..30fc1fdcf8 100644
--- a/test/run_tests.cpp
+++ b/test/run_tests.cpp
@@ -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");
diff --git a/test/self_hosted.zig b/test/self_hosted.zig
index 7d3f0b8e46..33b1834d96 100644
--- a/test/self_hosted.zig
+++ b/test/self_hosted.zig
@@ -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]))
+}