diff --git a/example/rand/main.zig b/example/rand/main.zig index 35a1335249..1b6d18122b 100644 --- a/example/rand/main.zig +++ b/example/rand/main.zig @@ -42,8 +42,8 @@ struct Rand { /// inclusive and `end` exclusive. pub fn range_u64(r: &Rand, start: u64, end: u64) -> u64 { const range = end - start; - const leftover = #max_int(u64) % range; - const upper_bound = #max_int(u64) - leftover; + const leftover = #max_value(u64) % range; + const upper_bound = #max_value(u64) - leftover; var rand_val_array : [u8; #sizeof(u64)]; while (true) { @@ -90,13 +90,14 @@ pub fn rand_init(r: &Rand, seed: u32) { var i : #typeof(ARRAY_SIZE) = 1; while (i < ARRAY_SIZE) { const prev_value : u64 = r.array[i - 1]; - r.array[i] = ((previous_value ^ (previous_value << 30)) * 0x6c078965 + i) as u32; + r.array[i] = ((prev_value ^ (prev_value << 30)) * 0x6c078965 + i) as u32; i += 1; } } pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { - var rand = rand_init(13); + var rand : Rand; + rand_init(&rand, 13); const answer = rand.range_u64(0, 100) + 1; print_str("random number: "); print_u64(answer); diff --git a/src/analyze.cpp b/src/analyze.cpp index 27772f6ef1..2bffcee8c8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1482,7 +1482,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa } if (implicit_type == nullptr && variable_declaration->is_const) { - add_node_error(g, source_node, buf_sprintf("variables must have initial values or be declared 'mut'.")); + add_node_error(g, source_node, buf_sprintf("const variable missing initialization")); implicit_type = g->builtin_types.entry_invalid; } @@ -1684,23 +1684,44 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, node->data.if_var_expr.then_block, node->data.if_var_expr.else_node, node); } +static TypeTableEntry *analyze_min_max_value(CodeGen *g, AstNode *node, TypeTableEntry *type_entry, + const char *err_format) +{ + if (type_entry->id == TypeTableEntryIdInt || + type_entry->id == TypeTableEntryIdFloat || + type_entry->id == TypeTableEntryIdBool) + { + return type_entry; + } else { + add_node_error(g, node, + buf_sprintf(err_format, buf_ptr(&type_entry->name))); + return g->builtin_types.entry_invalid; + } +} + static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { assert(node->type == NodeTypeCompilerFnType); Buf *name = &node->data.compiler_fn_type.name; + TypeTableEntry *type_entry = resolve_type(g, node->data.compiler_fn_type.type, import, context); + if (buf_eql_str(name, "sizeof")) { - TypeTableEntry *type_entry = resolve_type(g, node->data.compiler_fn_type.type, import, context); uint64_t size_in_bytes = type_entry->size_in_bits / 8; TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, size_in_bytes); NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; assert(!codegen_num_lit->resolved_type); - codegen_num_lit->resolved_type = resolve_type_compatibility(g, context, node, expected_type, num_lit_type); + codegen_num_lit->resolved_type = resolve_type_compatibility(g, context, node, + expected_type, num_lit_type); return num_lit_type; + } else if (buf_eql_str(name, "min_value")) { + return analyze_min_max_value(g, node, type_entry, "no min value available for type '%s'"); + } else if (buf_eql_str(name, "max_value")) { + return analyze_min_max_value(g, node, type_entry, "no max value available for type '%s'"); } else { add_node_error(g, node, buf_sprintf("invalid compiler function: '%s'", buf_ptr(name))); @@ -1966,16 +1987,43 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, break; case PrefixOpBinNot: { - // TODO: don't require i32 - analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.prefix_op_expr.primary_expr); - return_type = g->builtin_types.entry_i32; + AstNode *operand_node = node->data.prefix_op_expr.primary_expr; + TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, + operand_node); + if (expr_type->id == TypeTableEntryIdInvalid) { + return_type = expr_type; + } else if (expr_type->id == TypeTableEntryIdInt || + (expr_type->id == TypeTableEntryIdNumberLiteral && + !is_num_lit_float(expr_type->data.num_lit.kind))) + { + return_type = expr_type; + } else { + add_node_error(g, operand_node, buf_sprintf("invalid binary not type: '%s'", + buf_ptr(&expr_type->name))); + return_type = g->builtin_types.entry_invalid; + } break; } case PrefixOpNegation: { - // TODO: don't require i32 - analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.prefix_op_expr.primary_expr); - return_type = g->builtin_types.entry_i32; + AstNode *operand_node = node->data.prefix_op_expr.primary_expr; + TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, + operand_node); + if (expr_type->id == TypeTableEntryIdInvalid) { + return_type = expr_type; + } else if (expr_type->id == TypeTableEntryIdInt && + expr_type->data.integral.is_signed) + { + return_type = expr_type; + } else if (expr_type->id == TypeTableEntryIdFloat) { + return_type = expr_type; + } else if (expr_type->id == TypeTableEntryIdNumberLiteral) { + return_type = expr_type; + } else { + add_node_error(g, operand_node, buf_sprintf("invalid negation type: '%s'", + buf_ptr(&expr_type->name))); + return_type = g->builtin_types.entry_invalid; + } break; } case PrefixOpAddressOf: diff --git a/src/codegen.cpp b/src/codegen.cpp index bc9462ae99..9c2cf3c88f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -859,28 +859,38 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV if (else_node) { LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else"); - LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); + + LLVMBasicBlockRef endif_block; + bool then_endif_reachable = get_expr_type(then_node)->id != TypeTableEntryIdUnreachable; + bool else_endif_reachable = get_expr_type(else_node)->id != TypeTableEntryIdUnreachable; + if (then_endif_reachable || else_endif_reachable) { + endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); + } LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); LLVMPositionBuilderAtEnd(g->builder, then_block); LLVMValueRef then_expr_result = gen_expr(g, then_node); - if (get_expr_type(then_node)->id != TypeTableEntryIdUnreachable) + if (then_endif_reachable) { LLVMBuildBr(g->builder, endif_block); + } LLVMPositionBuilderAtEnd(g->builder, else_block); LLVMValueRef else_expr_result = gen_expr(g, else_node); - if (get_expr_type(else_node)->id != TypeTableEntryIdUnreachable) + if (else_endif_reachable) { LLVMBuildBr(g->builder, endif_block); + } - LLVMPositionBuilderAtEnd(g->builder, endif_block); - if (use_expr_value) { - LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), ""); - LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result}; - LLVMBasicBlockRef incoming_blocks[2] = {then_block, else_block}; - LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); + if (then_endif_reachable || else_endif_reachable) { + LLVMPositionBuilderAtEnd(g->builder, endif_block); + if (use_expr_value) { + LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), ""); + LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result}; + LLVMBasicBlockRef incoming_blocks[2] = {then_block, else_block}; + LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); - return phi; + return phi; + } } return nullptr; @@ -1245,14 +1255,38 @@ static LLVMValueRef gen_compiler_fn_type(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeCompilerFnType); Buf *name = &node->data.compiler_fn_type.name; + TypeTableEntry *type_entry = get_type_for_type_node(g, node->data.compiler_fn_type.type); if (buf_eql_str(name, "sizeof")) { - TypeTableEntry *type_entry = get_type_for_type_node(g, node->data.compiler_fn_type.type); NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; AstNodeNumberLiteral num_lit_node; num_lit_node.kind = type_entry->data.num_lit.kind; num_lit_node.overflow = false; num_lit_node.data.x_uint = type_entry->size_in_bits / 8; return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node); + } else if (buf_eql_str(name, "min_value")) { + if (type_entry->id == TypeTableEntryIdInt) { + if (type_entry->data.integral.is_signed) { + return LLVMConstInt(type_entry->type_ref, 1ULL << (type_entry->size_in_bits - 1), false); + } else { + return LLVMConstNull(type_entry->type_ref); + } + } else if (type_entry->id == TypeTableEntryIdFloat) { + zig_panic("TODO codegen min_value float"); + } else { + zig_unreachable(); + } + } else if (buf_eql_str(name, "max_value")) { + if (type_entry->id == TypeTableEntryIdInt) { + if (type_entry->data.integral.is_signed) { + return LLVMConstInt(type_entry->type_ref, (1ULL << (type_entry->size_in_bits - 1)) - 1, false); + } else { + return LLVMConstAllOnes(type_entry->type_ref); + } + } else if (type_entry->id == TypeTableEntryIdFloat) { + zig_panic("TODO codegen max_value float"); + } else { + zig_unreachable(); + } } else { zig_unreachable(); } diff --git a/std/std.zig b/std/std.zig index ba9a3dd0ff..58bdca57d1 100644 --- a/std/std.zig +++ b/std/std.zig @@ -58,15 +58,32 @@ pub fn print_u64(x: u64) -> isize { return write(stdout_fileno, buf.ptr, len); } +// TODO handle buffering and flushing (mutex protected) +// TODO error handling +pub fn print_i64(x: i64) -> isize { + // TODO use max_u64_base10_digits instead of hardcoding 20 + var buf: [u8; 20]; + const len = buf_print_i64(buf.ptr, x); + return write(stdout_fileno, buf.ptr, len); +} + fn digit_to_char(digit: u64) -> u8 { '0' + (digit as u8) } const max_u64_base10_digits: usize = 20; +fn buf_print_i64(out_buf: &u8, x: i64) -> usize { + if (x < 0) { + out_buf[0] = '-'; + return 1 + buf_print_u64(&out_buf[1], ((-(x + 1)) as u64) + 1); + } else { + return buf_print_u64(out_buf, x as u64); + } +} + fn buf_print_u64(out_buf: &u8, x: u64) -> usize { - // TODO use max_u64_base10_digits instead of hardcoding 20 - var buf: [u8; 20]; + var buf: [u8; max_u64_base10_digits]; var a = x; var index = max_u64_base10_digits; diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 18e4102b70..6df16e9a59 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -399,7 +399,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { if (5 * 4 / 2 % 3 != 1) { print_str("BAD 9\n"); } if (5 as i32 as i32 != 5) { print_str("BAD 10\n"); } if (!!false) { print_str("BAD 11\n"); } - if (7 != --7) { print_str("BAD 12\n"); } + if (7 as i32 != --(7 as i32)) { print_str("BAD 12\n"); } print_str("OK\n"); return 0; @@ -790,6 +790,93 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { return 0; } )SOURCE", "20\n"); + + add_simple_case("#min_value() and #max_value()", R"SOURCE( +use "std.zig"; +pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { + print_str("max u8: "); + print_u64(#max_value(u8)); + print_str("\n"); + + print_str("max u16: "); + print_u64(#max_value(u16)); + print_str("\n"); + + print_str("max u32: "); + print_u64(#max_value(u32)); + print_str("\n"); + + print_str("max u64: "); + print_u64(#max_value(u64)); + print_str("\n"); + + print_str("max i8: "); + print_i64(#max_value(i8)); + print_str("\n"); + + print_str("max i16: "); + print_i64(#max_value(i16)); + print_str("\n"); + + print_str("max i32: "); + print_i64(#max_value(i32)); + print_str("\n"); + + print_str("max i64: "); + print_i64(#max_value(i64)); + print_str("\n"); + + print_str("min u8: "); + print_u64(#min_value(u8)); + print_str("\n"); + + print_str("min u16: "); + print_u64(#min_value(u16)); + print_str("\n"); + + print_str("min u32: "); + print_u64(#min_value(u32)); + print_str("\n"); + + print_str("min u64: "); + print_u64(#min_value(u64)); + print_str("\n"); + + print_str("min i8: "); + print_i64(#min_value(i8)); + print_str("\n"); + + print_str("min i16: "); + print_i64(#min_value(i16)); + print_str("\n"); + + print_str("min i32: "); + print_i64(#min_value(i32)); + print_str("\n"); + + print_str("min i64: "); + print_i64(#min_value(i64)); + print_str("\n"); + + return 0; +} + )SOURCE", + "max u8: 255\n" + "max u16: 65535\n" + "max u32: 4294967295\n" + "max u64: 18446744073709551615\n" + "max i8: 127\n" + "max i16: 32767\n" + "max i32: 2147483647\n" + "max i64: 9223372036854775807\n" + "min u8: 0\n" + "min u16: 0\n" + "min u32: 0\n" + "min u64: 0\n" + "min i8: -128\n" + "min i16: -32768\n" + "min i32: -2147483648\n" + "min i64: -9223372036854775808\n"); } ////////////////////////////////////////////////////////////////////////////////////