diff --git a/doc/langref.md b/doc/langref.md index 0aace75996..9b806fc384 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -637,6 +637,25 @@ const b: u8 = @truncate(u8, a); // b is now 0xcd ``` +This function always truncates the significant bits of the integer, regardless +of endianness on the target platform. + +This function also performs a twos complement cast. For example, the following +produces a crash in debug mode and undefined behavior in release mode: + +```zig +const a = i16(-1); +const b = u16(a); +``` + +However this is well defined and working code: + +```zig +const a = i16(-1); +const b = @truncate(u16, a); +// b is now 0xffff +``` + ### @compileError(comptime msg: []u8) This function, when semantically analyzed, causes a compile error with the diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 35c0d46760..0a799c3af4 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -6,10 +6,11 @@ const os = std.os; pub fn main(args: [][]u8) -> %void { %%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n"); - var seed: [@sizeOf(usize)]u8 = undefined; - %%os.getRandomBytes(seed); + var seed_bytes: [@sizeOf(usize)]u8 = undefined; + %%os.getRandomBytes(seed_bytes[0...]); + const seed = std.mem.readInt(seed_bytes, usize, true); var rand: Rand = undefined; - rand.init(([]usize)(seed)[0]); + rand.init(seed); const answer = rand.rangeUnsigned(u8, 0, 100) + 1; diff --git a/src/all_types.hpp b/src/all_types.hpp index dee4b3c4d6..5e2a275b22 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -119,11 +119,21 @@ enum ConstPtrSpecial { ConstPtrSpecialHardCodedAddr, }; -struct ConstPtrValue { - ConstPtrSpecial special; +enum ConstPtrMut { + // The pointer points to memory that is known at compile time and immutable. + ConstPtrMutComptimeConst, // This means that the pointer points to memory used by a comptime variable, // so attempting to write a non-compile-time known value is an error - bool comptime_var_mem; + // But the underlying value is allowed to change at compile time. + ConstPtrMutComptimeVar, + // The pointer points to memory that is known only at runtime. + // For example it may point to the initializer value of a variable. + ConstPtrMutRuntimeVar, +}; + +struct ConstPtrValue { + ConstPtrSpecial special; + ConstPtrMut mut; union { struct { diff --git a/src/analyze.cpp b/src/analyze.cpp index 9a65479e49..1fa3d063bb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2833,7 +2833,18 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { const_val->data.x_arg_tuple.end_index * 2290442768; case TypeTableEntryIdPointer: { - uint32_t hash_val = const_val->data.x_ptr.comptime_var_mem ? 2216297012 : 170810250; + uint32_t hash_val = 0; + switch (const_val->data.x_ptr.mut) { + case ConstPtrMutRuntimeVar: + hash_val += 3500721036; + break; + case ConstPtrMutComptimeConst: + hash_val += 4214318515; + break; + case ConstPtrMutComptimeVar: + hash_val += 1103195694; + break; + } switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); @@ -3339,7 +3350,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdPointer: if (a->data.x_ptr.special != b->data.x_ptr.special) return false; - if (a->data.x_ptr.comptime_var_mem != b->data.x_ptr.comptime_var_mem) + if (a->data.x_ptr.mut != b->data.x_ptr.mut) return false; switch (a->data.x_ptr.special) { case ConstPtrSpecialInvalid: diff --git a/src/codegen.cpp b/src/codegen.cpp index e56c3b7de2..b98d549e84 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1859,9 +1859,18 @@ static LLVMValueRef ir_render_div_exact(CodeGen *g, IrExecutable *executable, Ir } static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) { - TypeTableEntry *dest_type = get_underlying_type(instruction->base.value.type); LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - return LLVMBuildTrunc(g->builder, target_val, dest_type->type_ref, ""); + TypeTableEntry *dest_type = get_underlying_type(instruction->base.value.type); + TypeTableEntry *src_type = get_underlying_type(instruction->target->value.type); + if (dest_type == src_type) { + // no-op + return target_val; + } if (src_type->data.integral.bit_count == dest_type->data.integral.bit_count) { + return LLVMBuildBitCast(g->builder, target_val, dest_type->type_ref, ""); + } else { + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + return LLVMBuildTrunc(g->builder, target_val, dest_type->type_ref, ""); + } } static LLVMValueRef ir_render_alloca(CodeGen *g, IrExecutable *executable, IrInstructionAlloca *instruction) { @@ -1945,10 +1954,14 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInstructionSlice *instruction) { assert(instruction->tmp_ptr); - TypeTableEntry *array_type = get_underlying_type(instruction->ptr->value.type); + LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); + TypeTableEntry *array_ptr_type = instruction->ptr->value.type; + assert(array_ptr_type->id == TypeTableEntryIdPointer); + bool is_volatile = array_ptr_type->data.pointer.is_volatile; + TypeTableEntry *array_type = array_ptr_type->data.pointer.child_type; + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, is_volatile); LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr; - LLVMValueRef array_ptr = ir_llvm_value(g, instruction->ptr); bool want_debug_safety = instruction->safety_check_on && ir_want_debug_safety(g, &instruction->base); @@ -2582,7 +2595,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { return LLVMGetUndef(canon_type->type_ref); case ConstValSpecialStatic: break; - } switch (canon_type->id) { diff --git a/src/ir.cpp b/src/ir.cpp index b0a9c14108..72b1ae055b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1480,15 +1480,6 @@ static IrInstruction *ir_build_ref(IrBuilder *irb, Scope *scope, AstNode *source return &instruction->base; } -static IrInstruction *ir_build_ref_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value, - bool is_const, bool is_volatile) -{ - IrInstruction *new_instruction = ir_build_ref(irb, old_instruction->scope, old_instruction->source_node, - value, is_const, is_volatile); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_min_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionMinValue *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; @@ -5290,7 +5281,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; - IrInstruction *ptr_value = ir_gen_node(irb, array_node, scope); + IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LVAL_PTR); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5822,12 +5813,16 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit array to slice conversion if (expected_type->id == TypeTableEntryIdStruct && expected_type->data.structure.is_slice && - actual_type->id == TypeTableEntryIdArray && - types_match_const_cast_only( - expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, - actual_type->data.array.child_type)) + actual_type->id == TypeTableEntryIdArray) { - return ImplicitCastMatchResultYes; + TypeTableEntry *ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; + assert(ptr_type->id == TypeTableEntryIdPointer); + + if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && + types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + { + return ImplicitCastMatchResultYes; + } } // implicit number literal to typed number @@ -6180,7 +6175,7 @@ static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_typ return result_type; } -static ConstExprValue *ir_build_const_from(IrAnalyze *ira, IrInstruction *old_instruction) { +static IrInstruction *ir_get_const(IrAnalyze *ira, IrInstruction *old_instruction) { IrInstruction *new_instruction; if (old_instruction->id == IrInstructionIdVarPtr) { IrInstructionVarPtr *old_var_ptr_instruction = (IrInstructionVarPtr *)old_instruction; @@ -6201,10 +6196,14 @@ static ConstExprValue *ir_build_const_from(IrAnalyze *ira, IrInstruction *old_in old_instruction->scope, old_instruction->source_node); new_instruction = &const_instruction->base; } + new_instruction->value.special = ConstValSpecialStatic; + return new_instruction; +} + +static ConstExprValue *ir_build_const_from(IrAnalyze *ira, IrInstruction *old_instruction) { + IrInstruction *new_instruction = ir_get_const(ira, old_instruction); ir_link_new_instruction(new_instruction, old_instruction); - ConstExprValue *const_val = &new_instruction->value; - const_val->special = ConstValSpecialStatic; - return const_val; + return &new_instruction->value; } static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instruction) { @@ -6212,33 +6211,47 @@ static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instructio return ira->codegen->builtin_types.entry_void; } -static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction, +static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instruction, ConstExprValue *pointee, TypeTableEntry *pointee_type, - bool comptime_var_mem, bool ptr_is_const, bool ptr_is_volatile) + ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile) { if (pointee_type->id == TypeTableEntryIdMetaType) { TypeTableEntry *type_entry = pointee->data.x_type; if (type_entry->id == TypeTableEntryIdUnreachable) { ir_add_error(ira, instruction, buf_sprintf("pointer to unreachable not allowed")); - return ira->codegen->builtin_types.entry_invalid; + return ira->codegen->invalid_instruction; } - ConstExprValue *const_val = ir_build_const_from(ira, instruction); + IrInstruction *const_instr = ir_get_const(ira, instruction); + ConstExprValue *const_val = &const_instr->value; + const_val->type = pointee_type; type_ensure_zero_bits_known(ira->codegen, type_entry); const_val->data.x_type = get_pointer_to_type_volatile(ira->codegen, type_entry, ptr_is_const, ptr_is_volatile); - return pointee_type; + return const_instr; } else { TypeTableEntry *ptr_type = get_pointer_to_type_volatile(ira->codegen, pointee_type, ptr_is_const, ptr_is_volatile); - ConstExprValue *const_val = ir_build_const_from(ira, instruction); + IrInstruction *const_instr = ir_get_const(ira, instruction); + ConstExprValue *const_val = &const_instr->value; + const_val->type = ptr_type; const_val->data.x_ptr.special = ConstPtrSpecialRef; - const_val->data.x_ptr.comptime_var_mem = comptime_var_mem; + const_val->data.x_ptr.mut = ptr_mut; const_val->data.x_ptr.data.ref.pointee = pointee; - return ptr_type; + return const_instr; } } +static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction, + ConstExprValue *pointee, TypeTableEntry *pointee_type, + ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile) +{ + IrInstruction *const_instr = ir_get_const_ptr(ira, instruction, pointee, + pointee_type, ptr_mut, ptr_is_const, ptr_is_volatile); + ir_link_new_instruction(const_instr, instruction); + return const_instr->value.type; +} + static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *instruction, uint64_t value) { ConstExprValue *const_val = ir_build_const_from(ira, instruction); bignum_init_unsigned(&const_val->data.x_bignum, value); @@ -6513,6 +6526,37 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so return &const_instruction->base; } +static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value, + bool is_const, bool is_volatile) +{ + if (value->value.type->id == TypeTableEntryIdInvalid) + return ira->codegen->invalid_instruction; + + if (value->id == IrInstructionIdLoadPtr) { + IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *) value; + if (load_ptr_inst->ptr->value.type->data.pointer.is_const) { + return load_ptr_inst->ptr; + } + } + + if (instr_is_comptime(value)) { + ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + return ir_get_const_ptr(ira, source_instruction, val, value->value.type, + ConstPtrMutComptimeConst, is_const, is_volatile); + } + + TypeTableEntry *ptr_type = get_pointer_to_type_volatile(ira->codegen, value->value.type, is_const, is_volatile); + FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); + assert(fn_entry); + IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, + source_instruction->source_node, value, is_const, is_volatile); + new_instruction->value.type = ptr_type; + fn_entry->alloca_list.append(new_instruction); + return new_instruction; +} + static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *array, TypeTableEntry *wanted_type) { @@ -6536,19 +6580,14 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s source_instr->source_node, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &end->value, array_type->data.array.len); - bool is_const; - if (array->id == IrInstructionIdLoadPtr) { - IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *) array; - is_const = load_ptr_inst->ptr->value.type->data.pointer.is_const; - } else { - is_const = true; - } + IrInstruction *array_ptr = ir_get_ref(ira, source_instr, array, true, false); IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, - source_instr->source_node, array, start, end, is_const, false); + source_instr->source_node, array_ptr, start, end, false, false); TypeTableEntry *child_type = array_type->data.array.child_type; - result->value.type = get_slice_type(ira->codegen, child_type, is_const); + result->value.type = get_slice_type(ira->codegen, child_type, true); ir_add_alloca(ira, result, result->value.type); + return result; } @@ -6780,29 +6819,31 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from array to slice - if (is_slice(wanted_type) && - actual_type->id == TypeTableEntryIdArray && - types_match_const_cast_only( - wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type, - actual_type->data.array.child_type)) - { - return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); + if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { + TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + assert(ptr_type->id == TypeTableEntryIdPointer); + if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && + types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + { + return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); + } } // explicit cast from []T to []u8 or []u8 to []T if (is_slice(wanted_type) && is_slice(actual_type) && - (is_u8(wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type) || - is_u8(actual_type->data.structure.fields[0].type_entry->data.pointer.child_type)) && - (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const || - !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const)) + (is_u8(wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type) || + is_u8(actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type)) && + (wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || + !actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const)) { if (!ir_emit_global_runtime_side_effect(ira, source_instr)) return ira->codegen->invalid_instruction; return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true); } - // explicit cast from [N]u8 to []T + // explicit cast from [N]u8 to []const T if (is_slice(wanted_type) && + wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const && actual_type->id == TypeTableEntryIdArray && is_u8(actual_type->data.array.child_type)) { @@ -7010,9 +7051,9 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } else if (type_entry->id == TypeTableEntryIdPointer) { TypeTableEntry *child_type = type_entry->data.pointer.child_type; if (instr_is_comptime(ptr)) { - // Dereferencing a mutable pointer at compile time is not allowed - // unless that pointer is from a comptime variable - if (type_entry->data.pointer.is_const || ptr->value.data.x_ptr.comptime_var_mem) { + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst || + ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) + { ConstExprValue *pointee = const_ptr_pointee(&ptr->value); if (pointee->special != ConstValSpecialRuntime) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, @@ -7053,23 +7094,9 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value, bool is_const, bool is_volatile) { - if (value->value.type->id == TypeTableEntryIdInvalid) - return ira->codegen->builtin_types.entry_invalid; - - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); - if (!val) - return ira->codegen->builtin_types.entry_invalid; - return ir_analyze_const_ptr(ira, source_instruction, val, value->value.type, false, is_const, is_volatile); - } - - TypeTableEntry *ptr_type = get_pointer_to_type_volatile(ira->codegen, value->value.type, is_const, is_volatile); - FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); - assert(fn_entry); - IrInstruction *new_instruction = ir_build_ref_from(&ira->new_irb, source_instruction, - value, is_const, is_volatile); - fn_entry->alloca_list.append(new_instruction); - return ptr_type; + IrInstruction *result = ir_get_ref(ira, source_instruction, value, is_const, is_volatile); + ir_link_new_instruction(result, source_instruction); + return result->value.type; } static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) { @@ -8747,8 +8774,16 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc bool is_const = (var->value.type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const; bool is_volatile = (var->value.type->id == TypeTableEntryIdMetaType) ? is_volatile_ptr : false; if (mem_slot && mem_slot->special != ConstValSpecialRuntime) { - return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value.type, - comptime_var_mem, is_const, is_volatile); + ConstPtrMut ptr_mut; + if (comptime_var_mem) { + ptr_mut = ConstPtrMutComptimeVar; + } else if (var->gen_is_const) { + ptr_mut = ConstPtrMutComptimeConst; + } else { + assert(!comptime_var_mem); + ptr_mut = ConstPtrMutRuntimeVar; + } + return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value.type, ptr_mut, is_const, is_volatile); } else { ir_build_var_ptr_from(&ira->new_irb, instruction, var, is_const, is_volatile); type_ensure_zero_bits_known(ira->codegen, var->value.type); @@ -8837,7 +8872,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc is_const, is_volatile); } else { return ir_analyze_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val, - ira->codegen->builtin_types.entry_void, false, is_const, is_volatile); + ira->codegen->builtin_types.entry_void, ConstPtrMutComptimeConst, is_const, is_volatile); } } else { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, @@ -8872,8 +8907,8 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) { ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base); - out_val->data.x_ptr.comptime_var_mem = array_ptr->value.data.x_ptr.comptime_var_mem; if (array_type->id == TypeTableEntryIdPointer) { + out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; size_t new_index; size_t mem_size; size_t old_size; @@ -8926,6 +8961,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc index, slice_len)); return ira->codegen->builtin_types.entry_invalid; } + out_val->data.x_ptr.mut = ptr_field->data.x_ptr.mut; switch (ptr_field->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); @@ -8953,6 +8989,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } } else if (array_type->id == TypeTableEntryIdArray) { out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_ptr.mut = array_ptr->value.data.x_ptr.mut; out_val->data.x_ptr.data.base_array.array_val = array_ptr_val; out_val->data.x_ptr.data.base_array.elem_index = index; } else { @@ -9023,7 +9060,7 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field is_const, is_volatile); ConstExprValue *const_val = ir_build_const_from(ira, &field_ptr_instruction->base); const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; - const_val->data.x_ptr.comptime_var_mem = container_ptr->value.data.x_ptr.comptime_var_mem; + const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; const_val->data.x_ptr.data.base_struct.struct_val = struct_val; const_val->data.x_ptr.data.base_struct.field_index = field->src_index; return ptr_type; @@ -9088,7 +9125,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry, - false, ptr_is_const, ptr_is_volatile); + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } case TldIdTypeDef: { @@ -9105,7 +9142,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, source_instruction, const_val, ira->codegen->builtin_types.entry_type, - false, ptr_is_const, ptr_is_volatile); + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } } zig_unreachable(); @@ -9148,7 +9185,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, - usize, false, ptr_is_const, ptr_is_volatile); + usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { ir_add_error_node(ira, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), @@ -9172,7 +9209,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, - usize, false, ptr_is_const, ptr_is_volatile); + usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { ir_add_error_node(ira, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), @@ -9211,14 +9248,14 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_enum_tag(child_type, field->value), child_type, - false, ptr_is_const, ptr_is_volatile); + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_unsigned_negative(child_type->data.enumeration.tag_type, field->value, false), child_type->data.enumeration.tag_type, - false, ptr_is_const, ptr_is_volatile); + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } } } @@ -9243,7 +9280,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val, - child_type, false, ptr_is_const, ptr_is_volatile); + child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } ir_add_error(ira, &field_ptr_instruction->base, @@ -9257,14 +9294,14 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int, child_type->data.integral.bit_count, false), ira->codegen->builtin_types.entry_num_lit_int, - false, ptr_is_const, ptr_is_volatile); + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else if (buf_eql_str(field_name, "is_signed")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_bool(ira->codegen, child_type->data.integral.is_signed), ira->codegen->builtin_types.entry_bool, - false, ptr_is_const, ptr_is_volatile); + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", @@ -9352,8 +9389,8 @@ static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstru return ira->codegen->builtin_types.entry_invalid; if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - bool comptime_var_mem = ptr->value.data.x_ptr.comptime_var_mem; - if (comptime_var_mem) { + assert(ptr->value.data.x_ptr.mut != ConstPtrMutComptimeConst); + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { if (instr_is_comptime(casted_value)) { ConstExprValue *dest_val = const_ptr_pointee(&ptr->value); if (dest_val->special != ConstValSpecialRuntime) { @@ -11170,8 +11207,8 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc ir_add_error(ira, target, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; - } else if (canon_src_type->data.integral.bit_count <= canon_dest_type->data.integral.bit_count) { - ir_add_error(ira, target, buf_sprintf("type '%s' has same or fewer bits than destination type '%s'", + } else if (canon_src_type->data.integral.bit_count < canon_dest_type->data.integral.bit_count) { + ir_add_error(ira, target, buf_sprintf("type '%s' has fewer bits than destination type '%s'", buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; @@ -11457,10 +11494,15 @@ static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructi } static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice *instruction) { - IrInstruction *ptr = instruction->ptr->other; - if (ptr->value.type->id == TypeTableEntryIdInvalid) + IrInstruction *ptr_ptr = instruction->ptr->other; + if (ptr_ptr->value.type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; + TypeTableEntry *ptr_type = ptr_ptr->value.type; + assert(ptr_type->id == TypeTableEntryIdPointer); + TypeTableEntry *non_canon_array_type = ptr_type->data.pointer.child_type; + TypeTableEntry *canon_array_type = get_underlying_type(non_canon_array_type); + IrInstruction *start = instruction->start->other; if (start->value.type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; @@ -11482,44 +11524,42 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio end = nullptr; } - TypeTableEntry *array_type = get_underlying_type(ptr->value.type); - TypeTableEntry *return_type; - if (array_type->id == TypeTableEntryIdArray) { - return_type = get_slice_type(ira->codegen, array_type->data.array.child_type, instruction->is_const); - } else if (array_type->id == TypeTableEntryIdPointer) { - return_type = get_slice_type(ira->codegen, array_type->data.pointer.child_type, instruction->is_const); + if (canon_array_type->id == TypeTableEntryIdArray) { + return_type = get_slice_type(ira->codegen, canon_array_type->data.array.child_type, instruction->is_const); + } else if (canon_array_type->id == TypeTableEntryIdPointer) { + return_type = get_slice_type(ira->codegen, canon_array_type->data.pointer.child_type, instruction->is_const); if (!end) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); return ira->codegen->builtin_types.entry_invalid; } - } else if (is_slice(array_type)) { + } else if (is_slice(canon_array_type)) { return_type = get_slice_type(ira->codegen, - array_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + canon_array_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, instruction->is_const); } else { ir_add_error(ira, &instruction->base, - buf_sprintf("slice of non-array type '%s'", buf_ptr(&ptr->value.type->name))); + buf_sprintf("slice of non-array type '%s'", buf_ptr(&non_canon_array_type->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; } - if (ptr->value.special == ConstValSpecialStatic && - casted_start->value.special == ConstValSpecialStatic && - (!end || end->value.special == ConstValSpecialStatic)) + if (instr_is_comptime(ptr_ptr) && + value_is_comptime(&casted_start->value) && + (!end || value_is_comptime(&end->value))) { ConstExprValue *array_val; ConstExprValue *parent_ptr; size_t abs_offset; size_t rel_end; - if (array_type->id == TypeTableEntryIdArray) { - array_val = &ptr->value; + if (canon_array_type->id == TypeTableEntryIdArray) { + array_val = const_ptr_pointee(&ptr_ptr->value); abs_offset = 0; - rel_end = array_type->data.array.len; + rel_end = canon_array_type->data.array.len; parent_ptr = nullptr; - } else if (array_type->id == TypeTableEntryIdPointer) { - parent_ptr = &ptr->value; + } else if (canon_array_type->id == TypeTableEntryIdPointer) { + parent_ptr = const_ptr_pointee(&ptr_ptr->value); switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); @@ -11539,9 +11579,10 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio array_val = nullptr; break; } - } else if (is_slice(array_type)) { - parent_ptr = &ptr->value.data.x_struct.fields[slice_ptr_index]; - ConstExprValue *len_val = &ptr->value.data.x_struct.fields[slice_len_index]; + } else if (is_slice(canon_array_type)) { + ConstExprValue *slice_ptr = const_ptr_pointee(&ptr_ptr->value); + parent_ptr = &slice_ptr->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *len_val = &slice_ptr->data.x_struct.fields[slice_len_index]; switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: @@ -11596,6 +11637,9 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio if (array_val) { size_t index = abs_offset + start_scalar; init_const_ptr_array(ira->codegen, ptr_val, array_val, index, instruction->is_const); + if (canon_array_type->id == TypeTableEntryIdArray) { + ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut; + } } else { switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: @@ -11620,7 +11664,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio } } - IrInstruction *new_instruction = ir_build_slice_from(&ira->new_irb, &instruction->base, ptr, + IrInstruction *new_instruction = ir_build_slice_from(&ira->new_irb, &instruction->base, ptr_ptr, casted_start, end, instruction->is_const, instruction->safety_check_on); ir_add_alloca(ira, new_instruction, return_type); diff --git a/std/debug.zig b/std/debug.zig index f3278a0e45..2d30f2b540 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -149,7 +149,7 @@ const Constant = struct { return error.InvalidDebugInfo; if (self.signed) return error.InvalidDebugInfo; - return mem.sliceAsInt(self.payload, false, u64); + return mem.readInt(self.payload, u64, false); } }; diff --git a/std/elf.zig b/std/elf.zig index 1ad41bf245..2a3777b2a4 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -93,8 +93,8 @@ pub const Elf = struct { elf.auto_close_stream = false; var magic: [4]u8 = undefined; - %return elf.in_stream.readNoEof(magic); - if (!mem.eql(magic, "\x7fELF")) return error.InvalidFormat; + %return elf.in_stream.readNoEof(magic[0...]); + if (!mem.eql(u8, magic, "\x7fELF")) return error.InvalidFormat; elf.is_64 = switch (%return elf.in_stream.readByte()) { 1 => false, @@ -236,7 +236,7 @@ pub const Elf = struct { elf.in_stream.close(); } - pub fn findSection(elf: &Elf, name: []u8) -> %?&SectionHeader { + pub fn findSection(elf: &Elf, name: []const u8) -> %?&SectionHeader { for (elf.section_headers) |*section| { if (section.sh_type == SHT_NULL) continue; diff --git a/std/endian.zig b/std/endian.zig index 0dcc587e4a..1d67e414f8 100644 --- a/std/endian.zig +++ b/std/endian.zig @@ -1,21 +1,19 @@ -pub inline fn swapIfLe(comptime T: type, x: T) -> T { +const mem = @import("mem.zig"); + +pub fn swapIfLe(comptime T: type, x: T) -> T { swapIf(false, T, x) } -pub inline fn swapIfBe(comptime T: type, x: T) -> T { +pub fn swapIfBe(comptime T: type, x: T) -> T { swapIf(true, T, x) } -pub inline fn swapIf(is_be: bool, comptime T: type, x: T) -> T { +pub fn swapIf(is_be: bool, comptime T: type, x: T) -> T { if (@compileVar("is_big_endian") == is_be) swap(T, x) else x } pub fn swap(comptime T: type, x: T) -> T { - const x_slice = ([]u8)((&const x)[0...1]); - var result: T = undefined; - const result_slice = ([]u8)((&result)[0...1]); - for (result_slice) |*b, i| { - *b = x_slice[@sizeOf(T) - i - 1]; - } - return result; + var buf: [@sizeOf(T)]u8 = undefined; + mem.writeInt(buf[0...], x, false); + return mem.readInt(buf, T, true); } diff --git a/std/io.zig b/std/io.zig index 05bbba1bea..13b7c36abc 100644 --- a/std/io.zig +++ b/std/io.zig @@ -6,7 +6,6 @@ const system = switch(@compileVar("os")) { const errno = @import("errno.zig"); const math = @import("math.zig"); -const endian = @import("endian.zig"); const debug = @import("debug.zig"); const assert = debug.assert; const os = @import("os.zig"); @@ -365,7 +364,7 @@ pub const InStream = struct { pub fn readByte(is: &InStream) -> %u8 { var result: [1]u8 = undefined; - %return is.readNoEof(result); + %return is.readNoEof(result[0...]); return result[0]; } @@ -378,10 +377,9 @@ pub const InStream = struct { } pub fn readInt(is: &InStream, is_be: bool, comptime T: type) -> %T { - var result: T = undefined; - const result_slice = ([]u8)((&result)[0...1]); - %return is.readNoEof(result_slice); - return endian.swapIf(!is_be, T, result); + var bytes: [@sizeOf(T)]u8 = undefined; + %return is.readNoEof(bytes[0...]); + return mem.readInt(bytes, T, is_be); } pub fn readVarInt(is: &InStream, is_be: bool, comptime T: type, size: usize) -> %T { @@ -390,7 +388,7 @@ pub const InStream = struct { var input_buf: [8]u8 = undefined; const input_slice = input_buf[0...size]; %return is.readNoEof(input_slice); - return mem.sliceAsInt(input_slice, is_be, T); + return mem.readInt(input_slice, T, is_be); } pub fn seekForward(is: &InStream, amount: usize) -> %void { @@ -589,18 +587,19 @@ fn testParseUnsignedComptime() { fn testBufPrintInt() { @setFnTest(this); - var buf: [max_int_digits]u8 = undefined; - assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); - assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); - assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); - assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); + var buffer: [max_int_digits]u8 = undefined; + const buf = buffer[0...]; + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); - assert(mem.eql(bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); - assert(mem.eql(bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); - assert(mem.eql(bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); - assert(mem.eql(bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); - assert(mem.eql(bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); - assert(mem.eql(bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); } diff --git a/std/mem.zig b/std/mem.zig index 45734ce869..dba0422081 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -68,25 +68,8 @@ pub fn cmp(comptime T: type, a: []const T, b: []const T) -> Cmp { return if (a.len > b.len) Cmp.Greater else if (a.len < b.len) Cmp.Less else Cmp.Equal; } -pub fn sliceAsInt(buf: []u8, is_be: bool, comptime T: type) -> T { - var result: T = undefined; - const result_slice = ([]u8)((&result)[0...1]); - set(u8, result_slice, 0); - const padding = @sizeOf(T) - buf.len; - - if (is_be == @compileVar("is_big_endian")) { - copy(u8, result_slice, buf); - } else { - for (buf) |b, i| { - const index = result_slice.len - i - 1 - padding; - result_slice[index] = b; - } - } - return result; -} - /// Compares two slices and returns whether they are equal. -pub fn eql(a: var, b: var) -> bool { +pub fn eql(comptime T: type, a: []const T, b: []const T) -> bool { if (a.len != b.len) return false; for (a) |item, index| { if (b[index] != item) return false; @@ -94,24 +77,96 @@ pub fn eql(a: var, b: var) -> bool { return true; } +/// Reads an integer from memory with size equal to bytes.len. +/// T specifies the return type, which must be large enough to store +/// the result. +pub fn readInt(bytes: []const u8, comptime T: type, big_endian: bool) -> T { + var result: T = 0; + if (big_endian) { + for (bytes) |b| { + result = (result << 8) | b; + } + } else { + for (bytes) |b, index| { + result = result | (T(b) << T(index * 8)); + } + } + return result; +} + +/// Writes an integer to memory with size equal to bytes.len. Pads with zeroes +/// to fill the entire buffer provided. +/// value must be an integer. +pub fn writeInt(buf: []u8, value: var, big_endian: bool) { + const uint = @intType(false, @typeOf(value).bit_count); + var bits = @truncate(uint, value); + if (big_endian) { + var index: usize = buf.len; + while (index != 0) { + index -= 1; + + buf[index] = @truncate(u8, bits); + bits >>= 8; + } + } else { + for (buf) |*b| { + *b = @truncate(u8, bits); + bits >>= 8; + } + } + assert(bits == 0); +} + fn testStringEquality() { @setFnTest(this); - assert(eql("abcd", "abcd")); - assert(!eql("abcdef", "abZdef")); - assert(!eql("abcdefg", "abcdef")); + assert(eql(u8, "abcd", "abcd")); + assert(!eql(u8, "abcdef", "abZdef")); + assert(!eql(u8, "abcdefg", "abcdef")); } -fn testSliceAsInt() { +fn testReadInt() { @setFnTest(this); + + testReadIntImpl(); + comptime testReadIntImpl(); +} +fn testReadIntImpl() { + { + const bytes = []u8{ 0x12, 0x34, 0x56, 0x78 }; + assert(readInt(bytes, u32, true) == 0x12345678); + assert(readInt(bytes, u32, false) == 0x78563412); + } { const buf = []u8{0x00, 0x00, 0x12, 0x34}; - const answer = sliceAsInt(buf[0...], true, u64); + const answer = readInt(buf, u64, true); assert(answer == 0x00001234); } { const buf = []u8{0x12, 0x34, 0x00, 0x00}; - const answer = sliceAsInt(buf[0...], false, u64); + const answer = readInt(buf, u64, false); assert(answer == 0x00003412); } } + +fn testWriteInt() { + @setFnTest(this); + + testWriteIntImpl(); + comptime testWriteIntImpl(); +} +fn testWriteIntImpl() { + var bytes: [4]u8 = undefined; + + writeInt(bytes[0...], u32(0x12345678), true); + assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 })); + + writeInt(bytes[0...], u32(0x78563412), false); + assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 })); + + writeInt(bytes[0...], u16(0x1234), true); + assert(eql(u8, bytes, []u8{ 0x00, 0x00, 0x12, 0x34 })); + + writeInt(bytes[0...], u16(0x1234), false); + assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 })); +} diff --git a/std/net.zig b/std/net.zig index 067355aa9d..dfbe389424 100644 --- a/std/net.zig +++ b/std/net.zig @@ -134,7 +134,7 @@ pub fn connectAddr(addr: &Address, port: u16) -> %Connection { pub fn connect(hostname: []const u8, port: u16) -> %Connection { var addrs_buf: [1]Address = undefined; - const addrs_slice = %return lookup(hostname, addrs_buf); + const addrs_slice = %return lookup(hostname, addrs_buf[0...]); const main_addr = &addrs_slice[0]; return connectAddr(main_addr, port); diff --git a/std/rand.zig b/std/rand.zig index de2f62a621..a36ca4a9ca 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -1,5 +1,6 @@ const assert = @import("debug.zig").assert; const rand_test = @import("rand_test.zig"); +const mem = @import("mem.zig"); pub const MT19937_32 = MersenneTwister( u32, 624, 397, 31, @@ -28,14 +29,16 @@ pub const Rand = struct { r.rng.init(seed); } - /// Get an integer with random bits. + /// Get an integer or boolean with random bits. pub fn scalar(r: &Rand, comptime T: type) -> T { if (T == usize) { return r.rng.get(); + } else if (T == bool) { + return (r.rng.get() & 0b1) == 0; } else { var result: [@sizeOf(T)]u8 = undefined; - r.fillBytes(result); - return ([]T)(result)[0]; + r.fillBytes(result[0...]); + return mem.readInt(result, T, false); } } @@ -43,12 +46,12 @@ pub const Rand = struct { pub fn fillBytes(r: &Rand, buf: []u8) { var bytes_left = buf.len; while (bytes_left >= @sizeOf(usize)) { - ([]usize)(buf[buf.len - bytes_left...])[0] = r.rng.get(); + mem.writeInt(buf[buf.len - bytes_left...], r.rng.get(), false); bytes_left -= @sizeOf(usize); } if (bytes_left > 0) { - var rand_val_array : [@sizeOf(usize)]u8 = undefined; - ([]usize)(rand_val_array)[0] = r.rng.get(); + var rand_val_array: [@sizeOf(usize)]u8 = undefined; + mem.writeInt(rand_val_array[0...], r.rng.get(), false); while (bytes_left > 0) { buf[buf.len - bytes_left] = rand_val_array[@sizeOf(usize) - bytes_left]; bytes_left -= 1; @@ -63,11 +66,11 @@ pub const Rand = struct { const range = end - start; const leftover = @maxValue(T) % range; const upper_bound = @maxValue(T) - leftover; - var rand_val_array : [@sizeOf(T)]u8 = undefined; + var rand_val_array: [@sizeOf(T)]u8 = undefined; while (true) { - r.fillBytes(rand_val_array); - const rand_val = ([]T)(rand_val_array)[0]; + r.fillBytes(rand_val_array[0...]); + const rand_val = mem.readInt(rand_val_array, T, false); if (rand_val < upper_bound) { return start + (rand_val % range); } diff --git a/std/sort.zig b/std/sort.zig index 8c41d87034..23fd1eba34 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -61,13 +61,13 @@ fn reverse(was: Cmp) -> Cmp { fn testSort() { @setFnTest(this); - const u8cases = [][][]u8 { - [][]u8{"", ""}, - [][]u8{"a", "a"}, - [][]u8{"az", "az"}, - [][]u8{"za", "az"}, - [][]u8{"asdf", "adfs"}, - [][]u8{"one", "eno"}, + const u8cases = [][]const []const u8 { + [][]const u8{"", ""}, + [][]const u8{"a", "a"}, + [][]const u8{"az", "az"}, + [][]const u8{"za", "az"}, + [][]const u8{"asdf", "adfs"}, + [][]const u8{"one", "eno"}, }; for (u8cases) |case| { @@ -75,16 +75,16 @@ fn testSort() { const slice = buf[0...case[0].len]; mem.copy(u8, slice, case[0]); sort(u8, slice, u8asc); - assert(mem.eql(slice, case[1])); + assert(mem.eql(u8, slice, case[1])); } - const i32cases = [][][]i32 { - [][]i32{[]i32{}, []i32{}}, - [][]i32{[]i32{1}, []i32{1}}, - [][]i32{[]i32{0, 1}, []i32{0, 1}}, - [][]i32{[]i32{1, 0}, []i32{0, 1}}, - [][]i32{[]i32{1, -1, 0}, []i32{-1, 0, 1}}, - [][]i32{[]i32{2, 1, 3}, []i32{1, 2, 3}}, + const i32cases = [][]const []const i32 { + [][]const i32{[]i32{}, []i32{}}, + [][]const i32{[]i32{1}, []i32{1}}, + [][]const i32{[]i32{0, 1}, []i32{0, 1}}, + [][]const i32{[]i32{1, 0}, []i32{0, 1}}, + [][]const i32{[]i32{1, -1, 0}, []i32{-1, 0, 1}}, + [][]const i32{[]i32{2, 1, 3}, []i32{1, 2, 3}}, }; for (i32cases) |case| { @@ -92,20 +92,20 @@ fn testSort() { const slice = buf[0...case[0].len]; mem.copy(i32, slice, case[0]); sort(i32, slice, i32asc); - assert(mem.eql(slice, case[1])); + assert(mem.eql(i32, slice, case[1])); } } fn testSortDesc() { @setFnTest(this); - const rev_cases = [][][]i32 { - [][]i32{[]i32{}, []i32{}}, - [][]i32{[]i32{1}, []i32{1}}, - [][]i32{[]i32{0, 1}, []i32{1, 0}}, - [][]i32{[]i32{1, 0}, []i32{1, 0}}, - [][]i32{[]i32{1, -1, 0}, []i32{1, 0, -1}}, - [][]i32{[]i32{2, 1, 3}, []i32{3, 2, 1}}, + const rev_cases = [][]const []const i32 { + [][]const i32{[]i32{}, []i32{}}, + [][]const i32{[]i32{1}, []i32{1}}, + [][]const i32{[]i32{0, 1}, []i32{1, 0}}, + [][]const i32{[]i32{1, 0}, []i32{1, 0}}, + [][]const i32{[]i32{1, -1, 0}, []i32{1, 0, -1}}, + [][]const i32{[]i32{2, 1, 3}, []i32{3, 2, 1}}, }; for (rev_cases) |case| { @@ -113,6 +113,6 @@ fn testSortDesc() { const slice = buf[0...case[0].len]; mem.copy(i32, slice, case[0]); sort(i32, slice, i32desc); - assert(mem.eql(slice, case[1])); + assert(mem.eql(i32, slice, case[1])); } } diff --git a/test/cases/array.zig b/test/cases/array.zig index 1dc1242d37..09f3c425f3 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -23,7 +23,7 @@ fn arrays() { assert(accumulator == 15); assert(getArrayLen(array) == 5); } -fn getArrayLen(a: []u32) -> usize { +fn getArrayLen(a: []const u32) -> usize { a.len } @@ -61,12 +61,12 @@ const some_array = []u8 {0, 1, 2, 3}; fn nestedArrays() { @setFnTest(this); - const array_of_strings = [][]u8 {"hello", "this", "is", "my", "thing"}; + const array_of_strings = [][]const u8 {"hello", "this", "is", "my", "thing"}; for (array_of_strings) |s, i| { - if (i == 0) assert(mem.eql(s, "hello")); - if (i == 1) assert(mem.eql(s, "this")); - if (i == 2) assert(mem.eql(s, "is")); - if (i == 3) assert(mem.eql(s, "my")); - if (i == 4) assert(mem.eql(s, "thing")); + if (i == 0) assert(mem.eql(u8, s, "hello")); + if (i == 1) assert(mem.eql(u8, s, "this")); + if (i == 2) assert(mem.eql(u8, s, "is")); + if (i == 3) assert(mem.eql(u8, s, "my")); + if (i == 4) assert(mem.eql(u8, s, "thing")); } } diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 5abb297674..da9ff0f40c 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -21,9 +21,9 @@ fn enumWithMembers() { const b = ET.UINT { 42 }; var buf: [20]u8 = undefined; - assert(%%a.print(buf) == 3); - assert(mem.eql(buf[0...3], "-42")); + assert(%%a.print(buf[0...]) == 3); + assert(mem.eql(u8, buf[0...3], "-42")); - assert(%%b.print(buf) == 2); - assert(mem.eql(buf[0...2], "42")); + assert(%%b.print(buf[0...]) == 2); + assert(mem.eql(u8, buf[0...2], "42")); } diff --git a/test/cases/error.zig b/test/cases/error.zig index 20fba13f90..eeed337141 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -28,8 +28,8 @@ fn gimmeItBroke() -> []const u8 { fn errorName() { @setFnTest(this); - assert(mem.eql(@errorName(error.AnError), "AnError")); - assert(mem.eql(@errorName(error.ALongerErrorName), "ALongerErrorName")); + assert(mem.eql(u8, @errorName(error.AnError), "AnError")); + assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); } error AnError; error ALongerErrorName; diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 00906488c0..4e7c94f2d3 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -283,3 +283,21 @@ fn callMethodOnBoundFnReferringToVarInstance() { assert(bound_fn() == 1237); } + + + +fn ptrToLocalArrayArgumentAtComptime() { + @setFnTest(this); + + comptime { + var bytes: [10]u8 = undefined; + modifySomeBytes(bytes[0...]); + assert(bytes[0] == 'a'); + assert(bytes[9] == 'b'); + } +} + +fn modifySomeBytes(bytes: []u8) { + bytes[0] = 'a'; + bytes[9] = 'b'; +} diff --git a/test/cases/for.zig b/test/cases/for.zig index dd1987d6b9..b7e9fba295 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -22,12 +22,42 @@ fn forLoopWithPointerElemVar() { const source = "abcdefg"; var target: [source.len]u8 = undefined; - @memcpy(&target[0], &source[0], source.len); - mangleString(target); - assert(mem.eql(target, "bcdefgh")); + mem.copy(u8, target[0...], source); + mangleString(target[0...]); + assert(mem.eql(u8, target, "bcdefgh")); } fn mangleString(s: []u8) { for (s) |*c| { *c += 1; } } + +fn basicForLoop() { + @setFnTest(this); + + const expected_result = []u8{9, 8, 7, 6, 0, 1, 2, 3, 9, 8, 7, 6, 0, 1, 2, 3 }; + + var buffer: [expected_result.len]u8 = undefined; + var buf_index: usize = 0; + + const array = []u8 {9, 8, 7, 6}; + for (array) |item| { + buffer[buf_index] = item; + buf_index += 1; + } + for (array) |item, index| { + buffer[buf_index] = u8(index); + buf_index += 1; + } + const unknown_size: []const u8 = array; + for (unknown_size) |item| { + buffer[buf_index] = item; + buf_index += 1; + } + for (unknown_size) |item, index| { + buffer[buf_index] = u8(index); + buf_index += 1; + } + + assert(mem.eql(u8, buffer[0...buf_index], expected_result)); +} diff --git a/test/cases/generics.zig b/test/cases/generics.zig index 822c7e0833..dcdb5086e1 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -136,7 +136,7 @@ fn genericFnWithImplicitCast() { assert(getFirstByte(u8, []u8 {13}) == 13); assert(getFirstByte(u16, []u16 {0, 13}) == 0); } -fn getByte(ptr: ?&u8) -> u8 {*??ptr} -fn getFirstByte(comptime T: type, mem: []T) -> u8 { - getByte((&u8)(&mem[0])) +fn getByte(ptr: ?&const u8) -> u8 {*??ptr} +fn getFirstByte(comptime T: type, mem: []const T) -> u8 { + getByte((&const u8)(&mem[0])) } diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 0984abcae9..1bcecb5e64 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -144,7 +144,7 @@ fn first4KeysOfHomeRow() -> []const u8 { fn ReturnStringFromFunction() { @setFnTest(this); - assert(mem.eql(first4KeysOfHomeRow(), "aoeu")); + assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); } const g1 : i32 = 1233 + 1; @@ -210,31 +210,31 @@ fn emptyFn() {} fn hexEscape() { @setFnTest(this); - assert(mem.eql("\x68\x65\x6c\x6c\x6f", "hello")); + assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } fn stringConcatenation() { @setFnTest(this); - assert(mem.eql("OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); + assert(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } fn arrayMultOperator() { @setFnTest(this); - assert(mem.eql("ab" ** 5, "ababababab")); + assert(mem.eql(u8, "ab" ** 5, "ababababab")); } fn stringEscapes() { @setFnTest(this); - assert(mem.eql("\"", "\x22")); - assert(mem.eql("\'", "\x27")); - assert(mem.eql("\n", "\x0a")); - assert(mem.eql("\r", "\x0d")); - assert(mem.eql("\t", "\x09")); - assert(mem.eql("\\", "\x5c")); - assert(mem.eql("\u1234\u0069", "\xe1\x88\xb4\x69")); + assert(mem.eql(u8, "\"", "\x22")); + assert(mem.eql(u8, "\'", "\x27")); + assert(mem.eql(u8, "\n", "\x0a")); + assert(mem.eql(u8, "\r", "\x0d")); + assert(mem.eql(u8, "\t", "\x09")); + assert(mem.eql(u8, "\\", "\x5c")); + assert(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); } fn multilineString() { @@ -246,7 +246,7 @@ fn multilineString() { \\three ; const s2 = "one\ntwo)\nthree"; - assert(mem.eql(s1, s2)); + assert(mem.eql(u8, s1, s2)); } fn multilineCString() { @@ -302,7 +302,7 @@ fn castUndefined() { @setFnTest(this); const array: [100]u8 = undefined; - const slice = ([]u8)(array); + const slice = ([]const u8)(array); testCastUndefined(slice); } fn testCastUndefined(x: []const u8) {} @@ -344,14 +344,14 @@ fn pointerDereferencing() { fn callResultOfIfElseExpression() { @setFnTest(this); - assert(mem.eql(f2(true), "a")); - assert(mem.eql(f2(false), "b")); + assert(mem.eql(u8, f2(true), "a")); + assert(mem.eql(u8, f2(false), "b")); } -fn f2(x: bool) -> []u8 { +fn f2(x: bool) -> []const u8 { return (if (x) fA else fB)(); } -fn fA() -> []u8 { "a" } -fn fB() -> []u8 { "b" } +fn fA() -> []const u8 { "a" } +fn fB() -> []const u8 { "b" } fn constExpressionEvalHandlingOfVariables() { @@ -434,7 +434,7 @@ fn intToPtrCast() { fn pointerComparison() { @setFnTest(this); - const a = ([]u8)("a"); + const a = ([]const u8)("a"); const b = &a; assert(ptrEql(b, b)); } @@ -463,7 +463,7 @@ fn castSliceToU8Slice() { assert(@sizeOf(i32) == 4); var big_thing_array = []i32{1, 2, 3, 4}; - const big_thing_slice: []i32 = big_thing_array; + const big_thing_slice: []i32 = big_thing_array[0...]; const bytes = ([]u8)(big_thing_slice); assert(bytes.len == 4 * 4); bytes[4] = 0; @@ -562,8 +562,8 @@ fn typeName() { @setFnTest(this); comptime { - assert(mem.eql(@typeName(i64), "i64")); - assert(mem.eql(@typeName(&usize), "&usize")); + assert(mem.eql(u8, @typeName(i64), "i64")); + assert(mem.eql(u8, @typeName(&usize), "&usize")); } } diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 09312990a2..fd49d00f76 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -205,7 +205,7 @@ fn passSliceOfEmptyStructToFn() { assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{ EmptyStruct2{} }) == 1); } -fn testPassSliceOfEmptyStructToFn(slice: []EmptyStruct2) -> usize { +fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) -> usize { slice.len } diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig index 281b4542e1..4c4bf431ea 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/cases/struct_contains_slice_of_itself.zig @@ -19,7 +19,7 @@ fn structContainsSliceOfItself() { }, Node { .payload = 3, - .children = []Node{ + .children = ([]Node{ Node { .payload = 31, .children = []Node{}, @@ -28,7 +28,7 @@ fn structContainsSliceOfItself() { .payload = 32, .children = []Node{}, }, - }, + })[0...], }, }; const root = Node { diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 1c282a8978..b142579def 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -465,27 +465,6 @@ fn print_ok(val: @typeOf(x)) -> @typeOf(foo) { const foo : i32 = 0; )SOURCE", "OK\n"); - add_simple_case("for loops", R"SOURCE( -const io = @import("std").io; - -pub fn main(args: [][]u8) -> %void { - const array = []u8 {9, 8, 7, 6}; - for (array) |item| { - %%io.stdout.printf("{}\n", item); - } - for (array) |item, index| { - %%io.stdout.printf("{}\n", index); - } - const unknown_size: []u8 = array; - for (unknown_size) |item| { - %%io.stdout.printf("{}\n", item); - } - for (unknown_size) |item, index| { - %%io.stdout.printf("{}\n", index); - } -} - )SOURCE", "9\n8\n7\n6\n0\n1\n2\n3\n9\n8\n7\n6\n0\n1\n2\n3\n"); - add_simple_case_libc("expose function pointer to C land", R"SOURCE( const c = @cImport(@cInclude("stdlib.h")); @@ -1350,13 +1329,6 @@ fn f() -> i8 { } )SOURCE", 1, ".tmp_source.zig:4:19: error: expected signed integer type, found 'u32'"); - add_compile_fail_case("truncate same bit count", R"SOURCE( -fn f() -> i8 { - const x: i8 = 10; - @truncate(i8, x) -} - )SOURCE", 1, ".tmp_source.zig:4:19: error: type 'i8' has same or fewer bits than destination type 'i8'"); - add_compile_fail_case("%return in function with non error return type", R"SOURCE( fn f() { %return something(); @@ -1396,9 +1368,9 @@ fn f() -> i32 { add_compile_fail_case("convert fixed size array to slice with invalid size", R"SOURCE( fn f() { var array: [5]u8 = undefined; - var foo = ([]u32)(array)[0]; + var foo = ([]const u32)(array)[0]; } - )SOURCE", 1, ".tmp_source.zig:4:22: error: unable to convert [5]u8 to []u32: size mismatch"); + )SOURCE", 1, ".tmp_source.zig:4:28: error: unable to convert [5]u8 to []const u32: size mismatch"); add_compile_fail_case("non-pure function returns type", R"SOURCE( var a: u32 = 0; @@ -1664,7 +1636,7 @@ pub fn main(args: [][]u8) -> %void { const a = []i32{1, 2, 3, 4}; baz(bar(a)); } -fn bar(a: []i32) -> i32 { +fn bar(a: []const i32) -> i32 { a[4] } fn baz(a: i32) { } @@ -1799,8 +1771,8 @@ pub fn main(args: [][]u8) -> %void { const x = widenSlice([]u8{1, 2, 3, 4, 5}); if (x.len == 0) return error.Whatever; } -fn widenSlice(slice: []u8) -> []i32 { - ([]i32)(slice) +fn widenSlice(slice: []const u8) -> []const i32 { + ([]const i32)(slice) } )SOURCE");