Merge branch 'eval'
commit
579856e502
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
489
src/analyze.cpp
489
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,10 +2241,10 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < actual_field_count; i += 1) {
|
||||
if (field_use_counts[i] == 0) {
|
||||
|
@ -2355,7 +2395,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
|
|||
AstNode *decl_node = entry ? entry->value : nullptr;
|
||||
if (decl_node) {
|
||||
bool pointer_only = false;
|
||||
return analyze_decl_ref(g, node, decl_node, pointer_only);
|
||||
return analyze_decl_ref(g, node, decl_node, pointer_only, context);
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("container '%s' has no member called '%s'",
|
||||
|
@ -2382,7 +2422,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
|
|||
add_error_note(g, msg, decl_node, buf_sprintf("declared here"));
|
||||
}
|
||||
bool pointer_only = false;
|
||||
return analyze_decl_ref(g, node, decl_node, pointer_only);
|
||||
return analyze_decl_ref(g, node, decl_node, pointer_only, context);
|
||||
} else {
|
||||
const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)";
|
||||
add_node_error(g, node,
|
||||
|
@ -2443,6 +2483,12 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import,
|
|||
return return_type;
|
||||
}
|
||||
|
||||
static void mark_impure_fn(BlockContext *context) {
|
||||
if (context->fn_entry) {
|
||||
context->fn_entry->is_pure = false;
|
||||
}
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
AstNode *node)
|
||||
{
|
||||
|
@ -2607,26 +2653,6 @@ static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNo
|
|||
return g->builtin_types.entry_num_lit_float;
|
||||
}
|
||||
|
||||
static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *node,
|
||||
bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), AstNode *op1, AstNode *op2,
|
||||
TypeTableEntry *resolved_type)
|
||||
{
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
|
||||
ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
|
||||
|
||||
const_val->ok = true;
|
||||
|
||||
if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("value cannot be represented in any integer type"));
|
||||
} else {
|
||||
num_lit_fits_in_other_type(g, node, resolved_type);
|
||||
}
|
||||
|
||||
return resolved_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
|
||||
BlockContext *context, AstNode *node, Buf *err_name)
|
||||
{
|
||||
|
@ -2642,8 +2668,21 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *
|
|||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var) {
|
||||
static bool var_is_pure(VariableTableEntry *var, BlockContext *context) {
|
||||
if (var->block_context->fn_entry == context->fn_entry) {
|
||||
// variable was declared in the current function, so it's OK.
|
||||
return true;
|
||||
}
|
||||
return var->is_const && var->type->deep_const;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var,
|
||||
BlockContext *context)
|
||||
{
|
||||
get_resolved_expr(source_node)->variable = var;
|
||||
if (!var_is_pure(var, context)) {
|
||||
mark_impure_fn(context);
|
||||
}
|
||||
if (var->is_const && var->val_node) {
|
||||
ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val;
|
||||
if (other_const_val->ok) {
|
||||
|
@ -2654,7 +2693,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl
|
|||
}
|
||||
|
||||
static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node,
|
||||
bool pointer_only)
|
||||
bool pointer_only, BlockContext *block_context)
|
||||
{
|
||||
resolve_top_level_decl(g, decl_node, pointer_only);
|
||||
TopLevelDecl *tld = get_as_top_level_decl(decl_node);
|
||||
|
@ -2664,7 +2703,7 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod
|
|||
|
||||
if (decl_node->type == NodeTypeVariableDeclaration) {
|
||||
VariableTableEntry *var = decl_node->data.variable_declaration.variable;
|
||||
return analyze_var_ref(g, source_node, var);
|
||||
return analyze_var_ref(g, source_node, var, block_context);
|
||||
} else if (decl_node->type == NodeTypeFnProto) {
|
||||
if (decl_node->data.fn_proto.generic_params.length > 0) {
|
||||
TypeTableEntry *type_entry = decl_node->data.fn_proto.generic_fn_type;
|
||||
|
@ -2700,12 +2739,13 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
|
|||
|
||||
VariableTableEntry *var = find_variable(g, context, variable_name);
|
||||
if (var) {
|
||||
return analyze_var_ref(g, node, var);
|
||||
TypeTableEntry *var_type = analyze_var_ref(g, node, var, context);
|
||||
return var_type;
|
||||
}
|
||||
|
||||
AstNode *decl_node = find_decl(context, variable_name);
|
||||
if (decl_node) {
|
||||
return analyze_decl_ref(g, node, decl_node, pointer_only);
|
||||
return analyze_decl_ref(g, node, decl_node, pointer_only, context);
|
||||
}
|
||||
|
||||
if (import->any_imports_failed) {
|
||||
|
@ -2840,71 +2880,6 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc
|
|||
return expected_rhs_type;
|
||||
}
|
||||
|
||||
static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) {
|
||||
if (bin_op == BinOpTypeBoolOr) {
|
||||
return a || b;
|
||||
} else if (bin_op == BinOpTypeBoolAnd) {
|
||||
return a && b;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry) {
|
||||
switch (type_entry->id) {
|
||||
case TypeTableEntryIdEnum:
|
||||
{
|
||||
ConstEnumValue *enum1 = &a->data.x_enum;
|
||||
ConstEnumValue *enum2 = &b->data.x_enum;
|
||||
if (enum1->tag == enum2->tag) {
|
||||
TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum1->tag];
|
||||
if (type_has_bits(enum_field->type_entry)) {
|
||||
zig_panic("TODO const expr analyze enum special value for equality");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case TypeTableEntryIdMetaType:
|
||||
return a->data.x_type == b->data.x_type;
|
||||
case TypeTableEntryIdVoid:
|
||||
return true;
|
||||
case TypeTableEntryIdPureError:
|
||||
return a->data.x_err.err == b->data.x_err.err;
|
||||
case TypeTableEntryIdFn:
|
||||
return a->data.x_fn == b->data.x_fn;
|
||||
case TypeTableEntryIdBool:
|
||||
return a->data.x_bool == b->data.x_bool;
|
||||
case TypeTableEntryIdInt:
|
||||
case TypeTableEntryIdFloat:
|
||||
case TypeTableEntryIdNumLitFloat:
|
||||
case TypeTableEntryIdNumLitInt:
|
||||
return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum);
|
||||
case TypeTableEntryIdPointer:
|
||||
zig_panic("TODO");
|
||||
case TypeTableEntryIdArray:
|
||||
zig_panic("TODO");
|
||||
case TypeTableEntryIdStruct:
|
||||
zig_panic("TODO");
|
||||
case TypeTableEntryIdUndefLit:
|
||||
zig_panic("TODO");
|
||||
case TypeTableEntryIdMaybe:
|
||||
zig_panic("TODO");
|
||||
case TypeTableEntryIdErrorUnion:
|
||||
zig_panic("TODO");
|
||||
case TypeTableEntryIdTypeDecl:
|
||||
zig_panic("TODO");
|
||||
case TypeTableEntryIdNamespace:
|
||||
zig_panic("TODO");
|
||||
case TypeTableEntryIdGenericFn:
|
||||
case TypeTableEntryIdInvalid:
|
||||
case TypeTableEntryIdUnreachable:
|
||||
zig_unreachable();
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
AstNode *node)
|
||||
{
|
||||
|
@ -2944,39 +2919,11 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
|
|||
return g->builtin_types.entry_bool;
|
||||
}
|
||||
|
||||
bool answer;
|
||||
if (type_can_gt_lt_cmp) {
|
||||
bool (*bignum_cmp)(BigNum *, BigNum *);
|
||||
if (bin_op_type == BinOpTypeCmpEq) {
|
||||
bignum_cmp = bignum_cmp_eq;
|
||||
} else if (bin_op_type == BinOpTypeCmpNotEq) {
|
||||
bignum_cmp = bignum_cmp_neq;
|
||||
} else if (bin_op_type == BinOpTypeCmpLessThan) {
|
||||
bignum_cmp = bignum_cmp_lt;
|
||||
} else if (bin_op_type == BinOpTypeCmpGreaterThan) {
|
||||
bignum_cmp = bignum_cmp_gt;
|
||||
} else if (bin_op_type == BinOpTypeCmpLessOrEq) {
|
||||
bignum_cmp = bignum_cmp_lte;
|
||||
} else if (bin_op_type == BinOpTypeCmpGreaterOrEq) {
|
||||
bignum_cmp = bignum_cmp_gte;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum);
|
||||
} else {
|
||||
bool are_equal = const_values_equal(op1_val, op2_val, resolved_type);
|
||||
if (bin_op_type == BinOpTypeCmpEq) {
|
||||
answer = are_equal;
|
||||
} else if (bin_op_type == BinOpTypeCmpNotEq) {
|
||||
answer = !are_equal;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
|
||||
eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val);
|
||||
return g->builtin_types.entry_bool;
|
||||
|
||||
bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
|
||||
return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var);
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
|
@ -3002,9 +2949,9 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i
|
|||
return g->builtin_types.entry_bool;
|
||||
}
|
||||
|
||||
bool answer = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op_type, op2_val->data.x_bool);
|
||||
bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
|
||||
return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var);
|
||||
ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
|
||||
eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val);
|
||||
return g->builtin_types.entry_bool;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
|
@ -3041,6 +2988,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
|
|||
}
|
||||
|
||||
analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
|
||||
// not const ok because expression has side effects
|
||||
return g->builtin_types.entry_void;
|
||||
}
|
||||
case BinOpTypeBoolOr:
|
||||
|
@ -3106,37 +3054,23 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
|
|||
return resolved_type;
|
||||
}
|
||||
|
||||
if (bin_op_type == BinOpTypeAdd) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_add, *op1, *op2, resolved_type);
|
||||
} else if (bin_op_type == BinOpTypeSub) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_sub, *op1, *op2, resolved_type);
|
||||
} else if (bin_op_type == BinOpTypeMult) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_mul, *op1, *op2, resolved_type);
|
||||
} else if (bin_op_type == BinOpTypeDiv) {
|
||||
ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
|
||||
if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||
|
||||
(is_float && op2_val->data.x_bignum.data.x_float == 0.0))
|
||||
ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
|
||||
int err;
|
||||
if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type,
|
||||
op2_val, resolved_type, out_val)))
|
||||
{
|
||||
if (err == ErrorDivByZero) {
|
||||
add_node_error(g, node, buf_sprintf("division by zero is undefined"));
|
||||
return g->builtin_types.entry_invalid;
|
||||
} else {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_div, *op1, *op2, resolved_type);
|
||||
} else if (err == ErrorOverflow) {
|
||||
add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type"));
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
} else if (bin_op_type == BinOpTypeMod) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_mod, *op1, *op2, resolved_type);
|
||||
} else if (bin_op_type == BinOpTypeBinOr) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_or, *op1, *op2, resolved_type);
|
||||
} else if (bin_op_type == BinOpTypeBinAnd) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_and, *op1, *op2, resolved_type);
|
||||
} else if (bin_op_type == BinOpTypeBinXor) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_xor, *op1, *op2, resolved_type);
|
||||
} else if (bin_op_type == BinOpTypeBitShiftLeft) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_shl, *op1, *op2, resolved_type);
|
||||
} else if (bin_op_type == BinOpTypeBitShiftRight) {
|
||||
return resolve_expr_const_val_as_bignum_op(g, node, bignum_shr, *op1, *op2, resolved_type);
|
||||
} else {
|
||||
zig_unreachable();
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
num_lit_fits_in_other_type(g, node, resolved_type);
|
||||
return resolved_type;
|
||||
}
|
||||
case BinOpTypeUnwrapMaybe:
|
||||
{
|
||||
|
@ -3720,6 +3654,10 @@ static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import
|
|||
}
|
||||
|
||||
ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val;
|
||||
if (cond_val->undef) {
|
||||
add_node_error(g, first_executing_node(*cond), buf_sprintf("branch on undefined value"));
|
||||
return cond_type;
|
||||
}
|
||||
if (cond_val->ok && !cond_val->depends_on_compile_var) {
|
||||
const char *str_val = cond_val->data.x_bool ? "true" : "false";
|
||||
add_node_error(g, first_executing_node(*cond),
|
||||
|
@ -3762,17 +3700,6 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import,
|
|||
node, then_node, else_node, cond_is_const, cond_bool_val);
|
||||
}
|
||||
|
||||
static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) {
|
||||
assert(int_type->id == TypeTableEntryIdInt);
|
||||
|
||||
for (int i = 0; i < CIntTypeCount; i += 1) {
|
||||
if (int_type == g->builtin_types.entry_c_int[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
AstNode *node, const char *err_format, bool is_max)
|
||||
{
|
||||
|
@ -3781,67 +3708,15 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
|
|||
|
||||
AstNode *type_node = node->data.fn_call_expr.params.at(0);
|
||||
TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
|
||||
|
||||
if (type_entry->id == TypeTableEntryIdInvalid) {
|
||||
return g->builtin_types.entry_invalid;
|
||||
} else if (type_entry->id == TypeTableEntryIdInt) {
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
const_val->ok = true;
|
||||
const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry);
|
||||
if (is_max) {
|
||||
if (type_entry->data.integral.is_signed) {
|
||||
int64_t val;
|
||||
if (type_entry->data.integral.bit_count == 64) {
|
||||
val = INT64_MAX;
|
||||
} else if (type_entry->data.integral.bit_count == 32) {
|
||||
val = INT32_MAX;
|
||||
} else if (type_entry->data.integral.bit_count == 16) {
|
||||
val = INT16_MAX;
|
||||
} else if (type_entry->data.integral.bit_count == 8) {
|
||||
val = INT8_MAX;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
bignum_init_signed(&const_val->data.x_bignum, val);
|
||||
} else {
|
||||
uint64_t val;
|
||||
if (type_entry->data.integral.bit_count == 64) {
|
||||
val = UINT64_MAX;
|
||||
} else if (type_entry->data.integral.bit_count == 32) {
|
||||
val = UINT32_MAX;
|
||||
} else if (type_entry->data.integral.bit_count == 16) {
|
||||
val = UINT16_MAX;
|
||||
} else if (type_entry->data.integral.bit_count == 8) {
|
||||
val = UINT8_MAX;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
bignum_init_unsigned(&const_val->data.x_bignum, val);
|
||||
}
|
||||
} else {
|
||||
if (type_entry->data.integral.is_signed) {
|
||||
int64_t val;
|
||||
if (type_entry->data.integral.bit_count == 64) {
|
||||
val = INT64_MIN;
|
||||
} else if (type_entry->data.integral.bit_count == 32) {
|
||||
val = INT32_MIN;
|
||||
} else if (type_entry->data.integral.bit_count == 16) {
|
||||
val = INT16_MIN;
|
||||
} else if (type_entry->data.integral.bit_count == 8) {
|
||||
val = INT8_MIN;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
bignum_init_signed(&const_val->data.x_bignum, val);
|
||||
} else {
|
||||
bignum_init_unsigned(&const_val->data.x_bignum, 0);
|
||||
}
|
||||
}
|
||||
} else if (type_entry->id == TypeTableEntryIdInt ||
|
||||
type_entry->id == TypeTableEntryIdFloat ||
|
||||
type_entry->id == TypeTableEntryIdBool)
|
||||
{
|
||||
eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
|
||||
return type_entry;
|
||||
} else if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
zig_panic("TODO analyze_min_max_value float");
|
||||
return type_entry;
|
||||
} else if (type_entry->id == TypeTableEntryIdBool) {
|
||||
return resolve_expr_const_val_as_bool(g, node, is_max, false);
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf(err_format, buf_ptr(&type_entry->name)));
|
||||
|
@ -3849,92 +3724,19 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
|
|||
}
|
||||
}
|
||||
|
||||
static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *expr_node) {
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
if (!other_val->ok) {
|
||||
return;
|
||||
}
|
||||
const_val->depends_on_compile_var = other_val->depends_on_compile_var;
|
||||
const_val->undef = other_val->undef;
|
||||
|
||||
assert(other_val != const_val);
|
||||
switch (node->data.fn_call_expr.cast_op) {
|
||||
case CastOpNoCast:
|
||||
zig_unreachable();
|
||||
case CastOpNoop:
|
||||
case CastOpWidenOrShorten:
|
||||
case CastOpPointerReinterpret:
|
||||
*const_val = *other_val;
|
||||
break;
|
||||
case CastOpPtrToInt:
|
||||
case CastOpIntToPtr:
|
||||
// can't do it
|
||||
break;
|
||||
case CastOpToUnknownSizeArray:
|
||||
{
|
||||
TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry;
|
||||
assert(other_type->id == TypeTableEntryIdArray);
|
||||
|
||||
ConstExprValue *all_fields = allocate<ConstExprValue>(2);
|
||||
ConstExprValue *ptr_field = &all_fields[0];
|
||||
ConstExprValue *len_field = &all_fields[1];
|
||||
|
||||
const_val->data.x_struct.fields = allocate<ConstExprValue*>(2);
|
||||
const_val->data.x_struct.fields[0] = ptr_field;
|
||||
const_val->data.x_struct.fields[1] = len_field;
|
||||
|
||||
ptr_field->ok = true;
|
||||
ptr_field->data.x_ptr.ptr = other_val->data.x_array.fields;
|
||||
ptr_field->data.x_ptr.len = other_type->data.array.len;
|
||||
|
||||
len_field->ok = true;
|
||||
bignum_init_unsigned(&len_field->data.x_bignum, other_type->data.array.len);
|
||||
|
||||
const_val->ok = true;
|
||||
break;
|
||||
}
|
||||
case CastOpMaybeWrap:
|
||||
const_val->data.x_maybe = other_val;
|
||||
const_val->ok = true;
|
||||
break;
|
||||
case CastOpErrorWrap:
|
||||
const_val->data.x_err.err = nullptr;
|
||||
const_val->data.x_err.payload = other_val;
|
||||
const_val->ok = true;
|
||||
break;
|
||||
case CastOpPureErrorWrap:
|
||||
const_val->data.x_err.err = other_val->data.x_err.err;
|
||||
const_val->ok = true;
|
||||
break;
|
||||
case CastOpErrToInt:
|
||||
{
|
||||
uint64_t value = other_val->data.x_err.err ? other_val->data.x_err.err->value : 0;
|
||||
bignum_init_unsigned(&const_val->data.x_bignum, value);
|
||||
const_val->ok = true;
|
||||
break;
|
||||
}
|
||||
case CastOpIntToFloat:
|
||||
bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum);
|
||||
const_val->ok = true;
|
||||
break;
|
||||
case CastOpFloatToInt:
|
||||
bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum);
|
||||
const_val->ok = true;
|
||||
break;
|
||||
case CastOpBoolToInt:
|
||||
bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0);
|
||||
const_val->ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode *node,
|
||||
AstNode *expr_node, TypeTableEntry *wanted_type, CastOp op, bool need_alloca)
|
||||
{
|
||||
node->data.fn_call_expr.cast_op = op;
|
||||
eval_const_expr_implicit_cast(g, node, expr_node);
|
||||
|
||||
ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
|
||||
TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry;
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
if (other_val->ok) {
|
||||
eval_const_expr_implicit_cast(node->data.fn_call_expr.cast_op, other_val, other_type,
|
||||
const_val, wanted_type);
|
||||
}
|
||||
|
||||
if (need_alloca) {
|
||||
if (context->fn_entry) {
|
||||
context->fn_entry->cast_alloca_list.append(node);
|
||||
|
@ -4630,13 +4432,15 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
|
|||
case BuiltinFnIdErrName:
|
||||
return analyze_err_name(g, import, context, node);
|
||||
case BuiltinFnIdBreakpoint:
|
||||
mark_impure_fn(context);
|
||||
return g->builtin_types.entry_void;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, TypeTableEntry *struct_type)
|
||||
TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type,
|
||||
AstNode *struct_node)
|
||||
{
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
|
@ -4648,31 +4452,49 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
|
|||
int src_param_count = fn_type->data.fn.fn_type_id.param_count;
|
||||
int actual_param_count = node->data.fn_call_expr.params.length;
|
||||
|
||||
if (struct_type) {
|
||||
if (struct_node) {
|
||||
actual_param_count += 1;
|
||||
}
|
||||
|
||||
bool ok_invocation = true;
|
||||
|
||||
if (fn_type->data.fn.fn_type_id.is_var_args) {
|
||||
if (actual_param_count < src_param_count) {
|
||||
ok_invocation = false;
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("expected at least %d arguments, got %d", src_param_count, actual_param_count));
|
||||
}
|
||||
} else if (src_param_count != actual_param_count) {
|
||||
ok_invocation = false;
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("expected %d arguments, got %d", src_param_count, actual_param_count));
|
||||
}
|
||||
|
||||
bool all_args_const_expr = true;
|
||||
|
||||
if (struct_node) {
|
||||
ConstExprValue *struct_const_val = &get_resolved_expr(struct_node)->const_val;
|
||||
if (!struct_const_val->ok) {
|
||||
all_args_const_expr = false;
|
||||
}
|
||||
}
|
||||
|
||||
// analyze each parameter. in the case of a method, we already analyzed the
|
||||
// first parameter in order to figure out which struct we were calling a method on.
|
||||
for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
|
||||
AstNode *child = node->data.fn_call_expr.params.at(i);
|
||||
AstNode **child = &node->data.fn_call_expr.params.at(i);
|
||||
// determine the expected type for each parameter
|
||||
TypeTableEntry *expected_param_type = nullptr;
|
||||
int fn_proto_i = i + (struct_type ? 1 : 0);
|
||||
int fn_proto_i = i + (struct_node ? 1 : 0);
|
||||
if (fn_proto_i < src_param_count) {
|
||||
expected_param_type = fn_type->data.fn.fn_type_id.param_info[fn_proto_i].type;
|
||||
}
|
||||
analyze_expression(g, import, context, expected_param_type, child);
|
||||
analyze_expression(g, import, context, expected_param_type, *child);
|
||||
|
||||
ConstExprValue *const_arg_val = &get_resolved_expr(*child)->const_val;
|
||||
if (!const_arg_val->ok) {
|
||||
all_args_const_expr = false;
|
||||
}
|
||||
}
|
||||
|
||||
TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
|
||||
|
@ -4681,6 +4503,27 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
|
|||
return return_type;
|
||||
}
|
||||
|
||||
FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
|
||||
if (ok_invocation && fn_table_entry && fn_table_entry->is_pure) {
|
||||
if (fn_table_entry->anal_state == FnAnalStateReady) {
|
||||
analyze_fn_body(g, fn_table_entry);
|
||||
}
|
||||
if (all_args_const_expr) {
|
||||
if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) {
|
||||
ConstExprValue *result_val = &get_resolved_expr(node)->const_val;
|
||||
if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) {
|
||||
// function evaluation generated an error
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
return return_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure) {
|
||||
// calling an impure fn is impure
|
||||
mark_impure_fn(context);
|
||||
}
|
||||
|
||||
if (handle_is_ptr(return_type)) {
|
||||
context->fn_entry->cast_alloca_list.append(node);
|
||||
}
|
||||
|
@ -4689,13 +4532,13 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
|
|||
}
|
||||
|
||||
static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, TypeTableEntry *struct_type)
|
||||
TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, AstNode *struct_node)
|
||||
{
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
node->data.fn_call_expr.fn_entry = fn_table_entry;
|
||||
|
||||
return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_type);
|
||||
return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_node);
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
|
||||
|
@ -4721,7 +4564,7 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp
|
|||
generic_fn_type_id->generic_param_count = actual_param_count;
|
||||
generic_fn_type_id->generic_params = allocate<GenericParamValue>(actual_param_count);
|
||||
|
||||
BlockContext *child_context = import->block_context;
|
||||
BlockContext *child_context = decl_node->owner->block_context;
|
||||
for (int i = 0; i < actual_param_count; i += 1) {
|
||||
AstNode *generic_param_decl_node = decl_node->data.fn_proto.generic_params.at(i);
|
||||
assert(generic_param_decl_node->type == NodeTypeParamDecl);
|
||||
|
@ -4861,17 +4704,17 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
|
|||
return analyze_cast_expr(g, import, context, node);
|
||||
}
|
||||
} else if (invoke_type_entry->id == TypeTableEntryIdFn) {
|
||||
TypeTableEntry *bare_struct_type;
|
||||
AstNode *struct_node;
|
||||
if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
|
||||
fn_ref_expr->data.field_access_expr.is_member_fn)
|
||||
{
|
||||
bare_struct_type = fn_ref_expr->data.field_access_expr.bare_struct_type;
|
||||
struct_node = fn_ref_expr->data.field_access_expr.struct_expr;
|
||||
} else {
|
||||
bare_struct_type = nullptr;
|
||||
struct_node = nullptr;
|
||||
}
|
||||
|
||||
return analyze_fn_call_raw(g, import, context, expected_type, node,
|
||||
const_val->data.x_fn, bare_struct_type);
|
||||
const_val->data.x_fn, struct_node);
|
||||
} else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) {
|
||||
return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type);
|
||||
} else {
|
||||
|
@ -5421,6 +5264,8 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import,
|
|||
static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node)
|
||||
{
|
||||
mark_impure_fn(context);
|
||||
|
||||
node->data.asm_expr.return_count = 0;
|
||||
TypeTableEntry *return_type = g->builtin_types.entry_void;
|
||||
for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
|
||||
|
@ -5641,8 +5486,10 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
|
|||
if (fn_proto_node->data.fn_proto.skip) {
|
||||
// we detected an error with this function definition which prevents us
|
||||
// from further analyzing it.
|
||||
fn_table_entry->anal_state = FnAnalStateSkipped;
|
||||
return;
|
||||
}
|
||||
fn_table_entry->anal_state = FnAnalStateProbing;
|
||||
|
||||
BlockContext *context = node->data.fn_def.block_context;
|
||||
|
||||
|
@ -5676,6 +5523,10 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
|
|||
param_decl_node->data.param_decl.variable = var;
|
||||
|
||||
var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
|
||||
|
||||
if (!type->deep_const) {
|
||||
fn_table_entry->is_pure = false;
|
||||
}
|
||||
}
|
||||
|
||||
TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.return_type;
|
||||
|
@ -5697,6 +5548,8 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
|
|||
buf_ptr(&label->decl_node->data.label.name)));
|
||||
}
|
||||
}
|
||||
|
||||
fn_table_entry->anal_state = FnAnalStateComplete;
|
||||
}
|
||||
|
||||
static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
|
||||
|
@ -6005,8 +5858,10 @@ void semantic_analyze(CodeGen *g) {
|
|||
|
||||
for (int i = 0; i < g->fn_defs.length; i += 1) {
|
||||
FnTableEntry *fn_entry = g->fn_defs.at(i);
|
||||
if (fn_entry->anal_state == FnAnalStateReady) {
|
||||
analyze_fn_body(g, fn_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr *get_resolved_expr(AstNode *node) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")) {
|
||||
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 {
|
||||
zig_panic("gen_field_access_expr bad array field");
|
||||
}
|
||||
} 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();
|
||||
|
|
|
@ -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)";
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ enum Error {
|
|||
ErrorFileNotFound,
|
||||
ErrorFileSystem,
|
||||
ErrorFileTooBig,
|
||||
ErrorDivByZero,
|
||||
ErrorOverflow
|
||||
};
|
||||
|
||||
const char *err_str(int err);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
||||
|
|
|
@ -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]))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue