From be4df96e4b80a3307b3661fd5ca3114478499daf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Apr 2016 13:30:52 -0700 Subject: [PATCH] passing all tests --- src/analyze.cpp | 71 ++++++++++++++++--------- src/eval.cpp | 105 +++++++++++++++++++++++++++++++----- std/rand.zig | 1 + test/run_tests.cpp | 124 ++----------------------------------------- test/self_hosted.zig | 103 ++++++++++++++++++++++++++++++++--- 5 files changed, 240 insertions(+), 164 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 7420dbe682..f7ef77c195 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -35,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, @@ -957,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))); @@ -2227,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) { @@ -2381,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'", @@ -2408,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, @@ -2674,8 +2688,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) { @@ -2686,7 +2713,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); @@ -2696,7 +2723,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; @@ -2716,14 +2743,6 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod } } -static bool var_is_pure(VariableTableEntry *var, TypeTableEntry *var_type, 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_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only) { @@ -2740,16 +2759,13 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, VariableTableEntry *var = find_variable(g, context, variable_name); if (var) { - TypeTableEntry *var_type = analyze_var_ref(g, node, var); - if (!var_is_pure(var, var_type, context)) { - mark_impure_fn(context); - } + 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) { @@ -2992,7 +3008,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, } analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); - return resolve_expr_const_val_as_void(g, node); + // not const ok because expression has side effects + return g->builtin_types.entry_void; } case BinOpTypeBoolOr: case BinOpTypeBoolAnd: @@ -4473,12 +4490,16 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, 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)); } @@ -4517,7 +4538,7 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, } FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; - if (fn_table_entry && fn_table_entry->is_pure && all_args_const_expr) { + if (ok_invocation && fn_table_entry && fn_table_entry->is_pure && all_args_const_expr) { if (fn_table_entry->anal_state == FnAnalStateReady) { analyze_fn_body(g, fn_table_entry); } else if (fn_table_entry->anal_state == FnAnalStateProbing) { @@ -4726,7 +4747,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import if (fn_ref_expr->type == NodeTypeFieldAccessExpr && fn_ref_expr->data.field_access_expr.is_member_fn) { - struct_node = fn_ref_expr; + struct_node = fn_ref_expr->data.field_access_expr.struct_expr; } else { struct_node = nullptr; } diff --git a/src/eval.cpp b/src/eval.cpp index 41263bfcb7..1e386e0368 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -70,6 +70,7 @@ static bool eval_block(EvalFn *ef, AstNode *node, ConstExprValue *out) { 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; } @@ -109,7 +110,6 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, { assert(op1_val->ok); assert(op2_val->ok); - assert(op1_type == op2_type); switch (bin_op) { case BinOpTypeAssign: @@ -275,6 +275,7 @@ static bool eval_symbol_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) Buf *name = &node->data.symbol_expr.symbol; EvalVar *var = find_var(ef, name); + assert(var); *out_val = var->value; @@ -374,6 +375,8 @@ void eval_const_expr_implicit_cast(CastOp cast_op, *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; @@ -393,8 +396,47 @@ void eval_const_expr_implicit_cast(CastOp cast_op, } else { zig_panic("TODO"); } - break; + } 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(1); + ConstExprValue **ptr_val = allocate(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 @@ -684,6 +726,7 @@ static bool eval_field_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou 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"); } @@ -916,11 +959,45 @@ static bool eval_char_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou 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(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); @@ -952,23 +1029,16 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { return eval_number_literal_expr(ef, node, out); case NodeTypeCharLiteral: return eval_char_literal_expr(ef, node, out); - case NodeTypeRoot: - case NodeTypeFnProto: - case NodeTypeFnDef: - case NodeTypeFnDecl: - case NodeTypeParamDecl: - case NodeTypeDirective: + case NodeTypeWhileExpr: + return eval_while_expr(ef, node, out); case NodeTypeDefer: - case NodeTypeTypeDecl: case NodeTypeErrorValueDecl: case NodeTypeUnwrapErrorExpr: case NodeTypeStringLiteral: case NodeTypeSliceExpr: - case NodeTypeUse: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: case NodeTypeIfVarExpr: - case NodeTypeWhileExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeSwitchRange: @@ -976,13 +1046,22 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: - case NodeTypeAsmExpr: 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(); } } @@ -1054,6 +1133,8 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va return true; } + assert(out_val->ok); + return efr.abort; } 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..f9f5707bca 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"); diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 7d3f0b8e46..1becd42e75 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; @@ -509,6 +514,7 @@ enum Fruit { Orange, Banana, } +#static_eval_enable(false) fn non_const_switch_on_enum(fruit: Fruit) { switch (fruit) { Apple => unreachable{}, @@ -521,6 +527,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 +556,7 @@ enum SwitchProngWithVarEnum { Two: f32, Meh, } +#static_eval_enable(false) fn switch_prong_with_var_fn(a: SwitchProngWithVarEnum) { switch(a) { One => |x| { @@ -569,6 +577,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 +617,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 +745,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 +799,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 +831,83 @@ 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 + } +}